diff options
Diffstat (limited to 'drivers/tty')
62 files changed, 2526 insertions, 2036 deletions
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig index 5646dc6242..a45d423ad1 100644 --- a/drivers/tty/Kconfig +++ b/drivers/tty/Kconfig @@ -75,14 +75,9 @@ config VT_CONSOLE_SLEEP def_bool y depends on VT_CONSOLE && PM_SLEEP -config HW_CONSOLE - bool - depends on VT - default y - config VT_HW_CONSOLE_BINDING bool "Support for binding and unbinding console drivers" - depends on HW_CONSOLE + depends on VT help The virtual terminal is the device that interacts with the physical terminal through console drivers. On these systems, at least one diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 7716ce0d35..e27360652d 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1566,7 +1566,7 @@ fail_tty_driver_kref_put: return error; } -static int __exit amiga_serial_remove(struct platform_device *pdev) +static void __exit amiga_serial_remove(struct platform_device *pdev) { struct serial_state *state = platform_get_drvdata(pdev); @@ -1576,12 +1576,10 @@ static int __exit amiga_serial_remove(struct platform_device *pdev) free_irq(IRQ_AMIGA_TBE, state); free_irq(IRQ_AMIGA_RBF, state); - - return 0; } static struct platform_driver amiga_serial_driver = { - .remove = __exit_p(amiga_serial_remove), + .remove_new = __exit_p(amiga_serial_remove), .driver = { .name = "amiga-serial", }, diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c index d27979eabf..34421ec06a 100644 --- a/drivers/tty/goldfish.c +++ b/drivers/tty/goldfish.c @@ -408,7 +408,7 @@ err_unmap: return ret; } -static int goldfish_tty_remove(struct platform_device *pdev) +static void goldfish_tty_remove(struct platform_device *pdev) { struct goldfish_tty *qtty = platform_get_drvdata(pdev); @@ -424,7 +424,6 @@ static int goldfish_tty_remove(struct platform_device *pdev) if (goldfish_tty_current_line_count == 0) goldfish_tty_delete_driver(); mutex_unlock(&goldfish_tty_lock); - return 0; } #ifdef CONFIG_GOLDFISH_TTY_EARLY_CONSOLE @@ -462,7 +461,7 @@ MODULE_DEVICE_TABLE(of, goldfish_tty_of_match); static struct platform_driver goldfish_tty_platform_driver = { .probe = goldfish_tty_probe, - .remove = goldfish_tty_remove, + .remove_new = goldfish_tty_remove, .driver = { .name = "goldfish_tty", .of_match_table = goldfish_tty_of_match, diff --git a/drivers/tty/hvc/hvc_iucv.c b/drivers/tty/hvc/hvc_iucv.c index fdecc0d637..b1149bc62c 100644 --- a/drivers/tty/hvc/hvc_iucv.c +++ b/drivers/tty/hvc/hvc_iucv.c @@ -1035,6 +1035,10 @@ static const struct attribute_group *hvc_iucv_dev_attr_groups[] = { NULL, }; +static void hvc_iucv_free(struct device *data) +{ + kfree(data); +} /** * hvc_iucv_alloc() - Allocates a new struct hvc_iucv_private instance @@ -1097,7 +1101,7 @@ static int __init hvc_iucv_alloc(int id, unsigned int is_console) priv->dev->bus = &iucv_bus; priv->dev->parent = iucv_root; priv->dev->groups = hvc_iucv_dev_attr_groups; - priv->dev->release = (void (*)(struct device *)) kfree; + priv->dev->release = hvc_iucv_free; rc = device_register(priv->dev); if (rc) { put_device(priv->dev); diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index f252d0b5a4..5e9ca4376d 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1619,15 +1619,25 @@ static void __receive_buf(struct tty_struct *tty, const u8 *cp, const u8 *fp, else if (ldata->raw || (L_EXTPROC(tty) && !preops)) n_tty_receive_buf_raw(tty, cp, fp, count); else if (tty->closing && !L_EXTPROC(tty)) { - if (la_count > 0) + if (la_count > 0) { n_tty_receive_buf_closing(tty, cp, fp, la_count, true); - if (count > la_count) - n_tty_receive_buf_closing(tty, cp, fp, count - la_count, false); + cp += la_count; + if (fp) + fp += la_count; + count -= la_count; + } + if (count > 0) + n_tty_receive_buf_closing(tty, cp, fp, count, false); } else { - if (la_count > 0) + if (la_count > 0) { n_tty_receive_buf_standard(tty, cp, fp, la_count, true); - if (count > la_count) - n_tty_receive_buf_standard(tty, cp, fp, count - la_count, false); + cp += la_count; + if (fp) + fp += la_count; + count -= la_count; + } + if (count > 0) + n_tty_receive_buf_standard(tty, cp, fp, count, false); flush_echoes(tty); if (tty->ops->flush_chars) diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index 822a5cd055..613cb356b9 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -431,7 +431,7 @@ static void serdev_drv_remove(struct device *dev) dev_pm_domain_detach(dev, true); } -static struct bus_type serdev_bus_type = { +static const struct bus_type serdev_bus_type = { .name = "serial", .match = serdev_device_match, .probe = serdev_drv_probe, diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index e94e090cf0..3d7ae7fa50 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -27,19 +27,17 @@ static size_t ttyport_receive_buf(struct tty_port *port, const u8 *cp, { struct serdev_controller *ctrl = port->client_data; struct serport *serport = serdev_controller_get_drvdata(ctrl); - int ret; + size_t ret; if (!test_bit(SERPORT_ACTIVE, &serport->flags)) return 0; ret = serdev_controller_receive_buf(ctrl, cp, count); - dev_WARN_ONCE(&ctrl->dev, ret < 0 || ret > count, - "receive_buf returns %d (count = %zu)\n", + dev_WARN_ONCE(&ctrl->dev, ret > count, + "receive_buf returns %zu (count = %zu)\n", ret, count); - if (ret < 0) - return 0; - else if (ret > count) + if (ret > count) return count; return ret; diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index 8c2aaf7af7..53d8eee9b1 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -419,8 +419,8 @@ static int aspeed_vuart_probe(struct platform_device *pdev) struct aspeed_vuart *vuart; struct device_node *np; struct resource *res; - u32 clk, prop, sirq[2]; int rc, sirq_polarity; + u32 prop, sirq[2]; struct clk *vclk; np = pdev->dev.of_node; @@ -447,53 +447,35 @@ static int aspeed_vuart_probe(struct platform_device *pdev) port.port.status = UPSTAT_SYNC_FIFO; port.port.dev = &pdev->dev; port.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); + port.port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_FIXED_PORT | UPF_FIXED_TYPE | + UPF_NO_THRE_TEST; port.bugs |= UART_BUG_TXRACE; rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); if (rc < 0) return rc; - if (of_property_read_u32(np, "clock-frequency", &clk)) { + rc = uart_read_port_properties(&port.port); + if (rc) + goto err_sysfs_remove; + + /* Get clk rate through clk driver if present */ + if (!port.port.uartclk) { vclk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(vclk)) { rc = dev_err_probe(dev, PTR_ERR(vclk), "clk or clock-frequency not defined\n"); goto err_sysfs_remove; } - clk = clk_get_rate(vclk); + port.port.uartclk = clk_get_rate(vclk); } /* If current-speed was set, then try not to change it. */ if (of_property_read_u32(np, "current-speed", &prop) == 0) - port.port.custom_divisor = clk / (16 * prop); - - /* Check for shifted address mapping */ - if (of_property_read_u32(np, "reg-offset", &prop) == 0) - port.port.mapbase += prop; - - /* Check for registers offset within the devices address range */ - if (of_property_read_u32(np, "reg-shift", &prop) == 0) - port.port.regshift = prop; + port.port.custom_divisor = port.port.uartclk / (16 * prop); - /* Check for fifo size */ - if (of_property_read_u32(np, "fifo-size", &prop) == 0) - port.port.fifosize = prop; - - /* Check for a fixed line number */ - rc = of_alias_get_id(np, "serial"); - if (rc >= 0) - port.port.line = rc; - - port.port.irq = irq_of_parse_and_map(np, 0); port.port.handle_irq = aspeed_vuart_handle_irq; - port.port.iotype = UPIO_MEM; port.port.type = PORT_ASPEED_VUART; - port.port.uartclk = clk; - port.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP - | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_NO_THRE_TEST; - - if (of_property_read_bool(np, "no-loopback-test")) - port.port.flags |= UPF_SKIP_TEST; if (port.port.fifosize) port.capabilities = UART_CAP_FIFO; @@ -503,7 +485,7 @@ static int aspeed_vuart_probe(struct platform_device *pdev) rc = serial8250_register_8250_port(&port); if (rc < 0) - goto err_clk_disable; + goto err_sysfs_remove; vuart->line = rc; vuart->port = serial8250_get_port(vuart->line); @@ -529,7 +511,7 @@ static int aspeed_vuart_probe(struct platform_device *pdev) rc = aspeed_vuart_set_lpc_address(vuart, prop); if (rc < 0) { dev_err_probe(dev, rc, "invalid value in aspeed,lpc-io-reg property\n"); - goto err_clk_disable; + goto err_sysfs_remove; } rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", sirq, 2); @@ -541,14 +523,14 @@ static int aspeed_vuart_probe(struct platform_device *pdev) rc = aspeed_vuart_set_sirq(vuart, sirq[0]); if (rc < 0) { dev_err_probe(dev, rc, "invalid sirq number in aspeed,lpc-interrupts property\n"); - goto err_clk_disable; + goto err_sysfs_remove; } sirq_polarity = aspeed_vuart_map_irq_polarity(sirq[1]); if (sirq_polarity < 0) { rc = dev_err_probe(dev, sirq_polarity, "invalid sirq polarity in aspeed,lpc-interrupts property\n"); - goto err_clk_disable; + goto err_sysfs_remove; } aspeed_vuart_set_sirq_polarity(vuart, sirq_polarity); @@ -559,8 +541,6 @@ static int aspeed_vuart_probe(struct platform_device *pdev) return 0; -err_clk_disable: - irq_dispose_mapping(port.port.irq); err_sysfs_remove: sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); return rc; diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c index beac6b340a..121a5ce860 100644 --- a/drivers/tty/serial/8250/8250_bcm2835aux.c +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -45,10 +45,6 @@ struct bcm2835aux_data { u32 cntl; }; -struct bcm2835_aux_serial_driver_data { - resource_size_t offset; -}; - static void bcm2835aux_rs485_start_tx(struct uart_8250_port *up) { if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) { @@ -85,10 +81,9 @@ static void bcm2835aux_rs485_stop_tx(struct uart_8250_port *up) static int bcm2835aux_serial_probe(struct platform_device *pdev) { - const struct bcm2835_aux_serial_driver_data *bcm_data; + const struct software_node *bcm2835_swnode; struct uart_8250_port up = { }; struct bcm2835aux_data *data; - resource_size_t offset = 0; struct resource *res; unsigned int uartclk; int ret; @@ -101,12 +96,8 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev) /* initialize data */ up.capabilities = UART_CAP_FIFO | UART_CAP_MINI; up.port.dev = &pdev->dev; - up.port.regshift = 2; up.port.type = PORT_16550; - up.port.iotype = UPIO_MEM; - up.port.fifosize = 8; - up.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE | - UPF_SKIP_TEST | UPF_IOREMAP; + up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SKIP_TEST | UPF_IOREMAP; up.port.rs485_config = serial8250_em485_config; up.port.rs485_supported = serial8250_em485_supported; up.rs485_start_tx = bcm2835aux_rs485_start_tx; @@ -122,12 +113,6 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev) if (IS_ERR(data->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(data->clk), "could not get clk\n"); - /* get the interrupt */ - ret = platform_get_irq(pdev, 0); - if (ret < 0) - return ret; - up.port.irq = ret; - /* map the main registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -135,52 +120,40 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev) return -EINVAL; } - bcm_data = device_get_match_data(&pdev->dev); - - /* Some UEFI implementations (e.g. tianocore/edk2 for the Raspberry Pi) - * describe the miniuart with a base address that encompasses the auxiliary - * registers shared between the miniuart and spi. - * - * This is due to historical reasons, see discussion here : - * https://edk2.groups.io/g/devel/topic/87501357#84349 - * - * We need to add the offset between the miniuart and auxiliary - * registers to get the real miniuart base address. - */ - if (bcm_data) - offset = bcm_data->offset; + up.port.mapbase = res->start; + up.port.mapsize = resource_size(res); + + bcm2835_swnode = device_get_match_data(&pdev->dev); + if (bcm2835_swnode) { + ret = device_add_software_node(&pdev->dev, bcm2835_swnode); + if (ret) + return ret; + } - up.port.mapbase = res->start + offset; - up.port.mapsize = resource_size(res) - offset; + ret = uart_read_port_properties(&up.port); + if (ret) + goto rm_swnode; - /* Check for a fixed line number */ - ret = of_alias_get_id(pdev->dev.of_node, "serial"); - if (ret >= 0) - up.port.line = ret; + up.port.regshift = 2; + up.port.fifosize = 8; /* enable the clock as a last step */ ret = clk_prepare_enable(data->clk); if (ret) { - dev_err(&pdev->dev, "unable to enable uart clock - %d\n", - ret); - return ret; + dev_err_probe(&pdev->dev, ret, "unable to enable uart clock\n"); + goto rm_swnode; } uartclk = clk_get_rate(data->clk); - if (!uartclk) { - ret = device_property_read_u32(&pdev->dev, "clock-frequency", &uartclk); - if (ret) { - dev_err_probe(&pdev->dev, ret, "could not get clk rate\n"); - goto dis_clk; - } - } + if (uartclk) + up.port.uartclk = uartclk; /* the HW-clock divider for bcm2835aux is 8, * but 8250 expects a divider of 16, * so we have to multiply the actual clock by 2 * to get identical baudrates. */ - up.port.uartclk = uartclk * 2; + up.port.uartclk *= 2; /* register the port */ ret = serial8250_register_8250_port(&up); @@ -194,6 +167,8 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev) dis_clk: clk_disable_unprepare(data->clk); +rm_swnode: + device_remove_software_node(&pdev->dev); return ret; } @@ -203,10 +178,27 @@ static void bcm2835aux_serial_remove(struct platform_device *pdev) serial8250_unregister_port(data->line); clk_disable_unprepare(data->clk); + device_remove_software_node(&pdev->dev); } -static const struct bcm2835_aux_serial_driver_data bcm2835_acpi_data = { - .offset = 0x40, +/* + * Some UEFI implementations (e.g. tianocore/edk2 for the Raspberry Pi) + * describe the miniuart with a base address that encompasses the auxiliary + * registers shared between the miniuart and spi. + * + * This is due to historical reasons, see discussion here: + * https://edk2.groups.io/g/devel/topic/87501357#84349 + * + * We need to add the offset between the miniuart and auxiliary registers + * to get the real miniuart base address. + */ +static const struct property_entry bcm2835_acpi_properties[] = { + PROPERTY_ENTRY_U32("reg-offset", 0x40), + { } +}; + +static const struct software_node bcm2835_acpi_node = { + .properties = bcm2835_acpi_properties, }; static const struct of_device_id bcm2835aux_serial_match[] = { @@ -216,7 +208,7 @@ static const struct of_device_id bcm2835aux_serial_match[] = { MODULE_DEVICE_TABLE(of, bcm2835aux_serial_match); static const struct acpi_device_id bcm2835aux_serial_acpi_match[] = { - { "BCM2836", (kernel_ulong_t)&bcm2835_acpi_data }, + { "BCM2836", (kernel_ulong_t)&bcm2835_acpi_node }, { } }; MODULE_DEVICE_TABLE(acpi, bcm2835aux_serial_acpi_match); diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c index 4f8b70f8ce..61d81b11f6 100644 --- a/drivers/tty/serial/8250/8250_bcm7271.c +++ b/drivers/tty/serial/8250/8250_bcm7271.c @@ -22,6 +22,7 @@ #include <linux/delay.h> #include <linux/clk.h> #include <linux/debugfs.h> +#include <linux/units.h> #include "8250.h" @@ -187,21 +188,19 @@ #define TX_BUF_SIZE 4096 #define RX_BUF_SIZE 4096 #define RX_BUFS_COUNT 2 -#define KHZ 1000 -#define MHZ(x) ((x) * KHZ * KHZ) static const u32 brcmstb_rate_table[] = { - MHZ(81), - MHZ(108), - MHZ(64), /* Actually 64285715 for some chips */ - MHZ(48), + 81 * HZ_PER_MHZ, + 108 * HZ_PER_MHZ, + 64 * HZ_PER_MHZ, /* Actually 64285715 for some chips */ + 48 * HZ_PER_MHZ, }; static const u32 brcmstb_rate_table_7278[] = { - MHZ(81), - MHZ(108), + 81 * HZ_PER_MHZ, + 108 * HZ_PER_MHZ, 0, - MHZ(48), + 48 * HZ_PER_MHZ, }; struct brcmuart_priv { @@ -955,17 +954,14 @@ static void brcmuart_init_debugfs(struct brcmuart_priv *priv, static int brcmuart_probe(struct platform_device *pdev) { struct resource *regs; - struct device_node *np = pdev->dev.of_node; const struct of_device_id *of_id = NULL; struct uart_8250_port *new_port; struct device *dev = &pdev->dev; struct brcmuart_priv *priv; struct clk *baud_mux_clk; struct uart_8250_port up; - int irq; void __iomem *membase = NULL; resource_size_t mapbase = 0; - u32 clk_rate = 0; int ret; int x; int dma_irq; @@ -973,15 +969,12 @@ static int brcmuart_probe(struct platform_device *pdev) "uart", "dma_rx", "dma_tx", "dma_intr2", "dma_arb" }; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; priv = devm_kzalloc(dev, sizeof(struct brcmuart_priv), GFP_KERNEL); if (!priv) return -ENOMEM; - of_id = of_match_node(brcmuart_dt_ids, np); + of_id = of_match_node(brcmuart_dt_ids, dev->of_node); if (!of_id || !of_id->data) priv->rate_table = brcmstb_rate_table; else @@ -1031,7 +1024,23 @@ static int brcmuart_probe(struct platform_device *pdev) } } - of_property_read_u32(np, "clock-frequency", &clk_rate); + dev_dbg(dev, "DMA is %senabled\n", priv->dma_enabled ? "" : "not "); + + memset(&up, 0, sizeof(up)); + up.port.type = PORT_BCM7271; + up.port.dev = dev; + up.port.mapbase = mapbase; + up.port.membase = membase; + up.port.handle_irq = brcmuart_handle_irq; + up.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_FIXED_TYPE; + up.port.private_data = priv; + + ret = uart_read_port_properties(&up.port); + if (ret) + goto release_dma; + + up.port.regshift = 2; + up.port.iotype = device_is_big_endian(dev) ? UPIO_MEM32BE : UPIO_MEM32; /* See if a Baud clock has been specified */ baud_mux_clk = devm_clk_get_optional_enabled(dev, "sw_baud"); @@ -1043,39 +1052,11 @@ static int brcmuart_probe(struct platform_device *pdev) priv->baud_mux_clk = baud_mux_clk; init_real_clk_rates(dev, priv); - clk_rate = priv->default_mux_rate; + up.port.uartclk = priv->default_mux_rate; } else { dev_dbg(dev, "BAUD MUX clock not specified\n"); } - if (clk_rate == 0) { - ret = dev_err_probe(dev, -EINVAL, "clock-frequency or clk not defined\n"); - goto release_dma; - } - - dev_dbg(dev, "DMA is %senabled\n", priv->dma_enabled ? "" : "not "); - - memset(&up, 0, sizeof(up)); - up.port.type = PORT_BCM7271; - up.port.uartclk = clk_rate; - up.port.dev = dev; - up.port.mapbase = mapbase; - up.port.membase = membase; - up.port.irq = irq; - up.port.handle_irq = brcmuart_handle_irq; - up.port.regshift = 2; - up.port.iotype = of_device_is_big_endian(np) ? - UPIO_MEM32BE : UPIO_MEM32; - up.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF - | UPF_FIXED_PORT | UPF_FIXED_TYPE; - up.port.dev = dev; - up.port.private_data = priv; - - /* Check for a fixed line number */ - ret = of_alias_get_id(np, "serial"); - if (ret >= 0) - up.port.line = ret; - /* setup HR timer */ hrtimer_init(&priv->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); priv->hrt.function = brcmuart_hrtimer_func; diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 2d1f350a4b..a58890fd53 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -9,7 +9,6 @@ * LCR is written whilst busy. If it is, then a busy detect interrupt is * raised, the LCR needs to be rewritten and the uart status register read. */ -#include <linux/acpi.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> @@ -17,7 +16,6 @@ #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/notifier.h> -#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/property.h> @@ -56,6 +54,35 @@ #define DW_UART_QUIRK_ARMADA_38X BIT(1) #define DW_UART_QUIRK_SKIP_SET_RATE BIT(2) #define DW_UART_QUIRK_IS_DMA_FC BIT(3) +#define DW_UART_QUIRK_APMC0D08 BIT(4) +#define DW_UART_QUIRK_CPR_VALUE BIT(5) + +struct dw8250_platform_data { + u8 usr_reg; + u32 cpr_value; + unsigned int quirks; +}; + +struct dw8250_data { + struct dw8250_port_data data; + const struct dw8250_platform_data *pdata; + + int msr_mask_on; + int msr_mask_off; + struct clk *clk; + struct clk *pclk; + struct notifier_block clk_notifier; + struct work_struct clk_work; + struct reset_control *rst; + + unsigned int skip_autocfg:1; + unsigned int uart_16550_compatible:1; +}; + +static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data) +{ + return container_of(data, struct dw8250_data, data); +} static inline struct dw8250_data *clk_to_dw8250_data(struct notifier_block *nb) { @@ -445,44 +472,33 @@ static void dw8250_prepare_rx_dma(struct uart_8250_port *p) static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) { - struct device_node *np = p->dev->of_node; + unsigned int quirks = data->pdata ? data->pdata->quirks : 0; + u32 cpr_value = data->pdata ? data->pdata->cpr_value : 0; - if (np) { - unsigned int quirks = data->pdata->quirks; - int id; + if (quirks & DW_UART_QUIRK_CPR_VALUE) + data->data.cpr_value = cpr_value; - /* get index of serial line, if found in DT aliases */ - id = of_alias_get_id(np, "serial"); - if (id >= 0) - p->line = id; #ifdef CONFIG_64BIT - if (quirks & DW_UART_QUIRK_OCTEON) { - p->serial_in = dw8250_serial_inq; - p->serial_out = dw8250_serial_outq; - p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; - p->type = PORT_OCTEON; - data->skip_autocfg = true; - } + if (quirks & DW_UART_QUIRK_OCTEON) { + p->serial_in = dw8250_serial_inq; + p->serial_out = dw8250_serial_outq; + p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; + p->type = PORT_OCTEON; + data->skip_autocfg = true; + } #endif - if (of_device_is_big_endian(np)) { - p->iotype = UPIO_MEM32BE; - p->serial_in = dw8250_serial_in32be; - p->serial_out = dw8250_serial_out32be; - } - - if (quirks & DW_UART_QUIRK_ARMADA_38X) - p->serial_out = dw8250_serial_out38x; - if (quirks & DW_UART_QUIRK_SKIP_SET_RATE) - p->set_termios = dw8250_do_set_termios; - if (quirks & DW_UART_QUIRK_IS_DMA_FC) { - data->data.dma.txconf.device_fc = 1; - data->data.dma.rxconf.device_fc = 1; - data->data.dma.prepare_tx_dma = dw8250_prepare_tx_dma; - data->data.dma.prepare_rx_dma = dw8250_prepare_rx_dma; - } - - } else if (acpi_dev_present("APMC0D08", NULL, -1)) { + if (quirks & DW_UART_QUIRK_ARMADA_38X) + p->serial_out = dw8250_serial_out38x; + if (quirks & DW_UART_QUIRK_SKIP_SET_RATE) + p->set_termios = dw8250_do_set_termios; + if (quirks & DW_UART_QUIRK_IS_DMA_FC) { + data->data.dma.txconf.device_fc = 1; + data->data.dma.rxconf.device_fc = 1; + data->data.dma.prepare_tx_dma = dw8250_prepare_tx_dma; + data->data.dma.prepare_rx_dma = dw8250_prepare_rx_dma; + } + if (quirks & DW_UART_QUIRK_APMC0D08) { p->iotype = UPIO_MEM32; p->regshift = 2; p->serial_in = dw8250_serial_in32; @@ -510,39 +526,21 @@ static int dw8250_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct dw8250_data *data; struct resource *regs; - int irq; int err; - u32 val; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) return dev_err_probe(dev, -EINVAL, "no registers defined\n"); - irq = platform_get_irq_optional(pdev, 0); - /* no interrupt -> fall back to polling */ - if (irq == -ENXIO) - irq = 0; - if (irq < 0) - return irq; - spin_lock_init(&p->lock); - p->mapbase = regs->start; - p->irq = irq; p->handle_irq = dw8250_handle_irq; p->pm = dw8250_do_pm; p->type = PORT_8250; - p->flags = UPF_SHARE_IRQ | UPF_FIXED_PORT; + p->flags = UPF_FIXED_PORT; p->dev = dev; - p->iotype = UPIO_MEM; - p->serial_in = dw8250_serial_in; - p->serial_out = dw8250_serial_out; p->set_ldisc = dw8250_set_ldisc; p->set_termios = dw8250_set_termios; - p->membase = devm_ioremap(dev, regs->start, resource_size(regs)); - if (!p->membase) - return -ENOMEM; - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -554,15 +552,35 @@ static int dw8250_probe(struct platform_device *pdev) data->uart_16550_compatible = device_property_read_bool(dev, "snps,uart-16550-compatible"); - err = device_property_read_u32(dev, "reg-shift", &val); - if (!err) - p->regshift = val; + p->mapbase = regs->start; + p->mapsize = resource_size(regs); - err = device_property_read_u32(dev, "reg-io-width", &val); - if (!err && val == 4) { - p->iotype = UPIO_MEM32; + p->membase = devm_ioremap(dev, p->mapbase, p->mapsize); + if (!p->membase) + return -ENOMEM; + + err = uart_read_port_properties(p); + /* no interrupt -> fall back to polling */ + if (err == -ENXIO) + err = 0; + if (err) + return err; + + switch (p->iotype) { + case UPIO_MEM: + p->serial_in = dw8250_serial_in; + p->serial_out = dw8250_serial_out; + break; + case UPIO_MEM32: p->serial_in = dw8250_serial_in32; p->serial_out = dw8250_serial_out32; + break; + case UPIO_MEM32BE: + p->serial_in = dw8250_serial_in32be; + p->serial_out = dw8250_serial_out32be; + break; + default: + return -ENODEV; } if (device_property_read_bool(dev, "dcd-override")) { @@ -589,15 +607,13 @@ static int dw8250_probe(struct platform_device *pdev) data->msr_mask_off |= UART_MSR_TERI; } - /* Always ask for fixed clock rate from a property. */ - device_property_read_u32(dev, "clock-frequency", &p->uartclk); - /* If there is separate baudclk, get the rate from it. */ data->clk = devm_clk_get_optional_enabled(dev, "baudclk"); if (data->clk == NULL) data->clk = devm_clk_get_optional_enabled(dev, NULL); if (IS_ERR(data->clk)) - return PTR_ERR(data->clk); + return dev_err_probe(dev, PTR_ERR(data->clk), + "failed to get baudclk\n"); INIT_WORK(&data->clk_work, dw8250_clk_work_cb); data->clk_notifier.notifier_call = dw8250_clk_notifier_cb; @@ -743,8 +759,8 @@ static const struct dw8250_platform_data dw8250_armada_38x_data = { static const struct dw8250_platform_data dw8250_renesas_rzn1_data = { .usr_reg = DW_UART_USR, - .cpr_val = 0x00012f32, - .quirks = DW_UART_QUIRK_IS_DMA_FC, + .cpr_value = 0x00012f32, + .quirks = DW_UART_QUIRK_CPR_VALUE | DW_UART_QUIRK_IS_DMA_FC, }; static const struct dw8250_platform_data dw8250_starfive_jh7100_data = { @@ -762,13 +778,18 @@ static const struct of_device_id dw8250_of_match[] = { }; MODULE_DEVICE_TABLE(of, dw8250_of_match); +static const struct dw8250_platform_data dw8250_apmc0d08 = { + .usr_reg = DW_UART_USR, + .quirks = DW_UART_QUIRK_APMC0D08, +}; + static const struct acpi_device_id dw8250_acpi_match[] = { { "80860F0A", (kernel_ulong_t)&dw8250_dw_apb }, { "8086228A", (kernel_ulong_t)&dw8250_dw_apb }, { "AMD0020", (kernel_ulong_t)&dw8250_dw_apb }, { "AMDI0020", (kernel_ulong_t)&dw8250_dw_apb }, { "AMDI0022", (kernel_ulong_t)&dw8250_dw_apb }, - { "APMC0D08", (kernel_ulong_t)&dw8250_dw_apb}, + { "APMC0D08", (kernel_ulong_t)&dw8250_apmc0d08 }, { "BRCM2032", (kernel_ulong_t)&dw8250_dw_apb }, { "HISI0031", (kernel_ulong_t)&dw8250_dw_apb }, { "INT33C4", (kernel_ulong_t)&dw8250_dw_apb }, diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c index 3e33ddf7bc..5a2520943d 100644 --- a/drivers/tty/serial/8250/8250_dwlib.c +++ b/drivers/tty/serial/8250/8250_dwlib.c @@ -242,7 +242,6 @@ static const struct serial_rs485 dw8250_rs485_supported = { void dw8250_setup_port(struct uart_port *p) { struct dw8250_port_data *pd = p->private_data; - struct dw8250_data *data = to_dw8250_data(pd); struct uart_8250_port *up = up_to_u8250p(p); u32 reg, old_dlf; @@ -278,7 +277,7 @@ void dw8250_setup_port(struct uart_port *p) reg = dw8250_readl_ext(p, DW_UART_CPR); if (!reg) { - reg = data->pdata->cpr_val; + reg = pd->cpr_value; dev_dbg(p->dev, "CPR is not available, using 0x%08x instead\n", reg); } if (!reg) diff --git a/drivers/tty/serial/8250/8250_dwlib.h b/drivers/tty/serial/8250/8250_dwlib.h index f13e91f2ca..7dd2a8e7b7 100644 --- a/drivers/tty/serial/8250/8250_dwlib.h +++ b/drivers/tty/serial/8250/8250_dwlib.h @@ -2,15 +2,10 @@ /* Synopsys DesignWare 8250 library header file. */ #include <linux/io.h> -#include <linux/notifier.h> #include <linux/types.h> -#include <linux/workqueue.h> #include "8250.h" -struct clk; -struct reset_control; - struct dw8250_port_data { /* Port properties */ int line; @@ -19,42 +14,16 @@ struct dw8250_port_data { struct uart_8250_dma dma; /* Hardware configuration */ + u32 cpr_value; u8 dlf_size; /* RS485 variables */ bool hw_rs485_support; }; -struct dw8250_platform_data { - u8 usr_reg; - u32 cpr_val; - unsigned int quirks; -}; - -struct dw8250_data { - struct dw8250_port_data data; - const struct dw8250_platform_data *pdata; - - int msr_mask_on; - int msr_mask_off; - struct clk *clk; - struct clk *pclk; - struct notifier_block clk_notifier; - struct work_struct clk_work; - struct reset_control *rst; - - unsigned int skip_autocfg:1; - unsigned int uart_16550_compatible:1; -}; - void dw8250_do_set_termios(struct uart_port *p, struct ktermios *termios, const struct ktermios *old); void dw8250_setup_port(struct uart_port *p); -static inline struct dw8250_data *to_dw8250_data(struct dw8250_port_data *data) -{ - return container_of(data, struct dw8250_data, data); -} - static inline u32 dw8250_readl_ext(struct uart_port *p, int offset) { if (p->iotype == UPIO_MEM32BE) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index dab94835b6..4d1e07343d 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -6,23 +6,29 @@ * * Copyright (C) 2017 Sudip Mukherjee, All Rights Reserved. */ -#include <linux/acpi.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/device.h> #include <linux/dmi.h> +#include <linux/interrupt.h> #include <linux/io.h> -#include <linux/kernel.h> +#include <linux/math.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/pm.h> #include <linux/property.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <linux/serial_8250.h> #include <linux/serial_core.h> #include <linux/serial_reg.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/tty.h> -#include <linux/delay.h> #include <asm/byteorder.h> #include "8250.h" +#include "8250_pcilib.h" #define PCI_DEVICE_ID_ACCESSIO_COM_2S 0x1052 #define PCI_DEVICE_ID_ACCESSIO_COM_4S 0x105d @@ -40,8 +46,50 @@ #define PCI_DEVICE_ID_COMMTECH_4228PCIE 0x0021 #define PCI_DEVICE_ID_COMMTECH_4222PCIE 0x0022 +#define PCI_VENDOR_ID_CONNECT_TECH 0x12c4 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_SP_OPTO 0x0340 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_A 0x0341 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_SP_OPTO_B 0x0342 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS 0x0350 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_A 0x0351 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_B 0x0352 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS 0x0353 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_A 0x0354 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_16_XPRS_B 0x0355 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XPRS_OPTO 0x0360 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_A 0x0361 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XPRS_OPTO_B 0x0362 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP 0x0370 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232 0x0371 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_485 0x0372 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_SP 0x0373 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_6_2_SP 0x0374 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_6_SP 0x0375 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_SP_232_NS 0x0376 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_LEFT 0x0380 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_2_XP_OPTO_RIGHT 0x0381 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_XP_OPTO 0x0382 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_4_4_XPRS_OPTO 0x0392 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP 0x03A0 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232 0x03A1 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_485 0x03A2 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_XPRS_LP_232_NS 0x03A3 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XEG001 0x0602 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_BASE 0x1000 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_2 0x1002 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_4 0x1004 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_8 0x1008 +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_12 0x100C +#define PCI_SUBDEVICE_ID_CONNECT_TECH_PCIE_XR35X_16 0x1010 +#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG00X 0x110c +#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_12_XIG01X 0x110d +#define PCI_DEVICE_ID_CONNECT_TECH_PCI_XR79X_16 0x1110 + #define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358 #define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358 +#define PCI_DEVICE_ID_EXAR_XR17V252 0x0252 +#define PCI_DEVICE_ID_EXAR_XR17V254 0x0254 +#define PCI_DEVICE_ID_EXAR_XR17V258 0x0258 #define PCI_SUBDEVICE_ID_USR_2980 0x0128 #define PCI_SUBDEVICE_ID_USR_2981 0x0129 @@ -229,13 +277,12 @@ static int default_setup(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port) { const struct exar8250_board *board = priv->board; - unsigned int bar = 0; unsigned char status; + int err; - port->port.iotype = UPIO_MEM; - port->port.mapbase = pci_resource_start(pcidev, bar) + offset; - port->port.membase = priv->virt + offset; - port->port.regshift = board->reg_shift; + err = serial8250_pci_setup_port(pcidev, port, 0, offset, board->reg_shift); + if (err) + return err; /* * XR17V35x UARTs have an extra divisor register, DLD that gets enabled @@ -375,7 +422,7 @@ static struct platform_device *__xr17v35x_register_gpio(struct pci_dev *pcidev, return NULL; pdev->dev.parent = &pcidev->dev; - ACPI_COMPANION_SET(&pdev->dev, ACPI_COMPANION(&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) { @@ -713,14 +760,14 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent) uart.port.irq = pci_irq_vector(pcidev, 0); uart.port.dev = &pcidev->dev; + /* Clear interrupts */ + exar_misc_clear(priv); + rc = devm_request_irq(&pcidev->dev, uart.port.irq, exar_misc_handler, IRQF_SHARED, "exar_uart", priv); if (rc) return rc; - /* Clear interrupts */ - exar_misc_clear(priv); - for (i = 0; i < nr_ports && i < maxnr; i++) { rc = board->setup(priv, pcidev, &uart, i); if (rc) { @@ -758,10 +805,9 @@ static void exar_pci_remove(struct pci_dev *pcidev) priv->board->exit(pcidev); } -static int __maybe_unused exar_suspend(struct device *dev) +static int exar_suspend(struct device *dev) { - struct pci_dev *pcidev = to_pci_dev(dev); - struct exar8250 *priv = pci_get_drvdata(pcidev); + struct exar8250 *priv = dev_get_drvdata(dev); unsigned int i; for (i = 0; i < priv->nr; i++) @@ -771,7 +817,7 @@ static int __maybe_unused exar_suspend(struct device *dev) return 0; } -static int __maybe_unused exar_resume(struct device *dev) +static int exar_resume(struct device *dev) { struct exar8250 *priv = dev_get_drvdata(dev); unsigned int i; @@ -785,7 +831,7 @@ static int __maybe_unused exar_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(exar_pci_pm, exar_suspend, exar_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(exar_pci_pm, exar_suspend, exar_resume); static const struct exar8250_board pbn_fastcom335_2 = { .num_ports = 2, @@ -935,12 +981,13 @@ static struct pci_driver exar_pci_driver = { .probe = exar_pci_probe, .remove = exar_pci_remove, .driver = { - .pm = &exar_pci_pm, + .pm = pm_sleep_ptr(&exar_pci_pm), }, .id_table = exar_pci_tbl, }; module_pci_driver(exar_pci_driver); +MODULE_IMPORT_NS(SERIAL_8250_PCI); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Exar Serial Driver"); MODULE_AUTHOR("Sudip Mukherjee <sudip.mukherjee@codethink.co.uk>"); diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c index a12f737924..a2783e38a2 100644 --- a/drivers/tty/serial/8250/8250_ingenic.c +++ b/drivers/tty/serial/8250/8250_ingenic.c @@ -234,7 +234,7 @@ static int ingenic_uart_probe(struct platform_device *pdev) struct ingenic_uart_data *data; const struct ingenic_uart_config *cdata; struct resource *regs; - int irq, err, line; + int err; cdata = of_device_get_match_data(&pdev->dev); if (!cdata) { @@ -242,10 +242,6 @@ static int ingenic_uart_probe(struct platform_device *pdev) return -ENODEV; } - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) { dev_err(&pdev->dev, "no registers defined\n"); @@ -259,21 +255,19 @@ static int ingenic_uart_probe(struct platform_device *pdev) spin_lock_init(&uart.port.lock); uart.port.type = PORT_16550A; uart.port.flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE; - uart.port.iotype = UPIO_MEM; uart.port.mapbase = regs->start; - uart.port.regshift = 2; uart.port.serial_out = ingenic_uart_serial_out; uart.port.serial_in = ingenic_uart_serial_in; - uart.port.irq = irq; uart.port.dev = &pdev->dev; - uart.port.fifosize = cdata->fifosize; uart.tx_loadsz = cdata->tx_loadsz; uart.capabilities = UART_CAP_FIFO | UART_CAP_RTOIE; - /* Check for a fixed line number */ - line = of_alias_get_id(pdev->dev.of_node, "serial"); - if (line >= 0) - uart.port.line = line; + err = uart_read_port_properties(&uart.port); + if (err) + return err; + + uart.port.regshift = 2; + uart.port.fifosize = cdata->fifosize; uart.port.membase = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); diff --git a/drivers/tty/serial/8250/8250_lpc18xx.c b/drivers/tty/serial/8250/8250_lpc18xx.c index 8d728a6a59..47e1a056a6 100644 --- a/drivers/tty/serial/8250/8250_lpc18xx.c +++ b/drivers/tty/serial/8250/8250_lpc18xx.c @@ -92,11 +92,7 @@ static int lpc18xx_serial_probe(struct platform_device *pdev) struct lpc18xx_uart_data *data; struct uart_8250_port uart; struct resource *res; - int irq, ret; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -139,19 +135,12 @@ static int lpc18xx_serial_probe(struct platform_device *pdev) goto dis_clk_reg; } - ret = of_alias_get_id(pdev->dev.of_node, "serial"); - if (ret >= 0) - uart.port.line = ret; - data->dma.rx_param = data; data->dma.tx_param = data; spin_lock_init(&uart.port.lock); uart.port.dev = &pdev->dev; - uart.port.irq = irq; - uart.port.iotype = UPIO_MEM32; uart.port.mapbase = res->start; - uart.port.regshift = 2; uart.port.type = PORT_16550A; uart.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SKIP_TEST; uart.port.uartclk = clk_get_rate(data->clk_uart); @@ -160,6 +149,13 @@ static int lpc18xx_serial_probe(struct platform_device *pdev) uart.port.rs485_supported = lpc18xx_rs485_supported; uart.port.serial_out = lpc18xx_uart_serial_out; + ret = uart_read_port_properties(&uart.port); + if (ret) + goto dis_uart_clk; + + uart.port.iotype = UPIO_MEM32; + uart.port.regshift = 2; + uart.dma = &data->dma; uart.dma->rxconf.src_maxburst = 1; uart.dma->txconf.dst_maxburst = 1; diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 9dcc17e332..5d1dd992d8 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -69,37 +69,22 @@ static int of_platform_serial_setup(struct platform_device *ofdev, struct device *dev = &ofdev->dev; struct device_node *np = dev->of_node; struct uart_port *port = &up->port; - u32 clk, spd, prop; - int ret, irq; + u32 spd; + int ret; memset(port, 0, sizeof *port); pm_runtime_enable(&ofdev->dev); pm_runtime_get_sync(&ofdev->dev); - if (of_property_read_u32(np, "clock-frequency", &clk)) { - - /* Get clk rate through clk driver if present */ - info->clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(info->clk)) { - ret = dev_err_probe(dev, PTR_ERR(info->clk), "failed to get clock\n"); - goto err_pmruntime; - } - - clk = clk_get_rate(info->clk); - } - /* If current-speed was set, then try not to change it. */ - if (of_property_read_u32(np, "current-speed", &spd) == 0) - port->custom_divisor = clk / (16 * spd); - ret = of_address_to_resource(np, 0, &resource); if (ret) { dev_err_probe(dev, ret, "invalid address\n"); goto err_pmruntime; } - port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | - UPF_FIXED_TYPE; + port->dev = &ofdev->dev; + port->flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_FIXED_TYPE; spin_lock_init(&port->lock); if (resource_type(&resource) == IORESOURCE_IO) { @@ -108,70 +93,31 @@ static int of_platform_serial_setup(struct platform_device *ofdev, } else { port->mapbase = resource.start; port->mapsize = resource_size(&resource); + port->flags |= UPF_IOREMAP; + } - /* Check for shifted address mapping */ - if (of_property_read_u32(np, "reg-offset", &prop) == 0) { - if (prop >= port->mapsize) { - ret = dev_err_probe(dev, -EINVAL, "reg-offset %u exceeds region size %pa\n", - prop, &port->mapsize); - goto err_pmruntime; - } + ret = uart_read_and_validate_port_properties(port); + if (ret) + goto err_pmruntime; - port->mapbase += prop; - port->mapsize -= prop; + /* Get clk rate through clk driver if present */ + if (!port->uartclk) { + info->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(info->clk)) { + ret = dev_err_probe(dev, PTR_ERR(info->clk), "failed to get clock\n"); + goto err_pmruntime; } - port->iotype = UPIO_MEM; - if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { - switch (prop) { - case 1: - port->iotype = UPIO_MEM; - break; - case 2: - port->iotype = UPIO_MEM16; - break; - case 4: - port->iotype = of_device_is_big_endian(np) ? - UPIO_MEM32BE : UPIO_MEM32; - break; - default: - ret = dev_err_probe(dev, -EINVAL, "unsupported reg-io-width (%u)\n", - prop); - goto err_pmruntime; - } - } - port->flags |= UPF_IOREMAP; + port->uartclk = clk_get_rate(info->clk); } + /* If current-speed was set, then try not to change it. */ + if (of_property_read_u32(np, "current-speed", &spd) == 0) + port->custom_divisor = port->uartclk / (16 * spd); /* Compatibility with the deprecated pxa driver and 8250_pxa drivers. */ if (of_device_is_compatible(np, "mrvl,mmp-uart")) port->regshift = 2; - /* Check for registers offset within the devices address range */ - if (of_property_read_u32(np, "reg-shift", &prop) == 0) - port->regshift = prop; - - /* Check for fifo size */ - if (of_property_read_u32(np, "fifo-size", &prop) == 0) - port->fifosize = prop; - - /* Check for a fixed line number */ - ret = of_alias_get_id(np, "serial"); - if (ret >= 0) - port->line = ret; - - irq = of_irq_get(np, 0); - if (irq < 0) { - if (irq == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto err_pmruntime; - } - /* IRQ support not mandatory */ - irq = 0; - } - - port->irq = irq; - info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL); if (IS_ERR(info->rst)) { ret = PTR_ERR(info->rst); @@ -183,12 +129,6 @@ static int of_platform_serial_setup(struct platform_device *ofdev, goto err_pmruntime; port->type = type; - port->uartclk = clk; - - if (of_property_read_bool(np, "no-loopback-test")) - port->flags |= UPF_SKIP_TEST; - - port->dev = &ofdev->dev; port->rs485_config = serial8250_em485_config; port->rs485_supported = serial8250_em485_supported; up->rs485_start_tx = serial8250_em485_start_tx; @@ -280,7 +220,6 @@ static int of_platform_serial_probe(struct platform_device *ofdev) platform_set_drvdata(ofdev, info); return 0; err_dispose: - irq_dispose_mapping(port8250.port.irq); pm_runtime_put_sync(&ofdev->dev); pm_runtime_disable(&ofdev->dev); err_free: diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 6942990a33..66901d9308 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1394,11 +1394,7 @@ static int omap8250_probe(struct platform_device *pdev) struct uart_8250_port up; struct resource *regs; void __iomem *membase; - int irq, ret; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + int ret; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) { @@ -1419,7 +1415,6 @@ static int omap8250_probe(struct platform_device *pdev) up.port.dev = &pdev->dev; up.port.mapbase = regs->start; up.port.membase = membase; - up.port.irq = irq; /* * It claims to be 16C750 compatible however it is a little different. * It has EFR and has no FCR7_64byte bit. The AFE (which it claims to @@ -1429,13 +1424,9 @@ static int omap8250_probe(struct platform_device *pdev) * or pm callback. */ up.port.type = PORT_8250; - up.port.iotype = UPIO_MEM; - up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SOFT_FLOW | - UPF_HARD_FLOW; + up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SOFT_FLOW | UPF_HARD_FLOW; up.port.private_data = priv; - up.port.regshift = OMAP_UART_REGSHIFT; - up.port.fifosize = 64; up.tx_loadsz = 64; up.capabilities = UART_CAP_FIFO; #ifdef CONFIG_PM @@ -1461,14 +1452,14 @@ static int omap8250_probe(struct platform_device *pdev) up.rs485_stop_tx = serial8250_em485_stop_tx; up.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); - ret = of_alias_get_id(np, "serial"); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get alias\n"); + ret = uart_read_port_properties(&up.port); + if (ret) return ret; - } - up.port.line = ret; - if (of_property_read_u32(np, "clock-frequency", &up.port.uartclk)) { + up.port.regshift = OMAP_UART_REGSHIFT; + up.port.fifosize = 64; + + if (!up.port.uartclk) { struct clk *clk; clk = devm_clk_get(&pdev->dev, NULL); @@ -1560,8 +1551,8 @@ static int omap8250_probe(struct platform_device *pdev) } #endif - irq_set_status_flags(irq, IRQ_NOAUTOEN); - ret = devm_request_irq(&pdev->dev, irq, omap8250_irq, 0, + irq_set_status_flags(up.port.irq, IRQ_NOAUTOEN); + ret = devm_request_irq(&pdev->dev, up.port.irq, omap8250_irq, 0, dev_name(&pdev->dev), priv); if (ret < 0) return ret; diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 0d35c77fad..e2e4f99f9d 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -5010,12 +5010,6 @@ static const struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_A, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, - { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_B, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_bt_2_115200 }, { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_4_460800 }, diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c index 2dda737b16..2fbb5851f7 100644 --- a/drivers/tty/serial/8250/8250_pci1xxxx.c +++ b/drivers/tty/serial/8250/8250_pci1xxxx.c @@ -7,23 +7,31 @@ * Copyright (C) 2022 Microchip Technology Inc., All Rights Reserved. */ +#include <linux/array_size.h> #include <linux/bitfield.h> -#include <linux/bitops.h> -#include <linux/delay.h> +#include <linux/bits.h> +#include <linux/circ_buf.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gfp_types.h> #include <linux/io.h> #include <linux/iopoll.h> -#include <linux/kernel.h> +#include <linux/minmax.h> #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/overflow.h> #include <linux/pci.h> +#include <linux/pm.h> #include <linux/serial_core.h> #include <linux/serial_reg.h> #include <linux/serial_8250.h> -#include <linux/slab.h> +#include <linux/spinlock.h> #include <linux/string.h> -#include <linux/units.h> +#include <linux/time.h> #include <linux/tty.h> #include <linux/tty_flip.h> -#include <linux/8250_pci.h> +#include <linux/types.h> +#include <linux/units.h> #include <asm/byteorder.h> @@ -67,6 +75,7 @@ #define SYSLOCK_RETRY_CNT 1000 #define UART_RX_BYTE_FIFO 0x00 +#define UART_TX_BYTE_FIFO 0x00 #define UART_FIFO_CTL 0x02 #define UART_ACTV_REG 0x11 @@ -81,10 +90,10 @@ #define ADCL_CFG_PIN_SEL BIT(1) #define ADCL_CFG_EN BIT(0) -#define UART_BIT_SAMPLE_CNT 16 +#define UART_BIT_SAMPLE_CNT_8 8 +#define UART_BIT_SAMPLE_CNT_16 16 #define BAUD_CLOCK_DIV_INT_MSK GENMASK(31, 8) #define ADCL_CFG_RTS_DELAY_MASK GENMASK(11, 8) -#define UART_CLOCK_DEFAULT (62500 * HZ_PER_KHZ) #define UART_WAKE_REG 0x8C #define UART_WAKE_MASK_REG 0x90 @@ -95,13 +104,19 @@ (UART_WAKE_N_PIN | UART_WAKE_NCTS | UART_WAKE_INT) #define UART_BAUD_CLK_DIVISOR_REG 0x54 +#define FRAC_DIV_CFG_REG 0x58 #define UART_RESET_REG 0x94 #define UART_RESET_D3_RESET_DISABLE BIT(16) #define UART_BURST_STATUS_REG 0x9C +#define UART_TX_BURST_FIFO 0xA0 #define UART_RX_BURST_FIFO 0xA4 +#define UART_BIT_DIVISOR_8 0x26731000 +#define UART_BIT_DIVISOR_16 0x6ef71000 +#define UART_BAUD_4MBPS 4000000 + #define MAX_PORTS 4 #define PORT_OFFSET 0x100 #define RX_BUF_SIZE 512 @@ -109,6 +124,7 @@ #define UART_BURST_SIZE 4 #define UART_BST_STAT_RX_COUNT_MASK 0x00FF +#define UART_BST_STAT_TX_COUNT_MASK 0xFF00 #define UART_BST_STAT_IIR_INT_PEND 0x100000 #define UART_LSR_OVERRUN_ERR_CLR 0x43 #define UART_BST_STAT_LSR_RX_MASK 0x9F000000 @@ -116,6 +132,7 @@ #define UART_BST_STAT_LSR_OVERRUN_ERR 0x2000000 #define UART_BST_STAT_LSR_PARITY_ERR 0x4000000 #define UART_BST_STAT_LSR_FRAME_ERR 0x8000000 +#define UART_BST_STAT_LSR_THRE 0x20000000 struct pci1xxxx_8250 { unsigned int nr; @@ -206,15 +223,21 @@ static int pci1xxxx_get_num_ports(struct pci_dev *dev) static unsigned int pci1xxxx_get_divisor(struct uart_port *port, unsigned int baud, unsigned int *frac) { + unsigned int uart_sample_cnt; unsigned int quot; + if (baud >= UART_BAUD_4MBPS) + uart_sample_cnt = UART_BIT_SAMPLE_CNT_8; + else + uart_sample_cnt = UART_BIT_SAMPLE_CNT_16; + /* * Calculate baud rate sampling period in nanoseconds. * Fractional part x denotes x/255 parts of a nanosecond. */ - quot = NSEC_PER_SEC / (baud * UART_BIT_SAMPLE_CNT); - *frac = (NSEC_PER_SEC - quot * baud * UART_BIT_SAMPLE_CNT) * - 255 / UART_BIT_SAMPLE_CNT / baud; + quot = NSEC_PER_SEC / (baud * uart_sample_cnt); + *frac = (NSEC_PER_SEC - quot * baud * uart_sample_cnt) * + 255 / uart_sample_cnt / baud; return quot; } @@ -222,6 +245,11 @@ static unsigned int pci1xxxx_get_divisor(struct uart_port *port, static void pci1xxxx_set_divisor(struct uart_port *port, unsigned int baud, unsigned int quot, unsigned int frac) { + if (baud >= UART_BAUD_4MBPS) + writel(UART_BIT_DIVISOR_8, port->membase + FRAC_DIV_CFG_REG); + else + writel(UART_BIT_DIVISOR_16, port->membase + FRAC_DIV_CFG_REG); + writel(FIELD_PREP(BAUD_CLOCK_DIV_INT_MSK, quot) | frac, port->membase + UART_BAUD_CLK_DIVISOR_REG); } @@ -233,7 +261,16 @@ static int pci1xxxx_rs485_config(struct uart_port *port, u32 delay_in_baud_periods; u32 baud_period_in_ns; u32 mode_cfg = 0; + u32 sample_cnt; u32 clock_div; + u32 frac_div; + + frac_div = readl(port->membase + FRAC_DIV_CFG_REG); + + if (frac_div == UART_BIT_DIVISOR_16) + sample_cnt = UART_BIT_SAMPLE_CNT_16; + else + sample_cnt = UART_BIT_SAMPLE_CNT_8; /* * pci1xxxx's uart hardware supports only RTS delay after @@ -249,7 +286,7 @@ static int pci1xxxx_rs485_config(struct uart_port *port, clock_div = readl(port->membase + UART_BAUD_CLK_DIVISOR_REG); baud_period_in_ns = FIELD_GET(BAUD_CLOCK_DIV_INT_MSK, clock_div) * - UART_BIT_SAMPLE_CNT; + sample_cnt; delay_in_baud_periods = rs485->delay_rts_after_send * NSEC_PER_MSEC / baud_period_in_ns; @@ -344,6 +381,105 @@ static void pci1xxxx_rx_burst(struct uart_port *port, u32 uart_status) } } +static void pci1xxxx_process_write_data(struct uart_port *port, + struct circ_buf *xmit, + int *data_empty_count, + u32 *valid_byte_count) +{ + u32 valid_burst_count = *valid_byte_count / UART_BURST_SIZE; + + /* + * Each transaction transfers data in DWORDs. If there are less than + * four remaining valid_byte_count to transfer or if the circular + * buffer has insufficient space for a DWORD, the data is transferred + * one byte at a time. + */ + while (valid_burst_count) { + if (*data_empty_count - UART_BURST_SIZE < 0) + break; + if (xmit->tail > (UART_XMIT_SIZE - UART_BURST_SIZE)) + break; + writel(*(unsigned int *)&xmit->buf[xmit->tail], + port->membase + UART_TX_BURST_FIFO); + *valid_byte_count -= UART_BURST_SIZE; + *data_empty_count -= UART_BURST_SIZE; + valid_burst_count -= UART_BYTE_SIZE; + + xmit->tail = (xmit->tail + UART_BURST_SIZE) & + (UART_XMIT_SIZE - 1); + } + + while (*valid_byte_count) { + if (*data_empty_count - UART_BYTE_SIZE < 0) + break; + writeb(xmit->buf[xmit->tail], port->membase + + UART_TX_BYTE_FIFO); + *data_empty_count -= UART_BYTE_SIZE; + *valid_byte_count -= UART_BYTE_SIZE; + + /* + * When the tail of the circular buffer is reached, the next + * byte is transferred to the beginning of the buffer. + */ + xmit->tail = (xmit->tail + UART_BYTE_SIZE) & + (UART_XMIT_SIZE - 1); + + /* + * If there are any pending burst count, data is handled by + * transmitting DWORDs at a time. + */ + if (valid_burst_count && (xmit->tail < + (UART_XMIT_SIZE - UART_BURST_SIZE))) + break; + } +} + +static void pci1xxxx_tx_burst(struct uart_port *port, u32 uart_status) +{ + struct uart_8250_port *up = up_to_u8250p(port); + u32 valid_byte_count; + int data_empty_count; + struct circ_buf *xmit; + + xmit = &port->state->xmit; + + if (port->x_char) { + writeb(port->x_char, port->membase + UART_TX); + port->icount.tx++; + port->x_char = 0; + return; + } + + if ((uart_tx_stopped(port)) || (uart_circ_empty(xmit))) { + port->ops->stop_tx(port); + } else { + data_empty_count = (pci1xxxx_read_burst_status(port) & + UART_BST_STAT_TX_COUNT_MASK) >> 8; + do { + valid_byte_count = uart_circ_chars_pending(xmit); + + pci1xxxx_process_write_data(port, xmit, + &data_empty_count, + &valid_byte_count); + + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (data_empty_count && valid_byte_count); + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + /* + * With RPM enabled, we have to wait until the FIFO is empty before + * the HW can go idle. So we get here once again with empty FIFO and + * disable the interrupt and RPM in __stop_tx() + */ + if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM)) + port->ops->stop_tx(port); +} + static int pci1xxxx_handle_irq(struct uart_port *port) { unsigned long flags; @@ -359,6 +495,9 @@ static int pci1xxxx_handle_irq(struct uart_port *port) if (status & UART_BST_STAT_LSR_RX_MASK) pci1xxxx_rx_burst(port, status); + if (status & UART_BST_STAT_LSR_THRE) + pci1xxxx_tx_burst(port, status); + spin_unlock_irqrestore(&port->lock, flags); return 1; @@ -481,6 +620,17 @@ static int pci1xxxx_setup(struct pci_dev *pdev, port->port.flags |= UPF_FIXED_TYPE | UPF_SKIP_TEST; port->port.type = PORT_MCHP16550A; + /* + * 8250 core considers prescaller value to be always 16. + * The MCHP ports support downscaled mode and hence the + * functional UART clock can be lower, i.e. 62.5MHz, than + * software expects in order to support higher baud rates. + * Assign here 64MHz to support 4Mbps. + * + * The value itself is not really used anywhere except baud + * rate calculations, so we can mangle it as we wish. + */ + port->port.uartclk = 64 * HZ_PER_MHZ; port->port.set_termios = serial8250_do_set_termios; port->port.get_divisor = pci1xxxx_get_divisor; port->port.set_divisor = pci1xxxx_set_divisor; @@ -594,7 +744,6 @@ static int pci1xxxx_serial_probe(struct pci_dev *pdev, memset(&uart, 0, sizeof(uart)); uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT; - uart.port.uartclk = UART_CLOCK_DEFAULT; uart.port.dev = dev; if (num_vectors == max_vec_reqd) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index c0c34e410c..fc9dd5d452 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2684,12 +2684,8 @@ static unsigned int serial8250_get_baud_rate(struct uart_port *port, */ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) { - struct uart_8250_port *up = up_to_u8250p(port); struct tty_port *tport = &port->state->port; - unsigned int baud, quot, frac = 0; - struct ktermios *termios; struct tty_struct *tty; - unsigned long flags; tty = tty_port_tty_get(tport); if (!tty) { @@ -2710,21 +2706,7 @@ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) if (!tty_port_initialized(tport)) goto out_unlock; - termios = &tty->termios; - - baud = serial8250_get_baud_rate(port, termios, NULL); - quot = serial8250_get_divisor(port, baud, &frac); - - serial8250_rpm_get(up); - uart_port_lock_irqsave(port, &flags); - - uart_update_timeout(port, termios->c_cflag, baud); - - serial8250_set_divisor(port, baud, quot, frac); - serial_port_out(port, UART_LCR, up->lcr); - - uart_port_unlock_irqrestore(port, flags); - serial8250_rpm_put(up); + serial8250_do_set_termios(port, &tty->termios, NULL); out_unlock: mutex_unlock(&tport->mutex); diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c index 77686da42c..ba96fa913e 100644 --- a/drivers/tty/serial/8250/8250_pxa.c +++ b/drivers/tty/serial/8250/8250_pxa.c @@ -92,11 +92,7 @@ static int serial_pxa_probe(struct platform_device *pdev) struct uart_8250_port uart = {}; struct pxa8250_data *data; struct resource *mmres; - int irq, ret; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + int ret; mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mmres) @@ -114,21 +110,22 @@ static int serial_pxa_probe(struct platform_device *pdev) if (ret) return ret; - ret = of_alias_get_id(pdev->dev.of_node, "serial"); - if (ret >= 0) - uart.port.line = ret; - uart.port.type = PORT_XSCALE; - uart.port.iotype = UPIO_MEM32; uart.port.mapbase = mmres->start; - uart.port.regshift = 2; - uart.port.irq = irq; - uart.port.fifosize = 64; uart.port.flags = UPF_IOREMAP | UPF_SKIP_TEST | UPF_FIXED_TYPE; uart.port.dev = &pdev->dev; uart.port.uartclk = clk_get_rate(data->clk); uart.port.pm = serial_pxa_pm; uart.port.private_data = data; + + ret = uart_read_port_properties(&uart.port); + if (ret) + return ret; + + uart.port.iotype = UPIO_MEM32; + uart.port.regshift = 2; + uart.port.fifosize = 64; + uart.tx_loadsz = 32; uart.dl_write = serial_pxa_dl_write; ret = serial8250_register_8250_port(&uart); diff --git a/drivers/tty/serial/8250/8250_tegra.c b/drivers/tty/serial/8250/8250_tegra.c index ba352262df..60a80d00d2 100644 --- a/drivers/tty/serial/8250/8250_tegra.c +++ b/drivers/tty/serial/8250/8250_tegra.c @@ -57,25 +57,11 @@ static int tegra_uart_probe(struct platform_device *pdev) port = &port8250.port; spin_lock_init(&port->lock); - port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | - UPF_FIXED_TYPE; - port->iotype = UPIO_MEM32; - port->regshift = 2; + port->flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_FIXED_TYPE; port->type = PORT_TEGRA; - port->irqflags |= IRQF_SHARED; port->dev = &pdev->dev; port->handle_break = tegra_uart_handle_break; - ret = of_alias_get_id(pdev->dev.of_node, "serial"); - if (ret >= 0) - port->line = ret; - - ret = platform_get_irq(pdev, 0); - if (ret < 0) - return ret; - - port->irq = ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; @@ -88,12 +74,18 @@ static int tegra_uart_probe(struct platform_device *pdev) port->mapbase = res->start; port->mapsize = resource_size(res); + ret = uart_read_port_properties(port); + if (ret) + return ret; + + port->iotype = UPIO_MEM32; + port->regshift = 2; + uart->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); if (IS_ERR(uart->rst)) return PTR_ERR(uart->rst); - if (device_property_read_u32(&pdev->dev, "clock-frequency", - &port->uartclk)) { + if (!port->uartclk) { uart->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(uart->clk)) { dev_err(&pdev->dev, "failed to get clock!\n"); diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 6399a38ecc..670d2ca0f7 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -162,7 +162,6 @@ static int uniphier_uart_probe(struct platform_device *pdev) struct uniphier8250_priv *priv; struct resource *regs; void __iomem *membase; - int irq; int ret; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -175,23 +174,12 @@ static int uniphier_uart_probe(struct platform_device *pdev) if (!membase) return -ENOMEM; - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; memset(&up, 0, sizeof(up)); - ret = of_alias_get_id(dev->of_node, "serial"); - if (ret < 0) { - dev_err(dev, "failed to get alias id\n"); - return ret; - } - up.port.line = ret; - priv->clk = devm_clk_get(dev, NULL); if (IS_ERR(priv->clk)) { dev_err(dev, "failed to get clock\n"); @@ -211,7 +199,10 @@ static int uniphier_uart_probe(struct platform_device *pdev) up.port.mapbase = regs->start; up.port.mapsize = resource_size(regs); up.port.membase = membase; - up.port.irq = irq; + + ret = uart_read_port_properties(&up.port); + if (ret) + return ret; up.port.type = PORT_16550A; up.port.iotype = UPIO_MEM32; diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 8b9a2c4902..47ff50763c 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -149,6 +149,7 @@ config SERIAL_8250_PCI config SERIAL_8250_EXAR tristate "8250/16550 Exar/Commtech PCI/PCIe device support" depends on SERIAL_8250 && PCI + select SERIAL_8250_PCILIB default SERIAL_8250 help This builds support for XR17C1xx, XR17V3xx and some Commtech diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index cf2c890a56..2fa3fb30dc 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -348,10 +348,7 @@ static int pl011_fifo_to_tty(struct uart_amba_port *uap) flag = TTY_FRAME; } - uart_port_unlock(&uap->port); - sysrq = uart_handle_sysrq_char(&uap->port, ch & 255); - uart_port_lock(&uap->port); - + sysrq = uart_prepare_sysrq_char(&uap->port, ch & 255); if (!sysrq) uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag); } @@ -1017,7 +1014,7 @@ static void pl011_dma_rx_callback(void *data) ret = pl011_dma_rx_trigger_dma(uap); pl011_dma_rx_chars(uap, pending, lastbuf, false); - uart_port_unlock_irq(&uap->port); + uart_unlock_and_check_sysrq(&uap->port); /* * Do this check after we picked the DMA chars so we don't * get some IRQ immediately from RX. @@ -1540,11 +1537,10 @@ static void check_apply_cts_event_workaround(struct uart_amba_port *uap) static irqreturn_t pl011_int(int irq, void *dev_id) { struct uart_amba_port *uap = dev_id; - unsigned long flags; unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; int handled = 0; - uart_port_lock_irqsave(&uap->port, &flags); + uart_port_lock(&uap->port); status = pl011_read(uap, REG_RIS) & uap->im; if (status) { do { @@ -1573,7 +1569,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id) handled = 1; } - uart_port_unlock_irqrestore(&uap->port, flags); + uart_unlock_and_check_sysrq(&uap->port); return IRQ_RETVAL(handled); } @@ -2322,13 +2318,10 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) clk_enable(uap->clk); - local_irq_save(flags); - if (uap->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = uart_port_trylock(&uap->port); + if (oops_in_progress) + locked = uart_port_trylock_irqsave(&uap->port, &flags); else - uart_port_lock(&uap->port); + uart_port_lock_irqsave(&uap->port, &flags); /* * First save the CR then disable the interrupts @@ -2354,8 +2347,7 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) pl011_write(old_cr, uap, REG_CR); if (locked) - uart_port_unlock(&uap->port); - local_irq_restore(flags); + uart_port_unlock_irqrestore(&uap->port, flags); clk_disable(uap->clk); } diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index 8d09ace062..7790cbc573 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -378,7 +378,7 @@ static void ar933x_uart_rx_chars(struct ar933x_uart_port *up) up->port.icount.rx++; ch = rdata & AR933X_UART_DATA_TX_RX_MASK; - if (uart_handle_sysrq_char(&up->port, ch)) + if (uart_prepare_sysrq_char(&up->port, ch)) continue; if ((up->port.ignore_status_mask & AR933X_DUMMY_STATUS_RD) == 0) @@ -468,7 +468,7 @@ static irqreturn_t ar933x_uart_interrupt(int irq, void *dev_id) ar933x_uart_tx_chars(up); } - uart_port_unlock(&up->port); + uart_unlock_and_check_sysrq(&up->port); return IRQ_HANDLED; } @@ -627,14 +627,10 @@ static void ar933x_uart_console_write(struct console *co, const char *s, unsigned int int_en; int locked = 1; - local_irq_save(flags); - - if (up->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = uart_port_trylock(&up->port); + if (oops_in_progress) + locked = uart_port_trylock_irqsave(&up->port, &flags); else - uart_port_lock(&up->port); + uart_port_lock_irqsave(&up->port, &flags); /* * First save the IER then disable the interrupts @@ -654,9 +650,7 @@ static void ar933x_uart_console_write(struct console *co, const char *s, ar933x_uart_write(up, AR933X_UART_INT_REG, AR933X_UART_INT_ALLINTS); if (locked) - uart_port_unlock(&up->port); - - local_irq_restore(flags); + uart_port_unlock_irqrestore(&up->port, flags); } static int ar933x_uart_console_setup(struct console *co, char *options) diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c index a3cefa1534..34801a6f30 100644 --- a/drivers/tty/serial/bcm63xx_uart.c +++ b/drivers/tty/serial/bcm63xx_uart.c @@ -285,10 +285,9 @@ static void bcm_uart_do_rx(struct uart_port *port) flag = TTY_PARITY; } - if (uart_handle_sysrq_char(port, c)) + if (uart_prepare_sysrq_char(port, c)) continue; - if ((cstat & port->ignore_status_mask) == 0) tty_insert_flip_char(tty_port, c, flag); @@ -353,7 +352,7 @@ static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id) estat & UART_EXTINP_DCD_MASK); } - uart_port_unlock(port); + uart_unlock_and_check_sysrq(port); return IRQ_HANDLED; } @@ -703,20 +702,14 @@ static void bcm_console_write(struct console *co, const char *s, { struct uart_port *port; unsigned long flags; - int locked; + int locked = 1; port = &ports[co->index]; - local_irq_save(flags); - if (port->sysrq) { - /* bcm_uart_interrupt() already took the lock */ - locked = 0; - } else if (oops_in_progress) { - locked = uart_port_trylock(port); - } else { - uart_port_lock(port); - locked = 1; - } + if (oops_in_progress) + locked = uart_port_trylock_irqsave(port, &flags); + else + uart_port_lock_irqsave(port, &flags); /* call helper to deal with \r\n */ uart_console_write(port, s, count, bcm_console_putchar); @@ -725,8 +718,7 @@ static void bcm_console_write(struct console *co, const char *s, wait_for_xmitr(port); if (locked) - uart_port_unlock(port); - local_irq_restore(flags); + uart_port_unlock_irqrestore(port, flags); } /* diff --git a/drivers/tty/serial/fsl_linflexuart.c b/drivers/tty/serial/fsl_linflexuart.c index 52c87876a8..5426322b5f 100644 --- a/drivers/tty/serial/fsl_linflexuart.c +++ b/drivers/tty/serial/fsl_linflexuart.c @@ -837,7 +837,6 @@ static int linflex_probe(struct platform_device *pdev) return ret; sport->dev = &pdev->dev; - sport->type = PORT_LINFLEXUART; sport->iotype = UPIO_MEM; sport->irq = ret; sport->ops = &linflex_pops; diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index e148132506..09c1678ddf 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -26,6 +26,7 @@ #include <linux/slab.h> #include <linux/of.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/dma-mapping.h> #include <asm/irq.h> @@ -2010,7 +2011,7 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count) struct imx_port *sport = imx_uart_ports[co->index]; struct imx_port_ucrs old_ucr; unsigned long flags; - unsigned int ucr1; + unsigned int ucr1, usr2; int locked = 1; if (sport->port.sysrq) @@ -2041,8 +2042,8 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count) * Finally, wait for transmitter to become empty * and restore UCR1/2/3 */ - while (!(imx_uart_readl(sport, USR2) & USR2_TXDC)); - + read_poll_timeout_atomic(imx_uart_readl, usr2, usr2 & USR2_TXDC, + 0, USEC_PER_SEC, false, sport, USR2); imx_uart_ucrs_restore(sport, &old_ucr); if (locked) diff --git a/drivers/tty/serial/jsm/jsm_cls.c b/drivers/tty/serial/jsm/jsm_cls.c index 1eda48964c..ddbd42c096 100644 --- a/drivers/tty/serial/jsm/jsm_cls.c +++ b/drivers/tty/serial/jsm/jsm_cls.c @@ -395,7 +395,6 @@ static void cls_copy_data_from_uart_to_queue(struct jsm_channel *ch) * which in this case is the break signal. */ if (linestatus & error_mask) { - linestatus = 0; readb(&ch->ch_cls_uart->txrx); continue; } diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index ec20329f06..3e4ac46de1 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -136,20 +136,16 @@ static void lpc32xx_hsuart_console_write(struct console *co, const char *s, int locked = 1; touch_nmi_watchdog(); - local_irq_save(flags); - if (up->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = uart_port_trylock(&up->port); + if (oops_in_progress) + locked = uart_port_trylock_irqsave(&up->port, &flags); else - uart_port_lock(&up->port); + uart_port_lock_irqsave(&up->port, &flags); uart_console_write(&up->port, s, count, lpc32xx_hsuart_console_putchar); wait_for_xmit_empty(&up->port); if (locked) - uart_port_unlock(&up->port); - local_irq_restore(flags); + uart_port_unlock_irqrestore(&up->port, flags); } static int __init lpc32xx_hsuart_console_setup(struct console *co, @@ -233,8 +229,6 @@ static unsigned int __serial_get_clock_div(unsigned long uartclk, hsu_rate++; } - if (hsu_rate > 0xFF) - hsu_rate = 0xFF; return goodrate; } @@ -268,7 +262,8 @@ static void __serial_lpc32xx_rx(struct uart_port *port) tty_insert_flip_char(tport, 0, TTY_FRAME); } - tty_insert_flip_char(tport, (tmp & 0xFF), flag); + if (!uart_prepare_sysrq_char(port, tmp & 0xff)) + tty_insert_flip_char(tport, (tmp & 0xFF), flag); tmp = readl(LPC32XX_HSUART_FIFO(port->membase)); } @@ -333,7 +328,7 @@ static irqreturn_t serial_lpc32xx_interrupt(int irq, void *dev_id) __serial_lpc32xx_tx(port); } - uart_port_unlock(port); + uart_unlock_and_check_sysrq(port); return IRQ_HANDLED; } diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c index 5efb2b593b..3d2b83d6ab 100644 --- a/drivers/tty/serial/max3100.c +++ b/drivers/tty/serial/max3100.c @@ -45,6 +45,9 @@ #include <linux/freezer.h> #include <linux/tty.h> #include <linux/tty_flip.h> +#include <linux/types.h> + +#include <asm/unaligned.h> #include <linux/serial_max3100.h> @@ -191,7 +194,7 @@ static void max3100_timeout(struct timer_list *t) static int max3100_sr(struct max3100_port *s, u16 tx, u16 *rx) { struct spi_message message; - u16 etx, erx; + __be16 etx, erx; int status; struct spi_transfer tran = { .tx_buf = &etx, @@ -213,7 +216,7 @@ static int max3100_sr(struct max3100_port *s, u16 tx, u16 *rx) return 0; } -static int max3100_handlerx(struct max3100_port *s, u16 rx) +static int max3100_handlerx_unlocked(struct max3100_port *s, u16 rx) { unsigned int status = 0; int ret = 0, cts; @@ -254,6 +257,17 @@ static int max3100_handlerx(struct max3100_port *s, u16 rx) return ret; } +static int max3100_handlerx(struct max3100_port *s, u16 rx) +{ + unsigned long flags; + int ret; + + uart_port_lock_irqsave(&s->port, &flags); + ret = max3100_handlerx_unlocked(s, rx); + uart_port_unlock_irqrestore(&s->port, flags); + return ret; +} + static void max3100_work(struct work_struct *w) { struct max3100_port *s = container_of(w, struct max3100_port, work); @@ -738,13 +752,14 @@ static int max3100_probe(struct spi_device *spi) mutex_lock(&max3100s_lock); if (!uart_driver_registered) { - uart_driver_registered = 1; retval = uart_register_driver(&max3100_uart_driver); if (retval) { printk(KERN_ERR "Couldn't register max3100 uart driver\n"); mutex_unlock(&max3100s_lock); return retval; } + + uart_driver_registered = 1; } for (i = 0; i < MAX_MAX3100; i++) @@ -830,6 +845,7 @@ static void max3100_remove(struct spi_device *spi) } pr_debug("removing max3100 driver\n"); uart_unregister_driver(&max3100_uart_driver); + uart_driver_registered = 0; mutex_unlock(&max3100s_lock); } diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 198413df09..14dd9cfaa9 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -30,6 +30,7 @@ #define MAX310X_MAJOR 204 #define MAX310X_MINOR 209 #define MAX310X_UART_NRMAX 16 +#define MAX310X_MAX_PORTS 4 /* Maximum number of UART ports per IC. */ /* MAX310X register definitions */ #define MAX310X_RHR_REG (0x00) /* RX FIFO */ @@ -66,6 +67,7 @@ #define MAX310X_BRGDIVMSB_REG (0x1d) /* Baud rate divisor MSB */ #define MAX310X_CLKSRC_REG (0x1e) /* Clock source */ #define MAX310X_REG_1F (0x1f) +#define MAX310X_EXTREG_START (0x20) /* Only relevant in SPI mode. */ #define MAX310X_REVID_REG MAX310X_REG_1F /* Revision ID */ @@ -73,9 +75,9 @@ #define MAX310X_GLOBALCMD_REG MAX310X_REG_1F /* Global Command (WO) */ /* Extended registers */ -#define MAX310X_SPI_REVID_EXTREG MAX310X_REG_05 /* Revision ID */ -#define MAX310X_I2C_REVID_EXTREG (0x25) /* Revision ID */ - +#define MAX310X_REVID_EXTREG (0x25) /* Revision ID + * (extended addressing space) + */ /* IRQ register bits */ #define MAX310X_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ #define MAX310X_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */ @@ -160,14 +162,14 @@ #define MAX310X_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */ /* Flow control trigger level register masks */ -#define MAX310X_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */ -#define MAX310X_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */ +#define MAX310X_FLOWLVL_HALT_MASK GENMASK(3, 0) /* Flow control halt level */ +#define MAX310X_FLOWLVL_RES_MASK GENMASK(7, 4) /* Flow control resume level */ #define MAX310X_FLOWLVL_HALT(words) ((words / 8) & 0x0f) #define MAX310X_FLOWLVL_RES(words) (((words / 8) & 0x0f) << 4) /* FIFO interrupt trigger level register masks */ -#define MAX310X_FIFOTRIGLVL_TX_MASK (0x0f) /* TX FIFO trigger level */ -#define MAX310X_FIFOTRIGLVL_RX_MASK (0xf0) /* RX FIFO trigger level */ +#define MAX310X_FIFOTRIGLVL_TX_MASK GENMASK(3, 0) /* TX FIFO trigger level */ +#define MAX310X_FIFOTRIGLVL_RX_MASK GENMASK(7, 4) /* RX FIFO trigger level */ #define MAX310X_FIFOTRIGLVL_TX(words) ((words / 8) & 0x0f) #define MAX310X_FIFOTRIGLVL_RX(words) (((words / 8) & 0x0f) << 4) @@ -177,7 +179,8 @@ #define MAX310X_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs * are used in conjunction with * XOFF2 for definition of - * special character */ + * special character + */ #define MAX310X_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */ #define MAX310X_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */ #define MAX310X_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1 @@ -214,8 +217,8 @@ */ /* PLL configuration register masks */ -#define MAX310X_PLLCFG_PREDIV_MASK (0x3f) /* PLL predivision value */ -#define MAX310X_PLLCFG_PLLFACTOR_MASK (0xc0) /* PLL multiplication factor */ +#define MAX310X_PLLCFG_PREDIV_MASK GENMASK(5, 0) /* PLL predivision value */ +#define MAX310X_PLLCFG_PLLFACTOR_MASK GENMASK(7, 6) /* PLL multiplication factor */ /* Baud rate generator configuration register bits */ #define MAX310X_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */ @@ -234,7 +237,7 @@ /* Misc definitions */ #define MAX310X_FIFO_SIZE (128) -#define MAX310x_REV_MASK (0xf8) +#define MAX310x_REV_MASK GENMASK(7, 3) #define MAX310X_WRITE_BIT 0x80 /* Port startup definitions */ @@ -257,20 +260,21 @@ struct max310x_if_cfg { int (*extended_reg_enable)(struct device *dev, bool enable); - - unsigned int rev_id_reg; + u8 rev_id_offset; }; struct max310x_devtype { struct { unsigned short min; unsigned short max; - } slave_addr; - char name[9]; + } slave_addr; /* Relevant only in I2C mode. */ int nr; + char name[9]; u8 mode1; - int (*detect)(struct device *); - void (*power)(struct uart_port *, int); + u8 rev_id_val; + u8 rev_id_reg; /* Relevant only if rev_id_val is defined. */ + u8 power_reg; /* Register address for power/sleep control. */ + u8 power_bit; /* Bit for sleep or power-off mode (active high). */ }; struct max310x_one { @@ -331,62 +335,52 @@ static void max310x_port_update(struct uart_port *port, u8 reg, u8 mask, u8 val) regmap_update_bits(one->regmap, reg, mask, val); } -static int max3107_detect(struct device *dev) -{ - struct max310x_port *s = dev_get_drvdata(dev); - unsigned int val = 0; - int ret; - - ret = regmap_read(s->regmap, MAX310X_REVID_REG, &val); - if (ret) - return ret; - - if (((val & MAX310x_REV_MASK) != MAX3107_REV_ID)) { - dev_err(dev, - "%s ID 0x%02x does not match\n", s->devtype->name, val); - return -ENODEV; - } - - return 0; -} - -static int max3108_detect(struct device *dev) +static int max310x_detect(struct device *dev) { struct max310x_port *s = dev_get_drvdata(dev); unsigned int val = 0; int ret; - /* MAX3108 have not REV ID register, we just check default value - * from clocksource register to make sure everything works. - */ - ret = regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val); - if (ret) - return ret; + /* Check if variant supports REV ID register: */ + if (s->devtype->rev_id_val) { + u8 rev_id_reg = s->devtype->rev_id_reg; - if (val != (MAX310X_CLKSRC_EXTCLK_BIT | MAX310X_CLKSRC_PLLBYP_BIT)) { - dev_err(dev, "%s not present\n", s->devtype->name); - return -ENODEV; - } + /* Check if REV ID is in extended addressing space: */ + if (s->devtype->rev_id_reg >= MAX310X_EXTREG_START) { + ret = s->if_cfg->extended_reg_enable(dev, true); + if (ret) + return ret; - return 0; -} + /* Adjust REV ID extended addressing space address: */ + if (s->if_cfg->rev_id_offset) + rev_id_reg -= s->if_cfg->rev_id_offset; + } -static int max3109_detect(struct device *dev) -{ - struct max310x_port *s = dev_get_drvdata(dev); - unsigned int val = 0; - int ret; + regmap_read(s->regmap, rev_id_reg, &val); - ret = s->if_cfg->extended_reg_enable(dev, true); - if (ret) - return ret; + if (s->devtype->rev_id_reg >= MAX310X_EXTREG_START) { + ret = s->if_cfg->extended_reg_enable(dev, false); + if (ret) + return ret; + } - regmap_read(s->regmap, s->if_cfg->rev_id_reg, &val); - s->if_cfg->extended_reg_enable(dev, false); - if (((val & MAX310x_REV_MASK) != MAX3109_REV_ID)) { - dev_err(dev, - "%s ID 0x%02x does not match\n", s->devtype->name, val); - return -ENODEV; + if (((val & MAX310x_REV_MASK) != s->devtype->rev_id_val)) + return dev_err_probe(dev, -ENODEV, + "%s ID 0x%02x does not match\n", + s->devtype->name, val); + } else { + /* + * For variant without REV ID register, just check default value + * from clocksource register to make sure everything works. + */ + ret = regmap_read(s->regmap, MAX310X_CLKSRC_REG, &val); + if (ret) + return ret; + + if (val != (MAX310X_CLKSRC_EXTCLK_BIT | MAX310X_CLKSRC_PLLBYP_BIT)) + return dev_err_probe(dev, -ENODEV, + "%s not present\n", + s->devtype->name); } return 0; @@ -394,39 +388,10 @@ static int max3109_detect(struct device *dev) static void max310x_power(struct uart_port *port, int on) { - max310x_port_update(port, MAX310X_MODE1_REG, - MAX310X_MODE1_FORCESLEEP_BIT, - on ? 0 : MAX310X_MODE1_FORCESLEEP_BIT); - if (on) - msleep(50); -} - -static int max14830_detect(struct device *dev) -{ - struct max310x_port *s = dev_get_drvdata(dev); - unsigned int val = 0; - int ret; - - ret = s->if_cfg->extended_reg_enable(dev, true); - if (ret) - return ret; - - regmap_read(s->regmap, s->if_cfg->rev_id_reg, &val); - s->if_cfg->extended_reg_enable(dev, false); - if (((val & MAX310x_REV_MASK) != MAX14830_REV_ID)) { - dev_err(dev, - "%s ID 0x%02x does not match\n", s->devtype->name, val); - return -ENODEV; - } - - return 0; -} + struct max310x_port *s = dev_get_drvdata(port->dev); -static void max14830_power(struct uart_port *port, int on) -{ - max310x_port_update(port, MAX310X_BRGCFG_REG, - MAX14830_BRGCFG_CLKDIS_BIT, - on ? 0 : MAX14830_BRGCFG_CLKDIS_BIT); + max310x_port_update(port, s->devtype->power_reg, s->devtype->power_bit, + on ? 0 : s->devtype->power_bit); if (on) msleep(50); } @@ -435,8 +400,10 @@ static const struct max310x_devtype max3107_devtype = { .name = "MAX3107", .nr = 1, .mode1 = MAX310X_MODE1_AUTOSLEEP_BIT | MAX310X_MODE1_IRQSEL_BIT, - .detect = max3107_detect, - .power = max310x_power, + .rev_id_val = MAX3107_REV_ID, + .rev_id_reg = MAX310X_REVID_REG, + .power_reg = MAX310X_MODE1_REG, + .power_bit = MAX310X_MODE1_FORCESLEEP_BIT, .slave_addr = { .min = 0x2c, .max = 0x2f, @@ -447,8 +414,10 @@ static const struct max310x_devtype max3108_devtype = { .name = "MAX3108", .nr = 1, .mode1 = MAX310X_MODE1_AUTOSLEEP_BIT, - .detect = max3108_detect, - .power = max310x_power, + .rev_id_val = 0, /* Unsupported. */ + .rev_id_reg = 0, /* Irrelevant when rev_id_val is not defined. */ + .power_reg = MAX310X_MODE1_REG, + .power_bit = MAX310X_MODE1_FORCESLEEP_BIT, .slave_addr = { .min = 0x60, .max = 0x6f, @@ -459,8 +428,10 @@ static const struct max310x_devtype max3109_devtype = { .name = "MAX3109", .nr = 2, .mode1 = MAX310X_MODE1_AUTOSLEEP_BIT, - .detect = max3109_detect, - .power = max310x_power, + .rev_id_val = MAX3109_REV_ID, + .rev_id_reg = MAX310X_REVID_EXTREG, + .power_reg = MAX310X_MODE1_REG, + .power_bit = MAX310X_MODE1_FORCESLEEP_BIT, .slave_addr = { .min = 0x60, .max = 0x6f, @@ -471,8 +442,10 @@ static const struct max310x_devtype max14830_devtype = { .name = "MAX14830", .nr = 4, .mode1 = MAX310X_MODE1_IRQSEL_BIT, - .detect = max14830_detect, - .power = max14830_power, + .rev_id_val = MAX14830_REV_ID, + .rev_id_reg = MAX310X_REVID_EXTREG, + .power_reg = MAX310X_BRGCFG_REG, + .power_bit = MAX14830_BRGCFG_CLKDIS_BIT, .slave_addr = { .min = 0x60, .max = 0x6f, @@ -490,10 +463,8 @@ static bool max310x_reg_writeable(struct device *dev, unsigned int reg) case MAX310X_RXFIFOLVL_REG: return false; default: - break; + return true; } - - return true; } static bool max310x_reg_volatile(struct device *dev, unsigned int reg) @@ -512,10 +483,8 @@ static bool max310x_reg_volatile(struct device *dev, unsigned int reg) case MAX310X_REG_1F: return true; default: - break; + return false; } - - return false; } static bool max310x_reg_precious(struct device *dev, unsigned int reg) @@ -527,10 +496,8 @@ static bool max310x_reg_precious(struct device *dev, unsigned int reg) case MAX310X_STS_IRQSTS_REG: return true; default: - break; + return false; } - - return false; } static bool max310x_reg_noinc(struct device *dev, unsigned int reg) @@ -689,7 +656,8 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) u8 ch, flag; if (port->read_status_mask == MAX310X_LSR_RXOVR_BIT) { - /* We are just reading, happily ignoring any error conditions. + /* + * We are just reading, happily ignoring any error conditions. * Break condition, parity checking, framing errors -- they * are all ignored. That means that we can do a batch-read. * @@ -698,7 +666,7 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen) * that the LSR register applies to the "current" character. * That's also the reason why we cannot do batched reads when * asked to check the individual statuses. - * */ + */ sts = max310x_port_read(port, MAX310X_LSR_IRQSTS_REG); max310x_batch_read(port, one->rx_buf, rxlen); @@ -802,8 +770,10 @@ static void max310x_handle_tx(struct uart_port *port) to_send = (to_send > txlen) ? txlen : to_send; if (until_end < to_send) { - /* It's a circ buffer -- wrap around. - * We could do that in one SPI transaction, but meh. */ + /* + * It's a circ buffer -- wrap around. + * We could do that in one SPI transaction, but meh. + */ max310x_batch_write(port, xmit->buf + xmit->tail, until_end); max310x_batch_write(port, xmit->buf, to_send - until_end); } else { @@ -848,6 +818,7 @@ static irqreturn_t max310x_port_irq(struct max310x_port *s, int portno) if (ists & MAX310X_IRQ_TXEMPTY_BIT) max310x_start_tx(port); } while (1); + return res; } @@ -892,7 +863,8 @@ static unsigned int max310x_tx_empty(struct uart_port *port) static unsigned int max310x_get_mctrl(struct uart_port *port) { - /* DCD and DSR are not wired and CTS/RTS is handled automatically + /* + * DCD and DSR are not wired and CTS/RTS is handled automatically * so just indicate DSR and CAR asserted */ return TIOCM_DSR | TIOCM_CAR; @@ -984,7 +956,8 @@ static void max310x_set_termios(struct uart_port *port, max310x_port_write(port, MAX310X_XON1_REG, termios->c_cc[VSTART]); max310x_port_write(port, MAX310X_XOFF1_REG, termios->c_cc[VSTOP]); - /* Disable transmitter before enabling AutoCTS or auto transmitter + /* + * Disable transmitter before enabling AutoCTS or auto transmitter * flow control */ if (termios->c_cflag & CRTSCTS || termios->c_iflag & IXOFF) { @@ -1011,7 +984,8 @@ static void max310x_set_termios(struct uart_port *port, } max310x_port_write(port, MAX310X_FLOWCTRL_REG, flow); - /* Enable transmitter after disabling AutoCTS and auto transmitter + /* + * Enable transmitter after disabling AutoCTS and auto transmitter * flow control */ if (!(termios->c_cflag & CRTSCTS) && !(termios->c_iflag & IXOFF)) { @@ -1072,10 +1046,9 @@ static int max310x_rs485_config(struct uart_port *port, struct ktermios *termios static int max310x_startup(struct uart_port *port) { - struct max310x_port *s = dev_get_drvdata(port->dev); unsigned int val; - s->devtype->power(port, 1); + max310x_power(port, 1); /* Configure MODE1 register */ max310x_port_update(port, MAX310X_MODE1_REG, @@ -1103,8 +1076,11 @@ static int max310x_startup(struct uart_port *port) MAX310X_MODE2_ECHOSUPR_BIT); } - /* Configure flow control levels */ - /* Flow control halt level 96, resume level 48 */ + /* + * Configure flow control levels: + * resume: 48 + * halt: 96 + */ max310x_port_write(port, MAX310X_FLOWLVL_REG, MAX310X_FLOWLVL_RES(48) | MAX310X_FLOWLVL_HALT(96)); @@ -1120,12 +1096,10 @@ static int max310x_startup(struct uart_port *port) static void max310x_shutdown(struct uart_port *port) { - struct max310x_port *s = dev_get_drvdata(port->dev); - /* Disable all interrupts */ max310x_port_write(port, MAX310X_IRQEN_REG, 0); - s->devtype->power(port, 0); + max310x_power(port, 0); } static const char *max310x_type(struct uart_port *port) @@ -1187,7 +1161,7 @@ static int __maybe_unused max310x_suspend(struct device *dev) for (i = 0; i < s->devtype->nr; i++) { uart_suspend_port(&max310x_uart, &s->p[i].port); - s->devtype->power(&s->p[i].port, 0); + max310x_power(&s->p[i].port, 0); } return 0; @@ -1199,7 +1173,7 @@ static int __maybe_unused max310x_resume(struct device *dev) int i; for (i = 0; i < s->devtype->nr; i++) { - s->devtype->power(&s->p[i].port, 1); + max310x_power(&s->p[i].port, 1); uart_resume_port(&max310x_uart, &s->p[i].port); } @@ -1209,7 +1183,7 @@ static int __maybe_unused max310x_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(max310x_pm_ops, max310x_suspend, max310x_resume); #ifdef CONFIG_GPIOLIB -static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset) +static int max310x_gpio_get(struct gpio_chip *chip, unsigned int offset) { unsigned int val; struct max310x_port *s = gpiochip_get_data(chip); @@ -1220,7 +1194,7 @@ static int max310x_gpio_get(struct gpio_chip *chip, unsigned offset) return !!((val >> 4) & (1 << (offset % 4))); } -static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static void max310x_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct max310x_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[offset / 4].port; @@ -1229,7 +1203,7 @@ static void max310x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) value ? 1 << (offset % 4) : 0); } -static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct max310x_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[offset / 4].port; @@ -1240,7 +1214,7 @@ static int max310x_gpio_direction_input(struct gpio_chip *chip, unsigned offset) } static int max310x_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) + unsigned int offset, int value) { struct max310x_port *s = gpiochip_get_data(chip); struct uart_port *port = &s->p[offset / 4].port; @@ -1296,10 +1270,9 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty /* Alloc port structure */ s = devm_kzalloc(dev, struct_size(s, p, devtype->nr), GFP_KERNEL); - if (!s) { - dev_err(dev, "Error allocating port structure\n"); - return -ENOMEM; - } + if (!s) + return dev_err_probe(dev, -ENOMEM, + "Error allocating port structure\n"); /* Always ask for fixed clock rate from a property. */ device_property_read_u32(dev, "clock-frequency", &uartclk); @@ -1320,8 +1293,7 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty if (freq == 0) freq = uartclk; if (freq == 0) { - dev_err(dev, "Cannot get clock rate\n"); - ret = -EINVAL; + ret = dev_err_probe(dev, -EINVAL, "Cannot get clock rate\n"); goto out_clk; } @@ -1345,7 +1317,7 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty dev_set_drvdata(dev, s); /* Check device to ensure we are talking to what we expect */ - ret = devtype->detect(dev); + ret = max310x_detect(dev); if (ret) goto out_clk; @@ -1427,14 +1399,13 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty /* Register port */ ret = uart_add_one_port(&max310x_uart, &s->p[i].port); - if (ret) { - s->p[i].port.dev = NULL; + if (ret) goto out_uart; - } + set_bit(line, max310x_lines); /* Go to suspend mode */ - devtype->power(&s->p[i].port, 0); + max310x_power(&s->p[i].port, 0); } #ifdef CONFIG_GPIOLIB @@ -1465,10 +1436,8 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty out_uart: for (i = 0; i < devtype->nr; i++) { - if (s->p[i].port.dev) { + if (test_and_clear_bit(s->p[i].port.line, max310x_lines)) uart_remove_one_port(&max310x_uart, &s->p[i].port); - clear_bit(s->p[i].port.line, max310x_lines); - } } out_clk: @@ -1486,9 +1455,11 @@ static void max310x_remove(struct device *dev) cancel_work_sync(&s->p[i].tx_work); cancel_work_sync(&s->p[i].md_work); cancel_work_sync(&s->p[i].rs_work); - uart_remove_one_port(&max310x_uart, &s->p[i].port); - clear_bit(s->p[i].port.line, max310x_lines); - s->devtype->power(&s->p[i].port, 0); + + if (test_and_clear_bit(s->p[i].port.line, max310x_lines)) + uart_remove_one_port(&max310x_uart, &s->p[i].port); + + max310x_power(&s->p[i].port, 0); } clk_disable_unprepare(s->clk); @@ -1518,6 +1489,19 @@ static struct regmap_config regcfg = { .max_raw_write = MAX310X_FIFO_SIZE, }; +static const char *max310x_regmap_name(u8 port_id) +{ + switch (port_id) { + case 0: return "port0"; + case 1: return "port1"; + case 2: return "port2"; + case 3: return "port3"; + default: + WARN_ON(true); + return NULL; + } +} + #ifdef CONFIG_SPI_MASTER static int max310x_spi_extended_reg_enable(struct device *dev, bool enable) { @@ -1529,13 +1513,13 @@ static int max310x_spi_extended_reg_enable(struct device *dev, bool enable) static const struct max310x_if_cfg __maybe_unused max310x_spi_if_cfg = { .extended_reg_enable = max310x_spi_extended_reg_enable, - .rev_id_reg = MAX310X_SPI_REVID_EXTREG, + .rev_id_offset = MAX310X_EXTREG_START, }; static int max310x_spi_probe(struct spi_device *spi) { const struct max310x_devtype *devtype; - struct regmap *regmaps[4]; + struct regmap *regmaps[MAX310X_MAX_PORTS]; unsigned int i; int ret; @@ -1547,12 +1531,14 @@ static int max310x_spi_probe(struct spi_device *spi) if (ret) return ret; - devtype = device_get_match_data(&spi->dev); + devtype = spi_get_device_match_data(spi); if (!devtype) - devtype = (struct max310x_devtype *)spi_get_device_id(spi)->driver_data; + return dev_err_probe(&spi->dev, -ENODEV, "Failed to match device\n"); for (i = 0; i < devtype->nr; i++) { u8 port_mask = i * 0x20; + + regcfg.name = max310x_regmap_name(i); regcfg.read_flag_mask = port_mask; regcfg.write_flag_mask = port_mask | MAX310X_WRITE_BIT; regmaps[i] = devm_regmap_init_spi(spi, ®cfg); @@ -1600,7 +1586,7 @@ static struct regmap_config regcfg_i2c = { .writeable_reg = max310x_reg_writeable, .volatile_reg = max310x_reg_volatile, .precious_reg = max310x_reg_precious, - .max_register = MAX310X_I2C_REVID_EXTREG, + .max_register = MAX310X_REVID_EXTREG, .writeable_noinc_reg = max310x_reg_noinc, .readable_noinc_reg = max310x_reg_noinc, .max_raw_read = MAX310X_FIFO_SIZE, @@ -1609,7 +1595,7 @@ static struct regmap_config regcfg_i2c = { static const struct max310x_if_cfg max310x_i2c_if_cfg = { .extended_reg_enable = max310x_i2c_extended_reg_enable, - .rev_id_reg = MAX310X_I2C_REVID_EXTREG, + .rev_id_offset = 0, /* No offset in I2C mode. */ }; static unsigned short max310x_i2c_slave_addr(unsigned short addr, @@ -1619,10 +1605,10 @@ static unsigned short max310x_i2c_slave_addr(unsigned short addr, * For MAX14830 and MAX3109, the slave address depends on what the * A0 and A1 pins are tied to. * See Table I2C Address Map of the datasheet. - * Based on that table, the following formulas were determined. - * UART1 - UART0 = 0x10 - * UART2 - UART1 = 0x20 + 0x10 - * UART3 - UART2 = 0x10 + * Based on that table, the following formulas were determined: + * UART1 - UART0 = 0x10 + * UART2 - UART1 = 0x20 + 0x10 + * UART3 - UART2 = 0x10 */ addr -= nr * 0x10; @@ -1637,21 +1623,22 @@ static int max310x_i2c_probe(struct i2c_client *client) { const struct max310x_devtype *devtype; struct i2c_client *port_client; - struct regmap *regmaps[4]; + struct regmap *regmaps[MAX310X_MAX_PORTS]; unsigned int i; u8 port_addr; - devtype = device_get_match_data(&client->dev); + devtype = i2c_get_match_data(client); if (!devtype) return dev_err_probe(&client->dev, -ENODEV, "Failed to match device\n"); if (client->addr < devtype->slave_addr.min || - client->addr > devtype->slave_addr.max) + client->addr > devtype->slave_addr.max) return dev_err_probe(&client->dev, -EINVAL, "Slave addr 0x%x outside of range [0x%x, 0x%x]\n", client->addr, devtype->slave_addr.min, devtype->slave_addr.max); + regcfg_i2c.name = max310x_regmap_name(0); regmaps[0] = devm_regmap_init_i2c(client, ®cfg_i2c); for (i = 1; i < devtype->nr; i++) { @@ -1660,6 +1647,7 @@ static int max310x_i2c_probe(struct i2c_client *client) client->adapter, port_addr); + regcfg_i2c.name = max310x_regmap_name(i); regmaps[i] = devm_regmap_init_i2c(port_client, ®cfg_i2c); } @@ -1672,6 +1660,15 @@ static void max310x_i2c_remove(struct i2c_client *client) max310x_remove(&client->dev); } +static const struct i2c_device_id max310x_i2c_id_table[] = { + { "max3107", (kernel_ulong_t)&max3107_devtype, }, + { "max3108", (kernel_ulong_t)&max3108_devtype, }, + { "max3109", (kernel_ulong_t)&max3109_devtype, }, + { "max14830", (kernel_ulong_t)&max14830_devtype, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max310x_i2c_id_table); + static struct i2c_driver max310x_i2c_driver = { .driver = { .name = MAX310X_NAME, @@ -1680,6 +1677,7 @@ static struct i2c_driver max310x_i2c_driver = { }, .probe = max310x_i2c_probe, .remove = max310x_i2c_remove, + .id_table = max310x_i2c_id_table, }; #endif diff --git a/drivers/tty/serial/mcf.c b/drivers/tty/serial/mcf.c index 8690a45239..b0604d6da0 100644 --- a/drivers/tty/serial/mcf.c +++ b/drivers/tty/serial/mcf.c @@ -470,33 +470,6 @@ static struct mcf_uart mcf_ports[4]; #if defined(CONFIG_SERIAL_MCF_CONSOLE) /****************************************************************************/ -int __init early_mcf_setup(struct mcf_platform_uart *platp) -{ - struct uart_port *port; - int i; - - for (i = 0; ((i < MCF_MAXPORTS) && (platp[i].mapbase)); i++) { - port = &mcf_ports[i].port; - - port->line = i; - port->type = PORT_MCF; - port->mapbase = platp[i].mapbase; - port->membase = (platp[i].membase) ? platp[i].membase : - (unsigned char __iomem *) port->mapbase; - port->iotype = SERIAL_IO_MEM; - port->irq = platp[i].irq; - port->uartclk = MCF_BUSCLK; - port->flags = UPF_BOOT_AUTOCONF; - port->rs485_config = mcf_config_rs485; - port->rs485_supported = mcf_rs485_supported; - port->ops = &mcf_uart_ops; - } - - return 0; -} - -/****************************************************************************/ - static void mcf_console_putc(struct console *co, const char c) { struct uart_port *port = &(mcf_ports + co->index)->port; diff --git a/drivers/tty/serial/meson_uart.c b/drivers/tty/serial/meson_uart.c index 8395688f5e..6feac459c0 100644 --- a/drivers/tty/serial/meson_uart.c +++ b/drivers/tty/serial/meson_uart.c @@ -220,7 +220,7 @@ static void meson_receive_chars(struct uart_port *port) continue; } - if (uart_handle_sysrq_char(port, ch)) + if (uart_prepare_sysrq_char(port, ch)) continue; if ((status & port->ignore_status_mask) == 0) @@ -248,7 +248,7 @@ static irqreturn_t meson_uart_interrupt(int irq, void *dev_id) meson_uart_start_tx(port); } - uart_port_unlock(port); + uart_unlock_and_check_sysrq(port); return IRQ_HANDLED; } @@ -556,18 +556,13 @@ static void meson_serial_port_write(struct uart_port *port, const char *s, u_int count) { unsigned long flags; - int locked; + int locked = 1; u32 val, tmp; - local_irq_save(flags); - if (port->sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = uart_port_trylock(port); - } else { - uart_port_lock(port); - locked = 1; - } + if (oops_in_progress) + locked = uart_port_trylock_irqsave(port, &flags); + else + uart_port_lock_irqsave(port, &flags); val = readl(port->membase + AML_UART_CONTROL); tmp = val & ~(AML_UART_TX_INT_EN | AML_UART_RX_INT_EN); @@ -577,8 +572,7 @@ static void meson_serial_port_write(struct uart_port *port, const char *s, writel(val, port->membase + AML_UART_CONTROL); if (locked) - uart_port_unlock(port); - local_irq_restore(flags); + uart_port_unlock_irqrestore(port, flags); } static void meson_serial_console_write(struct console *co, const char *s, diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index e24204ad35..d27c4c8c84 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -588,16 +588,14 @@ static void msm_complete_rx_dma(void *args) if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK)) flag = TTY_NORMAL; - uart_port_unlock_irqrestore(port, flags); - sysrq = uart_handle_sysrq_char(port, dma->virt[i]); - uart_port_lock_irqsave(port, &flags); + sysrq = uart_prepare_sysrq_char(port, dma->virt[i]); if (!sysrq) tty_insert_flip_char(tport, dma->virt[i], flag); } msm_start_rx_dma(msm_port); done: - uart_port_unlock_irqrestore(port, flags); + uart_unlock_and_check_sysrq_irqrestore(port, flags); if (count) tty_flip_buffer_push(tport); @@ -763,9 +761,7 @@ static void msm_handle_rx_dm(struct uart_port *port, unsigned int misr) if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK)) flag = TTY_NORMAL; - uart_port_unlock(port); - sysrq = uart_handle_sysrq_char(port, buf[i]); - uart_port_lock(port); + sysrq = uart_prepare_sysrq_char(port, buf[i]); if (!sysrq) tty_insert_flip_char(tport, buf[i], flag); } @@ -825,9 +821,7 @@ static void msm_handle_rx(struct uart_port *port) else if (sr & MSM_UART_SR_PAR_FRAME_ERR) flag = TTY_FRAME; - uart_port_unlock(port); - sysrq = uart_handle_sysrq_char(port, c); - uart_port_lock(port); + sysrq = uart_prepare_sysrq_char(port, c); if (!sysrq) tty_insert_flip_char(tport, c, flag); } @@ -948,11 +942,10 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id) struct uart_port *port = dev_id; struct msm_port *msm_port = to_msm_port(port); struct msm_dma *dma = &msm_port->rx_dma; - unsigned long flags; unsigned int misr; u32 val; - uart_port_lock_irqsave(port, &flags); + uart_port_lock(port); misr = msm_read(port, MSM_UART_MISR); msm_write(port, 0, MSM_UART_IMR); /* disable interrupt */ @@ -984,7 +977,7 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id) msm_handle_delta_cts(port); msm_write(port, msm_port->imr, MSM_UART_IMR); /* restore interrupt */ - uart_port_unlock_irqrestore(port, flags); + uart_unlock_and_check_sysrq(port); return IRQ_HANDLED; } @@ -1621,14 +1614,10 @@ static void __msm_console_write(struct uart_port *port, const char *s, num_newlines++; count += num_newlines; - local_irq_save(flags); - - if (port->sysrq) - locked = 0; - else if (oops_in_progress) - locked = uart_port_trylock(port); + if (oops_in_progress) + locked = uart_port_trylock_irqsave(port, &flags); else - uart_port_lock(port); + uart_port_lock_irqsave(port, &flags); if (is_uartdm) msm_reset_dm_count(port, count); @@ -1667,9 +1656,7 @@ static void __msm_console_write(struct uart_port *port, const char *s, } if (locked) - uart_port_unlock(port); - - local_irq_restore(flags); + uart_port_unlock_irqrestore(port, flags); } static void msm_console_write(struct console *co, const char *s, diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index f5a0b401af..9be1c871cf 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -508,7 +508,7 @@ static void serial_omap_rdi(struct uart_omap_port *up, unsigned int lsr) up->port.icount.rx++; - if (uart_handle_sysrq_char(&up->port, ch)) + if (uart_prepare_sysrq_char(&up->port, ch)) return; uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, TTY_NORMAL); @@ -563,7 +563,7 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id) } } while (max_count--); - uart_port_unlock(&up->port); + uart_unlock_and_check_sysrq(&up->port); tty_flip_buffer_push(&up->port.state->port); @@ -1212,13 +1212,10 @@ serial_omap_console_write(struct console *co, const char *s, unsigned int ier; int locked = 1; - local_irq_save(flags); - if (up->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = uart_port_trylock(&up->port); + if (oops_in_progress) + locked = uart_port_trylock_irqsave(&up->port, &flags); else - uart_port_lock(&up->port); + uart_port_lock_irqsave(&up->port, &flags); /* * First save the IER then disable the interrupts @@ -1245,8 +1242,7 @@ serial_omap_console_write(struct console *co, const char *s, check_modem_status(up); if (locked) - uart_port_unlock(&up->port); - local_irq_restore(flags); + uart_port_unlock_irqrestore(&up->port, flags); } static int __init diff --git a/drivers/tty/serial/owl-uart.c b/drivers/tty/serial/owl-uart.c index d9fe853977..8b60ac0ad7 100644 --- a/drivers/tty/serial/owl-uart.c +++ b/drivers/tty/serial/owl-uart.c @@ -199,6 +199,7 @@ static void owl_uart_receive_chars(struct uart_port *port) stat = owl_uart_read(port, OWL_UART_STAT); while (!(stat & OWL_UART_STAT_RFEM)) { char flag = TTY_NORMAL; + bool sysrq; if (stat & OWL_UART_STAT_RXER) port->icount.overrun++; @@ -217,7 +218,9 @@ static void owl_uart_receive_chars(struct uart_port *port) val = owl_uart_read(port, OWL_UART_RXDAT); val &= 0xff; - if ((stat & port->ignore_status_mask) == 0) + sysrq = uart_prepare_sysrq_char(port, val); + + if (!sysrq && (stat & port->ignore_status_mask) == 0) tty_insert_flip_char(&port->state->port, val, flag); stat = owl_uart_read(port, OWL_UART_STAT); @@ -229,10 +232,9 @@ static void owl_uart_receive_chars(struct uart_port *port) static irqreturn_t owl_uart_irq(int irq, void *dev_id) { struct uart_port *port = dev_id; - unsigned long flags; u32 stat; - uart_port_lock_irqsave(port, &flags); + uart_port_lock(port); stat = owl_uart_read(port, OWL_UART_STAT); @@ -246,7 +248,7 @@ static irqreturn_t owl_uart_irq(int irq, void *dev_id) stat |= OWL_UART_STAT_RIP | OWL_UART_STAT_TIP; owl_uart_write(port, stat, OWL_UART_STAT); - uart_port_unlock_irqrestore(port, flags); + uart_unlock_and_check_sysrq(port); return IRQ_HANDLED; } @@ -508,18 +510,12 @@ static void owl_uart_port_write(struct uart_port *port, const char *s, { u32 old_ctl, val; unsigned long flags; - int locked; + int locked = 1; - local_irq_save(flags); - - if (port->sysrq) - locked = 0; - else if (oops_in_progress) - locked = uart_port_trylock(port); - else { - uart_port_lock(port); - locked = 1; - } + if (oops_in_progress) + locked = uart_port_trylock_irqsave(port, &flags); + else + uart_port_lock_irqsave(port, &flags); old_ctl = owl_uart_read(port, OWL_UART_CTL); val = old_ctl | OWL_UART_CTL_TRFS_TX; @@ -541,9 +537,7 @@ static void owl_uart_port_write(struct uart_port *port, const char *s, owl_uart_write(port, old_ctl, OWL_UART_CTL); if (locked) - uart_port_unlock(port); - - local_irq_restore(flags); + uart_port_unlock_irqrestore(port, flags); } static void owl_uart_console_write(struct console *co, const char *s, diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index 436cc6d52a..89257cddf5 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -237,9 +237,6 @@ struct eg20t_port { #define IRQ_NAME_SIZE 17 char irq_name[IRQ_NAME_SIZE]; - - /* protect the eg20t_port private structure and io access to membase */ - spinlock_t lock; }; /** @@ -567,7 +564,7 @@ static int pch_uart_hal_read(struct eg20t_port *priv, unsigned char *buf, if (uart_handle_break(port)) continue; } - if (uart_handle_sysrq_char(port, rbr)) + if (uart_prepare_sysrq_char(port, rbr)) continue; buf[i++] = rbr; @@ -599,16 +596,14 @@ static void pch_uart_hal_set_break(struct eg20t_port *priv, int on) iowrite8(lcr, priv->membase + UART_LCR); } -static int push_rx(struct eg20t_port *priv, const unsigned char *buf, - int size) +static void push_rx(struct eg20t_port *priv, const unsigned char *buf, + int size) { struct uart_port *port = &priv->port; struct tty_port *tport = &port->state->port; tty_insert_flip_string(tport, buf, size); tty_flip_buffer_push(tport); - - return 0; } static int dma_push_rx(struct eg20t_port *priv, int size) @@ -761,7 +756,7 @@ static int handle_rx_to(struct eg20t_port *priv) { struct pch_uart_buffer *buf; int rx_size; - int ret; + if (!priv->start_rx) { pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT | PCH_UART_HAL_RX_ERR_INT); @@ -770,19 +765,12 @@ static int handle_rx_to(struct eg20t_port *priv) buf = &priv->rxbuf; do { rx_size = pch_uart_hal_read(priv, buf->buf, buf->size); - ret = push_rx(priv, buf->buf, rx_size); - if (ret) - return 0; + push_rx(priv, buf->buf, rx_size); } while (rx_size == buf->size); return PCH_UART_HANDLED_RX_INT; } -static int handle_rx(struct eg20t_port *priv) -{ - return handle_rx_to(priv); -} - static int dma_handle_rx(struct eg20t_port *priv) { struct uart_port *port = &priv->port; @@ -1019,11 +1007,10 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) u8 lsr; int ret = 0; unsigned char iid; - unsigned long flags; int next = 1; u8 msr; - spin_lock_irqsave(&priv->lock, flags); + uart_port_lock(&priv->port); handled = 0; while (next) { iid = pch_uart_hal_get_iid(priv); @@ -1051,7 +1038,7 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) PCH_UART_HAL_RX_INT | PCH_UART_HAL_RX_ERR_INT); } else { - ret = handle_rx(priv); + ret = handle_rx_to(priv); } break; case PCH_UART_IID_RDR_TO: /* Received Data Ready @@ -1083,7 +1070,7 @@ static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) handled |= (unsigned int)ret; } - spin_unlock_irqrestore(&priv->lock, flags); + uart_unlock_and_check_sysrq(&priv->port); return IRQ_RETVAL(handled); } @@ -1194,9 +1181,9 @@ static void pch_uart_break_ctl(struct uart_port *port, int ctl) unsigned long flags; priv = container_of(port, struct eg20t_port, port); - spin_lock_irqsave(&priv->lock, flags); + uart_port_lock_irqsave(&priv->port, &flags); pch_uart_hal_set_break(priv, ctl); - spin_unlock_irqrestore(&priv->lock, flags); + uart_port_unlock_irqrestore(&priv->port, flags); } /* Grab any interrupt resources and initialise any low level driver state. */ @@ -1346,8 +1333,7 @@ static void pch_uart_set_termios(struct uart_port *port, baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); - spin_lock_irqsave(&priv->lock, flags); - uart_port_lock(port); + uart_port_lock_irqsave(port, &flags); uart_update_timeout(port, termios->c_cflag, baud); rtn = pch_uart_hal_set_line(priv, baud, parity, bits, stb); @@ -1360,8 +1346,7 @@ static void pch_uart_set_termios(struct uart_port *port, tty_termios_encode_baud_rate(termios, baud, baud); out: - uart_port_unlock(port); - spin_unlock_irqrestore(&priv->lock, flags); + uart_port_unlock_irqrestore(port, flags); } static const char *pch_uart_type(struct uart_port *port) @@ -1565,27 +1550,17 @@ pch_console_write(struct console *co, const char *s, unsigned int count) { struct eg20t_port *priv; unsigned long flags; - int priv_locked = 1; - int port_locked = 1; + int locked = 1; u8 ier; priv = pch_uart_ports[co->index]; touch_nmi_watchdog(); - local_irq_save(flags); - if (priv->port.sysrq) { - /* call to uart_handle_sysrq_char already took the priv lock */ - priv_locked = 0; - /* serial8250_handle_port() already took the port lock */ - port_locked = 0; - } else if (oops_in_progress) { - priv_locked = spin_trylock(&priv->lock); - port_locked = uart_port_trylock(&priv->port); - } else { - spin_lock(&priv->lock); - uart_port_lock(&priv->port); - } + if (oops_in_progress) + locked = uart_port_trylock_irqsave(&priv->port, &flags); + else + uart_port_lock_irqsave(&priv->port, &flags); /* * First save the IER then disable the interrupts @@ -1603,11 +1578,8 @@ pch_console_write(struct console *co, const char *s, unsigned int count) wait_for_xmitr(priv, UART_LSR_BOTH_EMPTY); iowrite8(ier, priv->membase + UART_IER); - if (port_locked) - uart_port_unlock(&priv->port); - if (priv_locked) - spin_unlock(&priv->lock); - local_irq_restore(flags); + if (locked) + uart_port_unlock_irqrestore(&priv->port, flags); } static int __init pch_console_setup(struct console *co, char *options) @@ -1704,8 +1676,6 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, pci_enable_msi(pdev); pci_set_master(pdev); - spin_lock_init(&priv->lock); - iobase = pci_resource_start(pdev, 0); mapbase = pci_resource_start(pdev, 1); priv->mapbase = mapbase; @@ -1735,8 +1705,6 @@ static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, KBUILD_MODNAME ":" PCH_UART_DRIVER_DEVICE "%d", priv->port.line); - spin_lock_init(&priv->port.lock); - pci_set_drvdata(pdev, priv); priv->trigger_level = 1; priv->fcr = 0; diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 77691fbbf7..92195f984d 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -1493,12 +1493,12 @@ static int pmz_attach(struct macio_dev *mdev, const struct of_device_id *match) * That one should not be called, macio isn't really a hotswap device, * we don't expect one of those serial ports to go away... */ -static int pmz_detach(struct macio_dev *mdev) +static void pmz_detach(struct macio_dev *mdev) { struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); if (!uap) - return -ENODEV; + return; uart_remove_one_port(&pmz_uart_reg, &uap->port); @@ -1509,11 +1509,8 @@ static int pmz_detach(struct macio_dev *mdev) dev_set_drvdata(&mdev->ofdev.dev, NULL); uap->dev = NULL; uap->port.dev = NULL; - - return 0; } - static int pmz_suspend(struct macio_dev *mdev, pm_message_t pm_state) { struct uart_pmac_port *uap = dev_get_drvdata(&mdev->ofdev.dev); @@ -1703,18 +1700,13 @@ static int __init pmz_attach(struct platform_device *pdev) return uart_add_one_port(&pmz_uart_reg, &uap->port); } -static int __exit pmz_detach(struct platform_device *pdev) +static void __exit pmz_detach(struct platform_device *pdev) { struct uart_pmac_port *uap = platform_get_drvdata(pdev); - if (!uap) - return -ENODEV; - uart_remove_one_port(&pmz_uart_reg, &uap->port); uap->port.dev = NULL; - - return 0; } #endif /* !CONFIG_PPC_PMAC */ @@ -1783,7 +1775,7 @@ static struct macio_driver pmz_driver = { #else static struct platform_driver pmz_driver = { - .remove = __exit_p(pmz_detach), + .remove_new = __exit_p(pmz_detach), .driver = { .name = "scc", }, diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index 46e70e155a..e395ff29c1 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -151,7 +151,7 @@ static inline void receive_chars(struct uart_pxa_port *up, int *status) flag = TTY_FRAME; } - if (uart_handle_sysrq_char(&up->port, ch)) + if (uart_prepare_sysrq_char(&up->port, ch)) goto ignore_char; uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag); @@ -232,7 +232,7 @@ static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id) check_modem_status(up); if (lsr & UART_LSR_THRE) transmit_chars(up); - uart_port_unlock(&up->port); + uart_unlock_and_check_sysrq(&up->port); return IRQ_HANDLED; } @@ -604,13 +604,10 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) int locked = 1; clk_enable(up->clk); - local_irq_save(flags); - if (up->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = uart_port_trylock(&up->port); + if (oops_in_progress) + locked = uart_port_trylock_irqsave(&up->port, &flags); else - uart_port_lock(&up->port); + uart_port_lock_irqsave(&up->port, &flags); /* * First save the IER then disable the interrupts @@ -628,10 +625,8 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) serial_out(up, UART_IER, ier); if (locked) - uart_port_unlock(&up->port); - local_irq_restore(flags); + uart_port_unlock_irqrestore(&up->port, flags); clk_disable(up->clk); - } #ifdef CONFIG_CONSOLE_POLL diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 99e08737f2..f9f7ac1a10 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -488,18 +488,16 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, geni_status = readl(uport->membase + SE_GENI_STATUS); - /* Cancel the current write to log the fault */ if (!locked) { - geni_se_cancel_m_cmd(&port->se); - if (!qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_CANCEL_EN, true)) { - geni_se_abort_m_cmd(&port->se); - qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_ABORT_EN, true); - writel(M_CMD_ABORT_EN, uport->membase + - SE_GENI_M_IRQ_CLEAR); - } - writel(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); + /* + * We can only get here if an oops is in progress then we were + * unable to get the lock. This means we can't safely access + * our state variables like tx_remaining. About the best we + * can do is wait for the FIFO to be empty before we start our + * transfer, so we'll do that. + */ + qcom_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_TX_FIFO_NOT_EMPTY_EN, false); } else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->tx_remaining) { /* * It seems we can't interrupt existing transfers if all data @@ -516,11 +514,12 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, __qcom_geni_serial_console_write(uport, s, count); - if (port->tx_remaining) - qcom_geni_serial_setup_tx(uport, port->tx_remaining); - if (locked) + if (locked) { + if (port->tx_remaining) + qcom_geni_serial_setup_tx(uport, port->tx_remaining); uart_port_unlock_irqrestore(uport, flags); + } } static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) diff --git a/drivers/tty/serial/rda-uart.c b/drivers/tty/serial/rda-uart.c index 13deb355cf..82def9b863 100644 --- a/drivers/tty/serial/rda-uart.c +++ b/drivers/tty/serial/rda-uart.c @@ -394,7 +394,8 @@ static void rda_uart_receive_chars(struct uart_port *port) val &= 0xff; port->icount.rx++; - tty_insert_flip_char(&port->state->port, val, flag); + if (!uart_prepare_sysrq_char(port, val)) + tty_insert_flip_char(&port->state->port, val, flag); status = rda_uart_read(port, RDA_UART_STATUS); } @@ -405,10 +406,9 @@ static void rda_uart_receive_chars(struct uart_port *port) static irqreturn_t rda_interrupt(int irq, void *dev_id) { struct uart_port *port = dev_id; - unsigned long flags; u32 val, irq_mask; - uart_port_lock_irqsave(port, &flags); + uart_port_lock(port); /* Clear IRQ cause */ val = rda_uart_read(port, RDA_UART_IRQ_CAUSE); @@ -425,7 +425,7 @@ static irqreturn_t rda_interrupt(int irq, void *dev_id) rda_uart_send_chars(port); } - uart_port_unlock_irqrestore(port, flags); + uart_unlock_and_check_sysrq(port); return IRQ_HANDLED; } @@ -590,18 +590,12 @@ static void rda_uart_port_write(struct uart_port *port, const char *s, { u32 old_irq_mask; unsigned long flags; - int locked; - - local_irq_save(flags); + int locked = 1; - if (port->sysrq) { - locked = 0; - } else if (oops_in_progress) { - locked = uart_port_trylock(port); - } else { - uart_port_lock(port); - locked = 1; - } + if (oops_in_progress) + locked = uart_port_trylock_irqsave(port, &flags); + else + uart_port_lock_irqsave(port, &flags); old_irq_mask = rda_uart_read(port, RDA_UART_IRQ_MASK); rda_uart_write(port, 0, RDA_UART_IRQ_MASK); @@ -615,9 +609,7 @@ static void rda_uart_port_write(struct uart_port *port, const char *s, rda_uart_write(port, old_irq_mask, RDA_UART_IRQ_MASK); if (locked) - uart_port_unlock(port); - - local_irq_restore(flags); + uart_port_unlock_irqrestore(port, flags); } static void rda_uart_console_write(struct console *co, const char *s, diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c index 6fdb32b833..a2d07e05c5 100644 --- a/drivers/tty/serial/samsung_tty.c +++ b/drivers/tty/serial/samsung_tty.c @@ -21,26 +21,28 @@ * BJD, 04-Nov-2004 */ -#include <linux/dmaengine.h> +#include <linux/console.h> +#include <linux/clk.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> #include <linux/dma-mapping.h> -#include <linux/slab.h> +#include <linux/dmaengine.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/ioport.h> #include <linux/math.h> #include <linux/module.h> -#include <linux/ioport.h> -#include <linux/io.h> +#include <linux/of.h> #include <linux/platform_device.h> -#include <linux/init.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/serial_s3c.h> +#include <linux/slab.h> #include <linux/sysrq.h> -#include <linux/console.h> #include <linux/tty.h> #include <linux/tty_flip.h> -#include <linux/serial_core.h> -#include <linux/serial.h> -#include <linux/serial_s3c.h> -#include <linux/delay.h> -#include <linux/clk.h> -#include <linux/cpufreq.h> -#include <linux/of.h> +#include <linux/types.h> + #include <asm/irq.h> /* UART name and device definitions */ @@ -73,21 +75,21 @@ struct s3c24xx_uart_info { enum s3c24xx_port_type type; unsigned int port_type; unsigned int fifosize; - unsigned long rx_fifomask; - unsigned long rx_fifoshift; - unsigned long rx_fifofull; - unsigned long tx_fifomask; - unsigned long tx_fifoshift; - unsigned long tx_fifofull; - unsigned int def_clk_sel; - unsigned long num_clks; - unsigned long clksel_mask; - unsigned long clksel_shift; - unsigned long ucon_mask; + u32 rx_fifomask; + u32 rx_fifoshift; + u32 rx_fifofull; + u32 tx_fifomask; + u32 tx_fifoshift; + u32 tx_fifofull; + u32 clksel_mask; + u32 clksel_shift; + u32 ucon_mask; + u8 def_clk_sel; + u8 num_clks; + u8 iotype; /* uart port features */ - - unsigned int has_divslot:1; + bool has_divslot; }; struct s3c24xx_serial_drv_data { @@ -196,7 +198,7 @@ static void wr_reg(const struct uart_port *port, u32 reg, u32 val) /* Byte-order aware bit setting/clearing functions. */ static inline void s3c24xx_set_bit(const struct uart_port *port, int idx, - unsigned int reg) + u32 reg) { unsigned long flags; u32 val; @@ -209,7 +211,7 @@ static inline void s3c24xx_set_bit(const struct uart_port *port, int idx, } static inline void s3c24xx_clear_bit(const struct uart_port *port, int idx, - unsigned int reg) + u32 reg) { unsigned long flags; u32 val; @@ -233,7 +235,7 @@ static inline const char *s3c24xx_serial_portname(const struct uart_port *port) return to_platform_device(port->dev)->name; } -static int s3c24xx_serial_txempty_nofifo(const struct uart_port *port) +static bool s3c24xx_serial_txempty_nofifo(const struct uart_port *port) { return rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE; } @@ -242,8 +244,8 @@ static void s3c24xx_serial_rx_enable(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); unsigned long flags; - unsigned int ucon, ufcon; int count = 10000; + u32 ucon, ufcon; uart_port_lock_irqsave(port, &flags); @@ -266,7 +268,7 @@ static void s3c24xx_serial_rx_disable(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); unsigned long flags; - unsigned int ucon; + u32 ucon; uart_port_lock_irqsave(port, &flags); @@ -587,8 +589,8 @@ static inline const struct s3c2410_uartcfg return ourport->cfg; } -static int s3c24xx_serial_rx_fifocnt(const struct s3c24xx_uart_port *ourport, - unsigned long ufstat) +static unsigned int +s3c24xx_serial_rx_fifocnt(const struct s3c24xx_uart_port *ourport, u32 ufstat) { const struct s3c24xx_uart_info *info = ourport->info; @@ -660,7 +662,7 @@ static void s3c64xx_start_rx_dma(struct s3c24xx_uart_port *ourport) static void enable_rx_dma(struct s3c24xx_uart_port *ourport) { struct uart_port *port = &ourport->port; - unsigned int ucon; + u32 ucon; /* set Rx mode to DMA mode */ ucon = rd_regl(port, S3C2410_UCON); @@ -683,7 +685,7 @@ static void enable_rx_dma(struct s3c24xx_uart_port *ourport) static void enable_rx_pio(struct s3c24xx_uart_port *ourport) { struct uart_port *port = &ourport->port; - unsigned int ucon; + u32 ucon; /* set Rx mode to DMA mode */ ucon = rd_regl(port, S3C2410_UCON); @@ -708,13 +710,14 @@ static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport); static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) { - unsigned int utrstat, received; struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; struct s3c24xx_uart_dma *dma = ourport->dma; struct tty_struct *tty = tty_port_tty_get(&ourport->port.state->port); struct tty_port *t = &port->state->port; struct dma_tx_state state; + unsigned int received; + u32 utrstat; utrstat = rd_regl(port, S3C2410_UTRSTAT); rd_regl(port, S3C2410_UFSTAT); @@ -756,9 +759,9 @@ finish: static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport) { struct uart_port *port = &ourport->port; - unsigned int ufcon, ufstat, uerstat; + unsigned int max_count = port->fifosize; unsigned int fifocnt = 0; - int max_count = port->fifosize; + u32 ufcon, ufstat, uerstat; u8 ch, flag; while (max_count-- > 0) { @@ -778,7 +781,7 @@ static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport) ch = rd_reg(port, S3C2410_URXH); if (port->flags & UPF_CONS_FLOW) { - int txe = s3c24xx_serial_txempty_nofifo(port); + bool txe = s3c24xx_serial_txempty_nofifo(port); if (ourport->rx_enabled) { if (!txe) { @@ -942,7 +945,7 @@ static irqreturn_t s3c64xx_serial_handle_irq(int irq, void *id) { const struct s3c24xx_uart_port *ourport = id; const struct uart_port *port = &ourport->port; - unsigned int pend = rd_regl(port, S3C64XX_UINTP); + u32 pend = rd_regl(port, S3C64XX_UINTP); irqreturn_t ret = IRQ_HANDLED; if (pend & S3C64XX_UINTM_RXD_MSK) { @@ -961,7 +964,7 @@ static irqreturn_t apple_serial_handle_irq(int irq, void *id) { const struct s3c24xx_uart_port *ourport = id; const struct uart_port *port = &ourport->port; - unsigned int pend = rd_regl(port, S3C2410_UTRSTAT); + u32 pend = rd_regl(port, S3C2410_UTRSTAT); irqreturn_t ret = IRQ_NONE; if (pend & (APPLE_S5L_UTRSTAT_RXTHRESH | APPLE_S5L_UTRSTAT_RXTO)) { @@ -980,11 +983,11 @@ static irqreturn_t apple_serial_handle_irq(int irq, void *id) static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port) { const struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT); - unsigned long ufcon = rd_regl(port, S3C2410_UFCON); + u32 ufstat = rd_regl(port, S3C2410_UFSTAT); + u32 ufcon = rd_regl(port, S3C2410_UFCON); if (ufcon & S3C2410_UFCON_FIFOMODE) { - if ((ufstat & info->tx_fifomask) != 0 || + if ((ufstat & info->tx_fifomask) || (ufstat & info->tx_fifofull)) return 0; return TIOCSER_TEMT; @@ -996,7 +999,7 @@ static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port) /* no modem control lines */ static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port) { - unsigned int umstat = rd_reg(port, S3C2410_UMSTAT); + u32 umstat = rd_reg(port, S3C2410_UMSTAT); if (umstat & S3C2410_UMSTAT_CTS) return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; @@ -1006,8 +1009,8 @@ static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port) static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) { - unsigned int umcon = rd_regl(port, S3C2410_UMCON); - unsigned int ucon = rd_regl(port, S3C2410_UCON); + u32 umcon = rd_regl(port, S3C2410_UMCON); + u32 ucon = rd_regl(port, S3C2410_UCON); if (mctrl & TIOCM_RTS) umcon |= S3C2410_UMCOM_RTS_LOW; @@ -1027,7 +1030,7 @@ static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) { unsigned long flags; - unsigned int ucon; + u32 ucon; uart_port_lock_irqsave(port, &flags); @@ -1185,7 +1188,7 @@ static void apple_s5l_serial_shutdown(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); - unsigned int ucon; + u32 ucon; ucon = rd_regl(port, S3C2410_UCON); ucon &= ~(APPLE_S5L_UCON_TXTHRESH_ENA_MSK | @@ -1211,7 +1214,7 @@ static int s3c64xx_serial_startup(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); unsigned long flags; - unsigned int ufcon; + u32 ufcon; int ret; wr_regl(port, S3C64XX_UINTM, 0xf); @@ -1256,7 +1259,7 @@ static int apple_s5l_serial_startup(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); unsigned long flags; - unsigned int ufcon; + u32 ufcon; int ret; wr_regl(port, S3C2410_UTRSTAT, APPLE_S5L_UTRSTAT_ALL_FLAGS); @@ -1291,8 +1294,6 @@ static int apple_s5l_serial_startup(struct uart_port *port) return ret; } -/* power power management control */ - static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, unsigned int old) { @@ -1338,10 +1339,10 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, #define MAX_CLK_NAME_LENGTH 15 -static inline int s3c24xx_serial_getsource(struct uart_port *port) +static inline u8 s3c24xx_serial_getsource(struct uart_port *port) { const struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - unsigned int ucon; + u32 ucon; if (info->num_clks == 1) return 0; @@ -1351,11 +1352,10 @@ static inline int s3c24xx_serial_getsource(struct uart_port *port) return ucon >> info->clksel_shift; } -static void s3c24xx_serial_setsource(struct uart_port *port, - unsigned int clk_sel) +static void s3c24xx_serial_setsource(struct uart_port *port, u8 clk_sel) { const struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - unsigned int ucon; + u32 ucon; if (info->num_clks == 1) return; @@ -1371,14 +1371,15 @@ static void s3c24xx_serial_setsource(struct uart_port *port, static unsigned int s3c24xx_serial_getclk(struct s3c24xx_uart_port *ourport, unsigned int req_baud, struct clk **best_clk, - unsigned int *clk_num) + u8 *clk_num) { const struct s3c24xx_uart_info *info = ourport->info; struct clk *clk; unsigned long rate; - unsigned int cnt, baud, quot, best_quot = 0; + unsigned int baud, quot, best_quot = 0; char clkname[MAX_CLK_NAME_LENGTH]; int calc_deviation, deviation = (1 << 30) - 1; + u8 cnt; for (cnt = 0; cnt < info->num_clks; cnt++) { /* Keep selected clock if provided */ @@ -1471,10 +1472,10 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, struct s3c24xx_uart_port *ourport = to_ourport(port); struct clk *clk = ERR_PTR(-EINVAL); unsigned long flags; - unsigned int baud, quot, clk_sel = 0; - unsigned int ulcon; - unsigned int umcon; + unsigned int baud, quot; unsigned int udivslot = 0; + u32 ulcon, umcon; + u8 clk_sel = 0; /* * We don't support modem control lines. @@ -1736,12 +1737,12 @@ static struct uart_driver s3c24xx_uart_drv = { static struct s3c24xx_uart_port s3c24xx_serial_ports[UART_NR]; -static void s3c24xx_serial_init_port_default(int index) { +static void s3c24xx_serial_init_port_default(int index) +{ struct uart_port *port = &s3c24xx_serial_ports[index].port; spin_lock_init(&port->lock); - port->iotype = UPIO_MEM; port->uartclk = 0; port->fifosize = 16; port->flags = UPF_BOOT_AUTOCONF; @@ -1757,7 +1758,7 @@ static void s3c24xx_serial_resetport(struct uart_port *port, const struct s3c2410_uartcfg *cfg) { const struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - unsigned long ucon = rd_regl(port, S3C2410_UCON); + u32 ucon = rd_regl(port, S3C2410_UCON); ucon &= (info->clksel_mask | info->ucon_mask); wr_regl(port, S3C2410_UCON, ucon | cfg->ucon); @@ -1775,10 +1776,9 @@ static int s3c24xx_serial_enable_baudclk(struct s3c24xx_uart_port *ourport) struct device *dev = ourport->port.dev; const struct s3c24xx_uart_info *info = ourport->info; char clk_name[MAX_CLK_NAME_LENGTH]; - unsigned int clk_sel; struct clk *clk; - int clk_num; int ret; + u8 clk_sel, clk_num; clk_sel = ourport->cfg->clk_sel ? : info->def_clk_sel; for (clk_num = 0; clk_num < info->num_clks; clk_num++) { @@ -1903,7 +1903,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, wr_regl(port, S3C64XX_UINTSP, 0xf); break; case TYPE_APPLE_S5L: { - unsigned int ucon; + u32 ucon; ucon = rd_regl(port, S3C2410_UCON); ucon &= ~(APPLE_S5L_UCON_TXTHRESH_ENA_MSK | @@ -1951,7 +1951,7 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct s3c24xx_uart_port *ourport; int index = probe_index; - int ret, prop = 0; + int ret, prop = 0, fifosize_prop = 1; if (np) { ret = of_alias_get_id(np, "serial"); @@ -1988,9 +1988,11 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) break; } + ourport->port.iotype = ourport->info->iotype; + if (np) { - of_property_read_u32(np, - "samsung,uart-fifosize", &ourport->port.fifosize); + fifosize_prop = of_property_read_u32(np, "samsung,uart-fifosize", + &ourport->port.fifosize); if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { switch (prop) { @@ -2008,10 +2010,13 @@ static int s3c24xx_serial_probe(struct platform_device *pdev) } } - if (ourport->drv_data->fifosize[index]) - ourport->port.fifosize = ourport->drv_data->fifosize[index]; - else if (ourport->info->fifosize) - ourport->port.fifosize = ourport->info->fifosize; + if (fifosize_prop) { + if (ourport->drv_data->fifosize[index]) + ourport->port.fifosize = ourport->drv_data->fifosize[index]; + else if (ourport->info->fifosize) + ourport->port.fifosize = ourport->info->fifosize; + } + ourport->port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_SAMSUNG_CONSOLE); /* @@ -2057,9 +2062,8 @@ static void s3c24xx_serial_remove(struct platform_device *dev) { struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); - if (port) { + if (port) uart_remove_one_port(&s3c24xx_uart_drv, port); - } uart_unregister_driver(&s3c24xx_uart_drv); } @@ -2105,7 +2109,7 @@ static int s3c24xx_serial_resume_noirq(struct device *dev) /* restore IRQ mask */ switch (ourport->info->type) { case TYPE_S3C6400: { - unsigned int uintm = 0xf; + u32 uintm = 0xf; if (ourport->tx_enabled) uintm &= ~S3C64XX_UINTM_TXD_MSK; @@ -2121,7 +2125,7 @@ static int s3c24xx_serial_resume_noirq(struct device *dev) break; } case TYPE_APPLE_S5L: { - unsigned int ucon; + u32 ucon; int ret; ret = clk_prepare_enable(ourport->clk); @@ -2182,27 +2186,27 @@ static const struct dev_pm_ops s3c24xx_serial_pm_ops = { static struct uart_port *cons_uart; -static int -s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon) +static bool +s3c24xx_serial_console_txrdy(struct uart_port *port, u32 ufcon) { const struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); - unsigned long ufstat, utrstat; + u32 ufstat, utrstat; if (ufcon & S3C2410_UFCON_FIFOMODE) { /* fifo mode - check amount of data in fifo registers... */ ufstat = rd_regl(port, S3C2410_UFSTAT); - return (ufstat & info->tx_fifofull) ? 0 : 1; + return !(ufstat & info->tx_fifofull); } /* in non-fifo mode, we go and use the tx buffer empty */ utrstat = rd_regl(port, S3C2410_UTRSTAT); - return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0; + return utrstat & S3C2410_UTRSTAT_TXE; } static bool -s3c24xx_port_configured(unsigned int ucon) +s3c24xx_port_configured(u32 ucon) { /* consider the serial port configured if the tx/rx mode set */ return (ucon & 0xf) != 0; @@ -2217,7 +2221,7 @@ s3c24xx_port_configured(unsigned int ucon) static int s3c24xx_serial_get_poll_char(struct uart_port *port) { const struct s3c24xx_uart_port *ourport = to_ourport(port); - unsigned int ufstat; + u32 ufstat; ufstat = rd_regl(port, S3C2410_UFSTAT); if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0) @@ -2229,8 +2233,8 @@ static int s3c24xx_serial_get_poll_char(struct uart_port *port) static void s3c24xx_serial_put_poll_char(struct uart_port *port, unsigned char c) { - unsigned int ufcon = rd_regl(port, S3C2410_UFCON); - unsigned int ucon = rd_regl(port, S3C2410_UCON); + u32 ufcon = rd_regl(port, S3C2410_UFCON); + u32 ucon = rd_regl(port, S3C2410_UCON); /* not possible to xmit on unconfigured port */ if (!s3c24xx_port_configured(ucon)) @@ -2246,7 +2250,7 @@ static void s3c24xx_serial_put_poll_char(struct uart_port *port, static void s3c24xx_serial_console_putchar(struct uart_port *port, unsigned char ch) { - unsigned int ufcon = rd_regl(port, S3C2410_UFCON); + u32 ufcon = rd_regl(port, S3C2410_UFCON); while (!s3c24xx_serial_console_txrdy(port, ufcon)) cpu_relax(); @@ -2257,7 +2261,7 @@ static void s3c24xx_serial_console_write(struct console *co, const char *s, unsigned int count) { - unsigned int ucon = rd_regl(cons_uart, S3C2410_UCON); + u32 ucon = rd_regl(cons_uart, S3C2410_UCON); unsigned long flags; bool locked = true; @@ -2284,12 +2288,10 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud, int *parity, int *bits) { struct clk *clk; - unsigned int ulcon; - unsigned int ucon; - unsigned int ubrdiv; unsigned long rate; - unsigned int clk_sel; + u32 ulcon, ucon, ubrdiv; char clk_name[MAX_CLK_NAME_LENGTH]; + u8 clk_sel; ulcon = rd_regl(port, S3C2410_ULCON); ucon = rd_regl(port, S3C2410_UCON); @@ -2398,8 +2400,9 @@ static const struct s3c24xx_serial_drv_data s3c6400_serial_drv_data = { .name = "Samsung S3C6400 UART", .type = TYPE_S3C6400, .port_type = PORT_S3C6400, + .iotype = UPIO_MEM, .fifosize = 64, - .has_divslot = 1, + .has_divslot = true, .rx_fifomask = S3C2440_UFSTAT_RXMASK, .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, .rx_fifofull = S3C2440_UFSTAT_RXFULL, @@ -2427,7 +2430,8 @@ static const struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { .name = "Samsung S5PV210 UART", .type = TYPE_S3C6400, .port_type = PORT_S3C6400, - .has_divslot = 1, + .iotype = UPIO_MEM, + .has_divslot = true, .rx_fifomask = S5PV210_UFSTAT_RXMASK, .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, .rx_fifofull = S5PV210_UFSTAT_RXFULL, @@ -2451,12 +2455,13 @@ static const struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { #endif #if defined(CONFIG_ARCH_EXYNOS) -#define EXYNOS_COMMON_SERIAL_DRV_DATA() \ +#define EXYNOS_COMMON_SERIAL_DRV_DATA \ .info = { \ .name = "Samsung Exynos UART", \ .type = TYPE_S3C6400, \ .port_type = PORT_S3C6400, \ - .has_divslot = 1, \ + .iotype = UPIO_MEM, \ + .has_divslot = true, \ .rx_fifomask = S5PV210_UFSTAT_RXMASK, \ .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, \ .rx_fifofull = S5PV210_UFSTAT_RXFULL, \ @@ -2475,39 +2480,57 @@ static const struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { } \ static const struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = { - EXYNOS_COMMON_SERIAL_DRV_DATA(), + EXYNOS_COMMON_SERIAL_DRV_DATA, .fifosize = { 256, 64, 16, 16 }, }; static const struct s3c24xx_serial_drv_data exynos5433_serial_drv_data = { - EXYNOS_COMMON_SERIAL_DRV_DATA(), + EXYNOS_COMMON_SERIAL_DRV_DATA, .fifosize = { 64, 256, 16, 256 }, }; static const struct s3c24xx_serial_drv_data exynos850_serial_drv_data = { - EXYNOS_COMMON_SERIAL_DRV_DATA(), + EXYNOS_COMMON_SERIAL_DRV_DATA, .fifosize = { 256, 64, 64, 64 }, }; -/* - * Common drv_data struct for platforms that specify samsung,uart-fifosize in - * device tree. - */ -static const struct s3c24xx_serial_drv_data exynos_fifoszdt_serial_drv_data = { - EXYNOS_COMMON_SERIAL_DRV_DATA(), +static const struct s3c24xx_serial_drv_data gs101_serial_drv_data = { + .info = { + .name = "Google GS101 UART", + .type = TYPE_S3C6400, + .port_type = PORT_S3C6400, + .iotype = UPIO_MEM32, + .has_divslot = true, + .rx_fifomask = S5PV210_UFSTAT_RXMASK, + .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, + .rx_fifofull = S5PV210_UFSTAT_RXFULL, + .tx_fifofull = S5PV210_UFSTAT_TXFULL, + .tx_fifomask = S5PV210_UFSTAT_TXMASK, + .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT, + .def_clk_sel = S3C2410_UCON_CLKSEL0, + .num_clks = 1, + .clksel_mask = 0, + .clksel_shift = 0, + }, + .def_cfg = { + .ucon = S5PV210_UCON_DEFAULT, + .ufcon = S5PV210_UFCON_DEFAULT, + .has_fracval = 1, + }, + /* samsung,uart-fifosize must be specified in the device tree. */ .fifosize = { 0 }, }; #define EXYNOS4210_SERIAL_DRV_DATA (&exynos4210_serial_drv_data) #define EXYNOS5433_SERIAL_DRV_DATA (&exynos5433_serial_drv_data) #define EXYNOS850_SERIAL_DRV_DATA (&exynos850_serial_drv_data) -#define EXYNOS_FIFOSZDT_DRV_DATA (&exynos_fifoszdt_serial_drv_data) +#define GS101_SERIAL_DRV_DATA (&gs101_serial_drv_data) #else #define EXYNOS4210_SERIAL_DRV_DATA NULL #define EXYNOS5433_SERIAL_DRV_DATA NULL #define EXYNOS850_SERIAL_DRV_DATA NULL -#define EXYNOS_FIFOSZDT_DRV_DATA NULL +#define GS101_SERIAL_DRV_DATA NULL #endif #ifdef CONFIG_ARCH_APPLE @@ -2516,6 +2539,7 @@ static const struct s3c24xx_serial_drv_data s5l_serial_drv_data = { .name = "Apple S5L UART", .type = TYPE_APPLE_S5L, .port_type = PORT_8250, + .iotype = UPIO_MEM, .fifosize = 16, .rx_fifomask = S3C2410_UFSTAT_RXMASK, .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT, @@ -2545,8 +2569,9 @@ static const struct s3c24xx_serial_drv_data artpec8_serial_drv_data = { .name = "Axis ARTPEC-8 UART", .type = TYPE_S3C6400, .port_type = PORT_S3C6400, + .iotype = UPIO_MEM, .fifosize = 64, - .has_divslot = 1, + .has_divslot = true, .rx_fifomask = S5PV210_UFSTAT_RXMASK, .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, .rx_fifofull = S5PV210_UFSTAT_RXFULL, @@ -2593,7 +2618,7 @@ static const struct platform_device_id s3c24xx_serial_driver_ids[] = { .driver_data = (kernel_ulong_t)ARTPEC8_SERIAL_DRV_DATA, }, { .name = "gs101-uart", - .driver_data = (kernel_ulong_t)EXYNOS_FIFOSZDT_DRV_DATA, + .driver_data = (kernel_ulong_t)GS101_SERIAL_DRV_DATA, }, { }, }; @@ -2616,7 +2641,7 @@ static const struct of_device_id s3c24xx_uart_dt_match[] = { { .compatible = "axis,artpec8-uart", .data = ARTPEC8_SERIAL_DRV_DATA }, { .compatible = "google,gs101-uart", - .data = EXYNOS_FIFOSZDT_DRV_DATA }, + .data = GS101_SERIAL_DRV_DATA }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match); @@ -2715,7 +2740,8 @@ static int samsung_early_read(struct console *con, char *s, unsigned int n) { struct earlycon_device *dev = con->data; const struct samsung_early_console_data *data = dev->port.private_data; - int ch, ufstat, num_read = 0; + int num_read = 0; + u32 ch, ufstat; while (num_read < n) { ufstat = rd_regl(&dev->port, S3C2410_UFSTAT); @@ -2784,6 +2810,17 @@ OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart", OF_EARLYCON_DECLARE(artpec8, "axis,artpec8-uart", s5pv210_early_console_setup); +static int __init gs101_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + /* gs101 always expects MMIO32 register accesses. */ + device->port.iotype = UPIO_MEM32; + + return s5pv210_early_console_setup(device, opt); +} + +OF_EARLYCON_DECLARE(gs101, "google,gs101-uart", gs101_early_console_setup); + /* Apple S5L */ static int __init apple_s5l_early_console_setup(struct earlycon_device *device, const char *opt) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 12915fffac..ace2c4b333 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/property.h> #include <linux/regmap.h> +#include <linux/sched.h> #include <linux/serial_core.h> #include <linux/serial.h> #include <linux/tty.h> @@ -25,7 +26,6 @@ #include <linux/spi/spi.h> #include <linux/uaccess.h> #include <linux/units.h> -#include <uapi/linux/sched/types.h> #define SC16IS7XX_NAME "sc16is7xx" #define SC16IS7XX_MAX_DEVS 8 diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c index 3dfcf20c4e..4df2a4b104 100644 --- a/drivers/tty/serial/serial_base_bus.c +++ b/drivers/tty/serial/serial_base_bus.c @@ -41,7 +41,7 @@ static int serial_base_match(struct device *dev, struct device_driver *drv) return 0; } -static struct bus_type serial_base_bus_type = { +static const struct bus_type serial_base_bus_type = { .name = "serial-base", .match = serial_base_match, }; diff --git a/drivers/tty/serial/serial_port.c b/drivers/tty/serial/serial_port.c index 7d51e66ec8..66fd117d8a 100644 --- a/drivers/tty/serial/serial_port.c +++ b/drivers/tty/serial/serial_port.c @@ -8,7 +8,10 @@ #include <linux/device.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/serial_core.h> #include <linux/spinlock.h> @@ -60,6 +63,13 @@ static int serial_port_runtime_suspend(struct device *dev) if (port->flags & UPF_DEAD) return 0; + /* + * Nothing to do on pm_runtime_force_suspend(), see + * DEFINE_RUNTIME_DEV_PM_OPS. + */ + if (!pm_runtime_enabled(dev)) + return 0; + uart_port_lock_irqsave(port, &flags); if (!port_dev->tx_enabled) { uart_port_unlock_irqrestore(port, flags); @@ -139,6 +149,148 @@ void uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) } EXPORT_SYMBOL(uart_remove_one_port); +/** + * __uart_read_properties - read firmware properties of the given UART port + * @port: corresponding port + * @use_defaults: apply defaults (when %true) or validate the values (when %false) + * + * The following device properties are supported: + * - clock-frequency (optional) + * - fifo-size (optional) + * - no-loopback-test (optional) + * - reg-shift (defaults may apply) + * - reg-offset (value may be validated) + * - reg-io-width (defaults may apply or value may be validated) + * - interrupts (OF only) + * - serial [alias ID] (OF only) + * + * If the port->dev is of struct platform_device type the interrupt line + * will be retrieved via platform_get_irq() call against that device. + * Otherwise it will be assigned by fwnode_irq_get() call. In both cases + * the index 0 of the resource is used. + * + * The caller is responsible to initialize the following fields of the @port + * ->dev (must be valid) + * ->flags + * ->mapbase + * ->mapsize + * ->regshift (if @use_defaults is false) + * before calling this function. Alternatively the above mentioned fields + * may be zeroed, in such case the only ones, that have associated properties + * found, will be set to the respective values. + * + * If no error happened, the ->irq, ->mapbase, ->mapsize will be altered. + * The ->iotype is always altered. + * + * When @use_defaults is true and the respective property is not found + * the following values will be applied: + * ->regshift = 0 + * In this case IRQ must be provided, otherwise an error will be returned. + * + * When @use_defaults is false and the respective property is found + * the following values will be validated: + * - reg-io-width (->iotype) + * - reg-offset (->mapsize against ->mapbase) + * + * Returns: 0 on success or negative errno on failure + */ +static int __uart_read_properties(struct uart_port *port, bool use_defaults) +{ + struct device *dev = port->dev; + u32 value; + int ret; + + /* Read optional UART functional clock frequency */ + device_property_read_u32(dev, "clock-frequency", &port->uartclk); + + /* Read the registers alignment (default: 8-bit) */ + ret = device_property_read_u32(dev, "reg-shift", &value); + if (ret) + port->regshift = use_defaults ? 0 : port->regshift; + else + port->regshift = value; + + /* Read the registers I/O access type (default: MMIO 8-bit) */ + ret = device_property_read_u32(dev, "reg-io-width", &value); + if (ret) { + port->iotype = UPIO_MEM; + } else { + switch (value) { + case 1: + port->iotype = UPIO_MEM; + break; + case 2: + port->iotype = UPIO_MEM16; + break; + case 4: + port->iotype = device_is_big_endian(dev) ? UPIO_MEM32BE : UPIO_MEM32; + break; + default: + if (!use_defaults) { + dev_err(dev, "Unsupported reg-io-width (%u)\n", value); + return -EINVAL; + } + port->iotype = UPIO_UNKNOWN; + break; + } + } + + /* Read the address mapping base offset (default: no offset) */ + ret = device_property_read_u32(dev, "reg-offset", &value); + if (ret) + value = 0; + + /* Check for shifted address mapping overflow */ + if (!use_defaults && port->mapsize < value) { + dev_err(dev, "reg-offset %u exceeds region size %pa\n", value, &port->mapsize); + return -EINVAL; + } + + port->mapbase += value; + port->mapsize -= value; + + /* Read optional FIFO size */ + device_property_read_u32(dev, "fifo-size", &port->fifosize); + + if (device_property_read_bool(dev, "no-loopback-test")) + port->flags |= UPF_SKIP_TEST; + + /* Get index of serial line, if found in DT aliases */ + ret = of_alias_get_id(dev_of_node(dev), "serial"); + if (ret >= 0) + port->line = ret; + + if (dev_is_platform(dev)) + ret = platform_get_irq(to_platform_device(dev), 0); + else + ret = fwnode_irq_get(dev_fwnode(dev), 0); + if (ret == -EPROBE_DEFER) + return ret; + if (ret > 0) + port->irq = ret; + else if (use_defaults) + /* By default IRQ support is mandatory */ + return ret; + else + port->irq = 0; + + port->flags |= UPF_SHARE_IRQ; + + return 0; +} + +int uart_read_port_properties(struct uart_port *port) +{ + return __uart_read_properties(port, true); +} +EXPORT_SYMBOL_GPL(uart_read_port_properties); + +int uart_read_and_validate_port_properties(struct uart_port *port) +{ + return __uart_read_properties(port, false); +} +EXPORT_SYMBOL_GPL(uart_read_and_validate_port_properties); + static struct device_driver serial_port_driver = { .name = "port", .suppress_bind_attrs = true, diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c index e1897894a4..abba397229 100644 --- a/drivers/tty/serial/serial_txx9.c +++ b/drivers/tty/serial/serial_txx9.c @@ -23,9 +23,10 @@ #include <linux/serial.h> #include <linux/tty.h> #include <linux/tty_flip.h> - #include <linux/io.h> +#include <asm/txx9/generic.h> + #define PASS_LIMIT 256 #if !defined(CONFIG_SERIAL_TXX9_STDSERIAL) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index a85e7b9a2e..a6f3517dce 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -576,13 +576,13 @@ static void sci_start_tx(struct uart_port *port) #ifdef CONFIG_SERIAL_SH_SCI_DMA if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - u16 new, scr = serial_port_in(port, SCSCR); + u16 new, scr = sci_serial_in(port, SCSCR); if (s->chan_tx) new = scr | SCSCR_TDRQE; else new = scr & ~SCSCR_TDRQE; if (new != scr) - serial_port_out(port, SCSCR, new); + sci_serial_out(port, SCSCR, new); } if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && @@ -599,7 +599,7 @@ static void sci_start_tx(struct uart_port *port) if (!s->chan_tx || s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE || port->type == PORT_SCIFA || port->type == PORT_SCIFB) { /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = serial_port_in(port, SCSCR); + ctrl = sci_serial_in(port, SCSCR); /* * For SCI, TE (transmit enable) must be set after setting TIE @@ -609,7 +609,7 @@ static void sci_start_tx(struct uart_port *port) if (port->type == PORT_SCI) ctrl |= SCSCR_TE; - serial_port_out(port, SCSCR, ctrl | SCSCR_TIE); + sci_serial_out(port, SCSCR, ctrl | SCSCR_TIE); } } @@ -618,14 +618,14 @@ static void sci_stop_tx(struct uart_port *port) unsigned short ctrl; /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = serial_port_in(port, SCSCR); + ctrl = sci_serial_in(port, SCSCR); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) ctrl &= ~SCSCR_TDRQE; ctrl &= ~SCSCR_TIE; - serial_port_out(port, SCSCR, ctrl); + sci_serial_out(port, SCSCR, ctrl); #ifdef CONFIG_SERIAL_SH_SCI_DMA if (to_sci_port(port)->chan_tx && @@ -640,41 +640,40 @@ static void sci_start_rx(struct uart_port *port) { unsigned short ctrl; - ctrl = serial_port_in(port, SCSCR) | port_rx_irq_mask(port); + ctrl = sci_serial_in(port, SCSCR) | port_rx_irq_mask(port); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) ctrl &= ~SCSCR_RDRQE; - serial_port_out(port, SCSCR, ctrl); + sci_serial_out(port, SCSCR, ctrl); } static void sci_stop_rx(struct uart_port *port) { unsigned short ctrl; - ctrl = serial_port_in(port, SCSCR); + ctrl = sci_serial_in(port, SCSCR); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) ctrl &= ~SCSCR_RDRQE; ctrl &= ~port_rx_irq_mask(port); - serial_port_out(port, SCSCR, ctrl); + sci_serial_out(port, SCSCR, ctrl); } static void sci_clear_SCxSR(struct uart_port *port, unsigned int mask) { if (port->type == PORT_SCI) { /* Just store the mask */ - serial_port_out(port, SCxSR, mask); + sci_serial_out(port, SCxSR, mask); } else if (to_sci_port(port)->params->overrun_mask == SCIFA_ORER) { /* SCIFA/SCIFB and SCIF on SH7705/SH7720/SH7721 */ /* Only clear the status bits we want to clear */ - serial_port_out(port, SCxSR, - serial_port_in(port, SCxSR) & mask); + sci_serial_out(port, SCxSR, sci_serial_in(port, SCxSR) & mask); } else { /* Store the mask, clear parity/framing errors */ - serial_port_out(port, SCxSR, mask & ~(SCIF_FERC | SCIF_PERC)); + sci_serial_out(port, SCxSR, mask & ~(SCIF_FERC | SCIF_PERC)); } } @@ -688,7 +687,7 @@ static int sci_poll_get_char(struct uart_port *port) int c; do { - status = serial_port_in(port, SCxSR); + status = sci_serial_in(port, SCxSR); if (status & SCxSR_ERRORS(port)) { sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); continue; @@ -699,10 +698,10 @@ static int sci_poll_get_char(struct uart_port *port) if (!(status & SCxSR_RDxF(port))) return NO_POLL_CHAR; - c = serial_port_in(port, SCxRDR); + c = sci_serial_in(port, SCxRDR); /* Dummy read */ - serial_port_in(port, SCxSR); + sci_serial_in(port, SCxSR); sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); return c; @@ -714,10 +713,10 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c) unsigned short status; do { - status = serial_port_in(port, SCxSR); + status = sci_serial_in(port, SCxSR); } while (!(status & SCxSR_TDxE(port))); - serial_port_out(port, SCxTDR, c); + sci_serial_out(port, SCxTDR, c); sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); } #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE || @@ -736,8 +735,8 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag) } if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - u16 data = serial_port_in(port, SCPDR); - u16 ctrl = serial_port_in(port, SCPCR); + u16 data = sci_serial_in(port, SCPDR); + u16 ctrl = sci_serial_in(port, SCPCR); /* Enable RXD and TXD pin functions */ ctrl &= ~(SCPCR_RXDC | SCPCR_TXDC); @@ -756,10 +755,10 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag) /* Enable CTS# pin function */ ctrl &= ~SCPCR_CTSC; } - serial_port_out(port, SCPDR, data); - serial_port_out(port, SCPCR, ctrl); + sci_serial_out(port, SCPDR, data); + sci_serial_out(port, SCPCR, ctrl); } else if (sci_getreg(port, SCSPTR)->size) { - u16 status = serial_port_in(port, SCSPTR); + u16 status = sci_serial_in(port, SCSPTR); /* RTS# is always output; and active low, unless autorts */ status |= SCSPTR_RTSIO; @@ -769,7 +768,7 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag) status &= ~SCSPTR_RTSDT; /* CTS# and SCK are inputs */ status &= ~(SCSPTR_CTSIO | SCSPTR_SCKIO); - serial_port_out(port, SCSPTR, status); + sci_serial_out(port, SCSPTR, status); } } @@ -781,13 +780,13 @@ static int sci_txfill(struct uart_port *port) reg = sci_getreg(port, SCTFDR); if (reg->size) - return serial_port_in(port, SCTFDR) & fifo_mask; + return sci_serial_in(port, SCTFDR) & fifo_mask; reg = sci_getreg(port, SCFDR); if (reg->size) - return serial_port_in(port, SCFDR) >> 8; + return sci_serial_in(port, SCFDR) >> 8; - return !(serial_port_in(port, SCxSR) & SCI_TDRE); + return !(sci_serial_in(port, SCxSR) & SCI_TDRE); } static int sci_txroom(struct uart_port *port) @@ -803,13 +802,13 @@ static int sci_rxfill(struct uart_port *port) reg = sci_getreg(port, SCRFDR); if (reg->size) - return serial_port_in(port, SCRFDR) & fifo_mask; + return sci_serial_in(port, SCRFDR) & fifo_mask; reg = sci_getreg(port, SCFDR); if (reg->size) - return serial_port_in(port, SCFDR) & fifo_mask; + return sci_serial_in(port, SCFDR) & fifo_mask; - return (serial_port_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; + return (sci_serial_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; } /* ********************************************************************** * @@ -824,14 +823,14 @@ static void sci_transmit_chars(struct uart_port *port) unsigned short ctrl; int count; - status = serial_port_in(port, SCxSR); + status = sci_serial_in(port, SCxSR); if (!(status & SCxSR_TDxE(port))) { - ctrl = serial_port_in(port, SCSCR); + ctrl = sci_serial_in(port, SCSCR); if (uart_circ_empty(xmit)) ctrl &= ~SCSCR_TIE; else ctrl |= SCSCR_TIE; - serial_port_out(port, SCSCR, ctrl); + sci_serial_out(port, SCSCR, ctrl); return; } @@ -847,15 +846,15 @@ static void sci_transmit_chars(struct uart_port *port) c = xmit->buf[xmit->tail]; xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); } else if (port->type == PORT_SCI && uart_circ_empty(xmit)) { - ctrl = serial_port_in(port, SCSCR); + ctrl = sci_serial_in(port, SCSCR); ctrl &= ~SCSCR_TE; - serial_port_out(port, SCSCR, ctrl); + sci_serial_out(port, SCSCR, ctrl); return; } else { break; } - serial_port_out(port, SCxTDR, c); + sci_serial_out(port, SCxTDR, c); port->icount.tx++; } while (--count > 0); @@ -866,10 +865,10 @@ static void sci_transmit_chars(struct uart_port *port) uart_write_wakeup(port); if (uart_circ_empty(xmit)) { if (port->type == PORT_SCI) { - ctrl = serial_port_in(port, SCSCR); + ctrl = sci_serial_in(port, SCSCR); ctrl &= ~SCSCR_TIE; ctrl |= SCSCR_TEIE; - serial_port_out(port, SCSCR, ctrl); + sci_serial_out(port, SCSCR, ctrl); } sci_stop_tx(port); @@ -883,7 +882,7 @@ static void sci_receive_chars(struct uart_port *port) unsigned short status; unsigned char flag; - status = serial_port_in(port, SCxSR); + status = sci_serial_in(port, SCxSR); if (!(status & SCxSR_RDxF(port))) return; @@ -896,7 +895,7 @@ static void sci_receive_chars(struct uart_port *port) break; if (port->type == PORT_SCI) { - char c = serial_port_in(port, SCxRDR); + char c = sci_serial_in(port, SCxRDR); if (uart_handle_sysrq_char(port, c)) count = 0; else @@ -907,11 +906,11 @@ static void sci_receive_chars(struct uart_port *port) if (port->type == PORT_SCIF || port->type == PORT_HSCIF) { - status = serial_port_in(port, SCxSR); - c = serial_port_in(port, SCxRDR); + status = sci_serial_in(port, SCxSR); + c = sci_serial_in(port, SCxRDR); } else { - c = serial_port_in(port, SCxRDR); - status = serial_port_in(port, SCxSR); + c = sci_serial_in(port, SCxRDR); + status = sci_serial_in(port, SCxSR); } if (uart_handle_sysrq_char(port, c)) { count--; i--; @@ -932,7 +931,7 @@ static void sci_receive_chars(struct uart_port *port) } } - serial_port_in(port, SCxSR); /* dummy read */ + sci_serial_in(port, SCxSR); /* dummy read */ sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); copied += count; @@ -944,8 +943,8 @@ static void sci_receive_chars(struct uart_port *port) tty_flip_buffer_push(tport); } else { /* TTY buffers full; read from RX reg to prevent lockup */ - serial_port_in(port, SCxRDR); - serial_port_in(port, SCxSR); /* dummy read */ + sci_serial_in(port, SCxRDR); + sci_serial_in(port, SCxSR); /* dummy read */ sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); } } @@ -953,7 +952,7 @@ static void sci_receive_chars(struct uart_port *port) static int sci_handle_errors(struct uart_port *port) { int copied = 0; - unsigned short status = serial_port_in(port, SCxSR); + unsigned short status = sci_serial_in(port, SCxSR); struct tty_port *tport = &port->state->port; struct sci_port *s = to_sci_port(port); @@ -1000,10 +999,10 @@ static int sci_handle_fifo_overrun(struct uart_port *port) if (!reg->size) return 0; - status = serial_port_in(port, s->params->overrun_reg); + status = sci_serial_in(port, s->params->overrun_reg); if (status & s->params->overrun_mask) { status &= ~s->params->overrun_mask; - serial_port_out(port, s->params->overrun_reg, status); + sci_serial_out(port, s->params->overrun_reg, status); port->icount.overrun++; @@ -1018,7 +1017,7 @@ static int sci_handle_fifo_overrun(struct uart_port *port) static int sci_handle_breaks(struct uart_port *port) { int copied = 0; - unsigned short status = serial_port_in(port, SCxSR); + unsigned short status = sci_serial_in(port, SCxSR); struct tty_port *tport = &port->state->port; if (uart_handle_break(port)) @@ -1051,7 +1050,7 @@ static int scif_set_rtrg(struct uart_port *port, int rx_trig) /* HSCIF can be set to an arbitrary level. */ if (sci_getreg(port, HSRTRGR)->size) { - serial_port_out(port, HSRTRGR, rx_trig); + sci_serial_out(port, HSRTRGR, rx_trig); return rx_trig; } @@ -1092,9 +1091,9 @@ static int scif_set_rtrg(struct uart_port *port, int rx_trig) return 1; } - serial_port_out(port, SCFCR, - (serial_port_in(port, SCFCR) & - ~(SCFCR_RTRG1 | SCFCR_RTRG0)) | bits); + sci_serial_out(port, SCFCR, + (sci_serial_in(port, SCFCR) & + ~(SCFCR_RTRG1 | SCFCR_RTRG0)) | bits); return rx_trig; } @@ -1102,9 +1101,9 @@ static int scif_set_rtrg(struct uart_port *port, int rx_trig) static int scif_rtrg_enabled(struct uart_port *port) { if (sci_getreg(port, HSRTRGR)->size) - return serial_port_in(port, HSRTRGR) != 0; + return sci_serial_in(port, HSRTRGR) != 0; else - return (serial_port_in(port, SCFCR) & + return (sci_serial_in(port, SCFCR) & (SCFCR_RTRG0 | SCFCR_RTRG1)) != 0; } @@ -1219,8 +1218,8 @@ static void sci_dma_tx_complete(void *arg) s->cookie_tx = -EINVAL; if (port->type == PORT_SCIFA || port->type == PORT_SCIFB || s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) { - u16 ctrl = serial_port_in(port, SCSCR); - serial_port_out(port, SCSCR, ctrl & ~SCSCR_TIE); + u16 ctrl = sci_serial_in(port, SCSCR); + sci_serial_out(port, SCSCR, ctrl & ~SCSCR_TIE); if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) { /* Switch irq from DMA to SCIF */ dmaengine_pause(s->chan_tx_saved); @@ -1272,9 +1271,14 @@ static void sci_dma_rx_chan_invalidate(struct sci_port *s) static void sci_dma_rx_release(struct sci_port *s) { struct dma_chan *chan = s->chan_rx_saved; + struct uart_port *port = &s->port; + unsigned long flags; + uart_port_lock_irqsave(port, &flags); s->chan_rx_saved = NULL; sci_dma_rx_chan_invalidate(s); + uart_port_unlock_irqrestore(port, flags); + dmaengine_terminate_sync(chan); dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0], sg_dma_address(&s->sg_rx[0])); @@ -1296,7 +1300,7 @@ static void sci_dma_rx_reenable_irq(struct sci_port *s) u16 scr; /* Direct new serial port interrupts back to CPU */ - scr = serial_port_in(port, SCSCR); + scr = sci_serial_in(port, SCSCR); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB || s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) { enable_irq(s->irqs[SCIx_RXI_IRQ]); @@ -1305,7 +1309,7 @@ static void sci_dma_rx_reenable_irq(struct sci_port *s) else scr &= ~SCSCR_RDRQE; } - serial_port_out(port, SCSCR, scr | SCSCR_RIE); + sci_serial_out(port, SCSCR, scr | SCSCR_RIE); } static void sci_dma_rx_complete(void *arg) @@ -1714,8 +1718,8 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) #ifdef CONFIG_SERIAL_SH_SCI_DMA if (s->chan_rx) { - u16 scr = serial_port_in(port, SCSCR); - u16 ssr = serial_port_in(port, SCxSR); + u16 scr = sci_serial_in(port, SCSCR); + u16 ssr = sci_serial_in(port, SCxSR); /* Disable future Rx interrupts */ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB || @@ -1733,10 +1737,10 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) scr &= ~SCSCR_RIE; } - serial_port_out(port, SCSCR, scr); + sci_serial_out(port, SCSCR, scr); /* Clear current interrupt */ - serial_port_out(port, SCxSR, - ssr & ~(SCIF_DR | SCxSR_RDxF(port))); + sci_serial_out(port, SCxSR, + ssr & ~(SCIF_DR | SCxSR_RDxF(port))); dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u us\n", jiffies, s->rx_timeout); start_hrtimer_us(&s->rx_timer, s->rx_timeout); @@ -1786,9 +1790,9 @@ static irqreturn_t sci_tx_end_interrupt(int irq, void *ptr) return sci_tx_interrupt(irq, ptr); uart_port_lock_irqsave(port, &flags); - ctrl = serial_port_in(port, SCSCR); + ctrl = sci_serial_in(port, SCSCR); ctrl &= ~(SCSCR_TE | SCSCR_TEIE); - serial_port_out(port, SCSCR, ctrl); + sci_serial_out(port, SCSCR, ctrl); uart_port_unlock_irqrestore(port, flags); return IRQ_HANDLED; @@ -1802,7 +1806,7 @@ static irqreturn_t sci_br_interrupt(int irq, void *ptr) sci_handle_breaks(port); /* drop invalid character received before break was detected */ - serial_port_in(port, SCxRDR); + sci_serial_in(port, SCxRDR); sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port)); @@ -1816,7 +1820,7 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr) if (s->irqs[SCIx_ERI_IRQ] == s->irqs[SCIx_BRI_IRQ]) { /* Break and Error interrupts are muxed */ - unsigned short ssr_status = serial_port_in(port, SCxSR); + unsigned short ssr_status = sci_serial_in(port, SCxSR); /* Break Interrupt */ if (ssr_status & SCxSR_BRK(port)) @@ -1831,7 +1835,7 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr) if (port->type == PORT_SCI) { if (sci_handle_errors(port)) { /* discard character in rx buffer */ - serial_port_in(port, SCxSR); + sci_serial_in(port, SCxSR); sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); } } else { @@ -1856,12 +1860,12 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) struct sci_port *s = to_sci_port(port); irqreturn_t ret = IRQ_NONE; - ssr_status = serial_port_in(port, SCxSR); - scr_status = serial_port_in(port, SCSCR); + ssr_status = sci_serial_in(port, SCxSR); + scr_status = sci_serial_in(port, SCSCR); if (s->params->overrun_reg == SCxSR) orer_status = ssr_status; else if (sci_getreg(port, s->params->overrun_reg)->size) - orer_status = serial_port_in(port, s->params->overrun_reg); + orer_status = sci_serial_in(port, s->params->overrun_reg); err_enabled = scr_status & port_rx_irq_mask(port); @@ -2038,7 +2042,7 @@ static void sci_free_irq(struct sci_port *port) static unsigned int sci_tx_empty(struct uart_port *port) { - unsigned short status = serial_port_in(port, SCxSR); + unsigned short status = sci_serial_in(port, SCxSR); unsigned short in_tx_fifo = sci_txfill(port); return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; @@ -2047,27 +2051,27 @@ static unsigned int sci_tx_empty(struct uart_port *port) static void sci_set_rts(struct uart_port *port, bool state) { if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - u16 data = serial_port_in(port, SCPDR); + u16 data = sci_serial_in(port, SCPDR); /* Active low */ if (state) data &= ~SCPDR_RTSD; else data |= SCPDR_RTSD; - serial_port_out(port, SCPDR, data); + sci_serial_out(port, SCPDR, data); /* RTS# is output */ - serial_port_out(port, SCPCR, - serial_port_in(port, SCPCR) | SCPCR_RTSC); + sci_serial_out(port, SCPCR, + sci_serial_in(port, SCPCR) | SCPCR_RTSC); } else if (sci_getreg(port, SCSPTR)->size) { - u16 ctrl = serial_port_in(port, SCSPTR); + u16 ctrl = sci_serial_in(port, SCSPTR); /* Active low */ if (state) ctrl &= ~SCSPTR_RTSDT; else ctrl |= SCSPTR_RTSDT; - serial_port_out(port, SCSPTR, ctrl); + sci_serial_out(port, SCSPTR, ctrl); } } @@ -2075,10 +2079,10 @@ static bool sci_get_cts(struct uart_port *port) { if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { /* Active low */ - return !(serial_port_in(port, SCPDR) & SCPDR_CTSD); + return !(sci_serial_in(port, SCPDR) & SCPDR_CTSD); } else if (sci_getreg(port, SCSPTR)->size) { /* Active low */ - return !(serial_port_in(port, SCSPTR) & SCSPTR_CTSDT); + return !(sci_serial_in(port, SCSPTR) & SCSPTR_CTSDT); } return true; @@ -2108,9 +2112,8 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) */ reg = sci_getreg(port, SCFCR); if (reg->size) - serial_port_out(port, SCFCR, - serial_port_in(port, SCFCR) | - SCFCR_LOOP); + sci_serial_out(port, SCFCR, + sci_serial_in(port, SCFCR) | SCFCR_LOOP); } mctrl_gpio_set(s->gpios, mctrl); @@ -2120,21 +2123,21 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) if (!(mctrl & TIOCM_RTS)) { /* Disable Auto RTS */ - serial_port_out(port, SCFCR, - serial_port_in(port, SCFCR) & ~SCFCR_MCE); + sci_serial_out(port, SCFCR, + sci_serial_in(port, SCFCR) & ~SCFCR_MCE); /* Clear RTS */ sci_set_rts(port, 0); } else if (s->autorts) { if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { /* Enable RTS# pin function */ - serial_port_out(port, SCPCR, - serial_port_in(port, SCPCR) & ~SCPCR_RTSC); + sci_serial_out(port, SCPCR, + sci_serial_in(port, SCPCR) & ~SCPCR_RTSC); } /* Enable Auto RTS */ - serial_port_out(port, SCFCR, - serial_port_in(port, SCFCR) | SCFCR_MCE); + sci_serial_out(port, SCFCR, + sci_serial_in(port, SCFCR) | SCFCR_MCE); } else { /* Set RTS */ sci_set_rts(port, 1); @@ -2187,8 +2190,8 @@ static void sci_break_ctl(struct uart_port *port, int break_state) } uart_port_lock_irqsave(port, &flags); - scsptr = serial_port_in(port, SCSPTR); - scscr = serial_port_in(port, SCSCR); + scsptr = sci_serial_in(port, SCSPTR); + scscr = sci_serial_in(port, SCSCR); if (break_state == -1) { scsptr = (scsptr | SCSPTR_SPB2IO) & ~SCSPTR_SPB2DT; @@ -2198,8 +2201,8 @@ static void sci_break_ctl(struct uart_port *port, int break_state) scscr |= SCSCR_TE; } - serial_port_out(port, SCSPTR, scsptr); - serial_port_out(port, SCSCR, scscr); + sci_serial_out(port, SCSPTR, scsptr); + sci_serial_out(port, SCSCR, scscr); uart_port_unlock_irqrestore(port, flags); } @@ -2239,9 +2242,9 @@ static void sci_shutdown(struct uart_port *port) * Stop RX and TX, disable related interrupts, keep clock source * and HSCIF TOT bits */ - scr = serial_port_in(port, SCSCR); - serial_port_out(port, SCSCR, scr & - (SCSCR_CKE1 | SCSCR_CKE0 | s->hscif_tot)); + scr = sci_serial_in(port, SCSCR); + sci_serial_out(port, SCSCR, + scr & (SCSCR_CKE1 | SCSCR_CKE0 | s->hscif_tot)); uart_port_unlock_irqrestore(port, flags); #ifdef CONFIG_SERIAL_SH_SCI_DMA @@ -2390,19 +2393,19 @@ static void sci_reset(struct uart_port *port) unsigned int status; struct sci_port *s = to_sci_port(port); - serial_port_out(port, SCSCR, s->hscif_tot); /* TE=0, RE=0, CKE1=0 */ + sci_serial_out(port, SCSCR, s->hscif_tot); /* TE=0, RE=0, CKE1=0 */ reg = sci_getreg(port, SCFCR); if (reg->size) - serial_port_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); + sci_serial_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port) & SCxSR_ERROR_CLEAR(port) & SCxSR_BREAK_CLEAR(port)); if (sci_getreg(port, SCLSR)->size) { - status = serial_port_in(port, SCLSR); + status = sci_serial_in(port, SCLSR); status &= ~(SCLSR_TO | SCLSR_ORER); - serial_port_out(port, SCLSR, status); + sci_serial_out(port, SCLSR, status); } if (s->rx_trigger > 1) { @@ -2540,8 +2543,8 @@ done: * It controls the mux to select (H)SCK or frequency divided clock. */ if (best_clk >= 0 && sci_getreg(port, SCCKS)->size) { - serial_port_out(port, SCDL, dl); - serial_port_out(port, SCCKS, sccks); + sci_serial_out(port, SCDL, dl); + sci_serial_out(port, SCCKS, sccks); } uart_port_lock_irqsave(port, &flags); @@ -2554,7 +2557,7 @@ done: bits = tty_get_frame_size(termios->c_cflag); if (sci_getreg(port, SEMR)->size) - serial_port_out(port, SEMR, 0); + sci_serial_out(port, SEMR, 0); if (best_clk >= 0) { if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) @@ -2569,9 +2572,9 @@ done: case 27: smr_val |= SCSMR_SRC_27; break; } smr_val |= cks; - serial_port_out(port, SCSCR, scr_val | s->hscif_tot); - serial_port_out(port, SCSMR, smr_val); - serial_port_out(port, SCBRR, brr); + sci_serial_out(port, SCSCR, scr_val | s->hscif_tot); + sci_serial_out(port, SCSMR, smr_val); + sci_serial_out(port, SCBRR, brr); if (sci_getreg(port, HSSRR)->size) { unsigned int hssrr = srr | HSCIF_SRE; /* Calculate deviation from intended rate at the @@ -2593,7 +2596,7 @@ done: HSCIF_SRHP_MASK; hssrr |= HSCIF_SRDE; } - serial_port_out(port, HSSRR, hssrr); + sci_serial_out(port, HSSRR, hssrr); } /* Wait one bit interval */ @@ -2601,10 +2604,10 @@ done: } else { /* Don't touch the bit rate configuration */ scr_val = s->cfg->scscr & (SCSCR_CKE1 | SCSCR_CKE0); - smr_val |= serial_port_in(port, SCSMR) & + smr_val |= sci_serial_in(port, SCSMR) & (SCSMR_CKEDG | SCSMR_SRC_MASK | SCSMR_CKS); - serial_port_out(port, SCSCR, scr_val | s->hscif_tot); - serial_port_out(port, SCSMR, smr_val); + sci_serial_out(port, SCSCR, scr_val | s->hscif_tot); + sci_serial_out(port, SCSMR, smr_val); } sci_init_pins(port, termios->c_cflag); @@ -2613,7 +2616,7 @@ done: s->autorts = false; reg = sci_getreg(port, SCFCR); if (reg->size) { - unsigned short ctrl = serial_port_in(port, SCFCR); + unsigned short ctrl = sci_serial_in(port, SCFCR); if ((port->flags & UPF_HARD_FLOW) && (termios->c_cflag & CRTSCTS)) { @@ -2630,7 +2633,7 @@ done: */ ctrl &= ~(SCFCR_RFRST | SCFCR_TFRST); - serial_port_out(port, SCFCR, ctrl); + sci_serial_out(port, SCFCR, ctrl); } if (port->flags & UPF_HARD_FLOW) { /* Refresh (Auto) RTS */ @@ -2645,7 +2648,7 @@ done: if (port->type != PORT_SCI) scr_val |= SCSCR_TE; scr_val |= SCSCR_RE | (s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0)); - serial_port_out(port, SCSCR, scr_val | s->hscif_tot); + sci_serial_out(port, SCSCR, scr_val | s->hscif_tot); if ((srr + 1 == 5) && (port->type == PORT_SCIFA || port->type == PORT_SCIFB)) { /* @@ -3017,9 +3020,6 @@ static int sci_init_single(struct platform_device *dev, port->irq = sci_port->irqs[SCIx_RXI_IRQ]; port->irqflags = 0; - port->serial_in = sci_serial_in; - port->serial_out = sci_serial_out; - return 0; } @@ -3056,21 +3056,21 @@ static void serial_console_write(struct console *co, const char *s, uart_port_lock_irqsave(port, &flags); /* first save SCSCR then disable interrupts, keep clock source */ - ctrl = serial_port_in(port, SCSCR); + ctrl = sci_serial_in(port, SCSCR); ctrl_temp = SCSCR_RE | SCSCR_TE | (sci_port->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0)) | (ctrl & (SCSCR_CKE1 | SCSCR_CKE0)); - serial_port_out(port, SCSCR, ctrl_temp | sci_port->hscif_tot); + sci_serial_out(port, SCSCR, ctrl_temp | sci_port->hscif_tot); uart_console_write(port, s, count, serial_console_putchar); /* wait until fifo is empty and last bit has been transmitted */ bits = SCxSR_TDxE(port) | SCxSR_TEND(port); - while ((serial_port_in(port, SCxSR) & bits) != bits) + while ((sci_serial_in(port, SCxSR) & bits) != bits) cpu_relax(); /* restore the SCSCR */ - serial_port_out(port, SCSCR, ctrl); + sci_serial_out(port, SCSCR, ctrl); if (locked) uart_port_unlock_irqrestore(port, flags); @@ -3503,8 +3503,6 @@ static int __init early_console_setup(struct earlycon_device *device, if (!device->port.membase) return -ENODEV; - device->port.serial_in = sci_serial_in; - device->port.serial_out = sci_serial_out; device->port.type = type; memcpy(&sci_ports[0].port, &device->port, sizeof(struct uart_port)); port_cfg.type = type; diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c index a4cc569a78..0670fd9f84 100644 --- a/drivers/tty/serial/sifive.c +++ b/drivers/tty/serial/sifive.c @@ -412,7 +412,8 @@ static void __ssp_receive_chars(struct sifive_serial_port *ssp) break; ssp->port.icount.rx++; - uart_insert_char(&ssp->port, 0, 0, ch, TTY_NORMAL); + if (!uart_prepare_sysrq_char(&ssp->port, ch)) + uart_insert_char(&ssp->port, 0, 0, ch, TTY_NORMAL); } tty_flip_buffer_push(&ssp->port.state->port); @@ -534,7 +535,7 @@ static irqreturn_t sifive_serial_irq(int irq, void *dev_id) if (ip & SIFIVE_SERIAL_IP_TXWM_MASK) __ssp_transmit_chars(ssp); - uart_port_unlock(&ssp->port); + uart_unlock_and_check_sysrq(&ssp->port); return IRQ_HANDLED; } @@ -791,13 +792,10 @@ static void sifive_serial_console_write(struct console *co, const char *s, if (!ssp) return; - local_irq_save(flags); - if (ssp->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = uart_port_trylock(&ssp->port); + if (oops_in_progress) + locked = uart_port_trylock_irqsave(&ssp->port, &flags); else - uart_port_lock(&ssp->port); + uart_port_lock_irqsave(&ssp->port, &flags); ier = __ssp_readl(ssp, SIFIVE_SERIAL_IE_OFFS); __ssp_writel(0, SIFIVE_SERIAL_IE_OFFS, ssp); @@ -807,8 +805,7 @@ static void sifive_serial_console_write(struct console *co, const char *s, __ssp_writel(ier, SIFIVE_SERIAL_IE_OFFS, ssp); if (locked) - uart_port_unlock(&ssp->port); - local_irq_restore(flags); + uart_port_unlock_irqrestore(&ssp->port, flags); } static int sifive_serial_console_setup(struct console *co, char *options) diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index bbb5595d7e..a23e595518 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -465,6 +465,7 @@ static void asc_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old) { struct asc_port *ascport = to_asc_port(port); + bool manual_rts, toggle_rts = false; struct gpio_desc *gpiod; unsigned int baud; u32 ctrl_val; @@ -518,25 +519,13 @@ static void asc_set_termios(struct uart_port *port, struct ktermios *termios, /* If flow-control selected, stop handling RTS manually */ if (ascport->rts) { - devm_gpiod_put(port->dev, ascport->rts); - ascport->rts = NULL; - - pinctrl_select_state(ascport->pinctrl, - ascport->states[DEFAULT]); + toggle_rts = true; + manual_rts = false; } } else { /* If flow-control disabled, it's safe to handle RTS manually */ - if (!ascport->rts && ascport->states[NO_HW_FLOWCTRL]) { - pinctrl_select_state(ascport->pinctrl, - ascport->states[NO_HW_FLOWCTRL]); - - gpiod = devm_gpiod_get(port->dev, "rts", GPIOD_OUT_LOW); - if (!IS_ERR(gpiod)) { - gpiod_set_consumer_name(gpiod, - port->dev->of_node->name); - ascport->rts = gpiod; - } - } + if (!ascport->rts && ascport->states[NO_HW_FLOWCTRL]) + manual_rts = toggle_rts = true; } if ((baud < 19200) && !ascport->force_m1) { @@ -595,6 +584,25 @@ static void asc_set_termios(struct uart_port *port, struct ktermios *termios, asc_out(port, ASC_CTL, (ctrl_val | ASC_CTL_RUN)); uart_port_unlock_irqrestore(port, flags); + + if (toggle_rts) { + if (manual_rts) { + pinctrl_select_state(ascport->pinctrl, + ascport->states[NO_HW_FLOWCTRL]); + + gpiod = devm_gpiod_get(port->dev, "rts", GPIOD_OUT_LOW); + if (!IS_ERR(gpiod)) { + gpiod_set_consumer_name(gpiod, + port->dev->of_node->name); + ascport->rts = gpiod; + } + } else { + devm_gpiod_put(port->dev, ascport->rts); + ascport->rts = NULL; + pinctrl_select_state(ascport->pinctrl, + ascport->states[DEFAULT]); + } + } } static const char *asc_type(struct uart_port *port) diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index d103b07d10..4fa5a03eba 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -9,6 +9,7 @@ * Inspired by st-asc.c from STMicroelectronics (c) */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/console.h> #include <linux/delay.h> @@ -39,60 +40,64 @@ /* Register offsets */ static struct stm32_usart_info __maybe_unused stm32f4_info = { .ofs = { - .isr = 0x00, - .rdr = 0x04, - .tdr = 0x04, - .brr = 0x08, - .cr1 = 0x0c, - .cr2 = 0x10, - .cr3 = 0x14, - .gtpr = 0x18, - .rtor = UNDEF_REG, - .rqr = UNDEF_REG, - .icr = UNDEF_REG, + .isr = 0x00, + .rdr = 0x04, + .tdr = 0x04, + .brr = 0x08, + .cr1 = 0x0c, + .cr2 = 0x10, + .cr3 = 0x14, + .gtpr = 0x18, + .rtor = UNDEF_REG, + .rqr = UNDEF_REG, + .icr = UNDEF_REG, + .presc = UNDEF_REG, + .hwcfgr1 = UNDEF_REG, }, .cfg = { .uart_enable_bit = 13, .has_7bits_data = false, - .fifosize = 1, } }; static struct stm32_usart_info __maybe_unused stm32f7_info = { .ofs = { - .cr1 = 0x00, - .cr2 = 0x04, - .cr3 = 0x08, - .brr = 0x0c, - .gtpr = 0x10, - .rtor = 0x14, - .rqr = 0x18, - .isr = 0x1c, - .icr = 0x20, - .rdr = 0x24, - .tdr = 0x28, + .cr1 = 0x00, + .cr2 = 0x04, + .cr3 = 0x08, + .brr = 0x0c, + .gtpr = 0x10, + .rtor = 0x14, + .rqr = 0x18, + .isr = 0x1c, + .icr = 0x20, + .rdr = 0x24, + .tdr = 0x28, + .presc = UNDEF_REG, + .hwcfgr1 = UNDEF_REG, }, .cfg = { .uart_enable_bit = 0, .has_7bits_data = true, .has_swap = true, - .fifosize = 1, } }; static struct stm32_usart_info __maybe_unused stm32h7_info = { .ofs = { - .cr1 = 0x00, - .cr2 = 0x04, - .cr3 = 0x08, - .brr = 0x0c, - .gtpr = 0x10, - .rtor = 0x14, - .rqr = 0x18, - .isr = 0x1c, - .icr = 0x20, - .rdr = 0x24, - .tdr = 0x28, + .cr1 = 0x00, + .cr2 = 0x04, + .cr3 = 0x08, + .brr = 0x0c, + .gtpr = 0x10, + .rtor = 0x14, + .rqr = 0x18, + .isr = 0x1c, + .icr = 0x20, + .rdr = 0x24, + .tdr = 0x28, + .presc = 0x2c, + .hwcfgr1 = 0x3f0, }, .cfg = { .uart_enable_bit = 0, @@ -100,7 +105,6 @@ static struct stm32_usart_info __maybe_unused stm32h7_info = { .has_swap = true, .has_wakeup = true, .has_fifo = true, - .fifosize = 16, } }; @@ -1156,6 +1160,8 @@ static void stm32_usart_shutdown(struct uart_port *port) free_irq(port->irq, port); } +static const unsigned int stm32_usart_presc_val[] = {1, 2, 4, 6, 8, 10, 12, 16, 32, 64, 128, 256}; + static void stm32_usart_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old) @@ -1164,17 +1170,19 @@ static void stm32_usart_set_termios(struct uart_port *port, const struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; const struct stm32_usart_config *cfg = &stm32_port->info->cfg; struct serial_rs485 *rs485conf = &port->rs485; - unsigned int baud, bits; + unsigned int baud, bits, uart_clk, uart_clk_pres; u32 usartdiv, mantissa, fraction, oversampling; tcflag_t cflag = termios->c_cflag; - u32 cr1, cr2, cr3, isr; + u32 cr1, cr2, cr3, isr, brr, presc; unsigned long flags; int ret; if (!stm32_port->hw_flow_control) cflag &= ~CRTSCTS; - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 8); + uart_clk = clk_get_rate(stm32_port->clk); + + baud = uart_get_baud_rate(port, termios, old, 0, uart_clk / 8); uart_port_lock_irqsave(port, &flags); @@ -1276,27 +1284,48 @@ static void stm32_usart_set_termios(struct uart_port *port, cr3 |= USART_CR3_CTSE | USART_CR3_RTSE; } - usartdiv = DIV_ROUND_CLOSEST(port->uartclk, baud); + for (presc = 0; presc <= USART_PRESC_MAX; presc++) { + uart_clk_pres = DIV_ROUND_CLOSEST(uart_clk, stm32_usart_presc_val[presc]); + usartdiv = DIV_ROUND_CLOSEST(uart_clk_pres, baud); - /* - * The USART supports 16 or 8 times oversampling. - * By default we prefer 16 times oversampling, so that the receiver - * has a better tolerance to clock deviations. - * 8 times oversampling is only used to achieve higher speeds. - */ - if (usartdiv < 16) { - oversampling = 8; - cr1 |= USART_CR1_OVER8; - stm32_usart_set_bits(port, ofs->cr1, USART_CR1_OVER8); - } else { - oversampling = 16; - cr1 &= ~USART_CR1_OVER8; - stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_OVER8); + /* + * The USART supports 16 or 8 times oversampling. + * By default we prefer 16 times oversampling, so that the receiver + * has a better tolerance to clock deviations. + * 8 times oversampling is only used to achieve higher speeds. + */ + if (usartdiv < 16) { + oversampling = 8; + cr1 |= USART_CR1_OVER8; + stm32_usart_set_bits(port, ofs->cr1, USART_CR1_OVER8); + } else { + oversampling = 16; + cr1 &= ~USART_CR1_OVER8; + stm32_usart_clr_bits(port, ofs->cr1, USART_CR1_OVER8); + } + + mantissa = (usartdiv / oversampling) << USART_BRR_DIV_M_SHIFT; + fraction = usartdiv % oversampling; + brr = mantissa | fraction; + + if (FIELD_FIT(USART_BRR_MASK, brr)) { + if (ofs->presc != UNDEF_REG) { + port->uartclk = uart_clk_pres; + writel_relaxed(presc, port->membase + ofs->presc); + } else if (presc) { + /* We need a prescaler but we don't have it (STM32F4, STM32F7) */ + dev_err(port->dev, + "unable to set baudrate, input clock is too high"); + } + break; + } else if (presc == USART_PRESC_MAX) { + /* Even with prescaler and brr at max value we can't set baudrate */ + dev_err(port->dev, "unable to set baudrate, input clock is too high"); + break; + } } - mantissa = (usartdiv / oversampling) << USART_BRR_DIV_M_SHIFT; - fraction = usartdiv % oversampling; - writel_relaxed(mantissa | fraction, port->membase + ofs->brr); + writel_relaxed(brr, port->membase + ofs->brr); uart_update_timeout(port, cflag, baud); @@ -1480,37 +1509,57 @@ static const struct uart_ops stm32_uart_ops = { #endif /* CONFIG_CONSOLE_POLL */ }; -/* - * STM32H7 RX & TX FIFO threshold configuration (CR3 RXFTCFG / TXFTCFG) - * Note: 1 isn't a valid value in RXFTCFG / TXFTCFG. In this case, - * RXNEIE / TXEIE can be used instead of threshold irqs: RXFTIE / TXFTIE. - * So, RXFTCFG / TXFTCFG bitfields values are encoded as array index + 1. - */ -static const u32 stm32h7_usart_fifo_thresh_cfg[] = { 1, 2, 4, 8, 12, 14, 16 }; +struct stm32_usart_thresh_ratio { + int mul; + int div; +}; + +static const struct stm32_usart_thresh_ratio stm32h7_usart_fifo_thresh_cfg[] = { + {1, 8}, {1, 4}, {1, 2}, {3, 4}, {7, 8}, {1, 1} }; + +static int stm32_usart_get_thresh_value(u32 fifo_size, int index) +{ + return fifo_size * stm32h7_usart_fifo_thresh_cfg[index].mul / + stm32h7_usart_fifo_thresh_cfg[index].div; +} -static void stm32_usart_get_ftcfg(struct platform_device *pdev, const char *p, - int *ftcfg) +static int stm32_usart_get_ftcfg(struct platform_device *pdev, struct stm32_port *stm32port, + const char *p, int *ftcfg) { - u32 bytes, i; + const struct stm32_usart_offsets *ofs = &stm32port->info->ofs; + u32 bytes, i, cfg8; + int fifo_size; + + if (WARN_ON(ofs->hwcfgr1 == UNDEF_REG)) + return 1; + + cfg8 = FIELD_GET(USART_HWCFGR1_CFG8, + readl_relaxed(stm32port->port.membase + ofs->hwcfgr1)); + + /* On STM32H7, hwcfgr is not present, so returned value will be 0 */ + fifo_size = cfg8 ? 1 << cfg8 : STM32H7_USART_FIFO_SIZE; - /* DT option to get RX & TX FIFO threshold (default to 8 bytes) */ + /* DT option to get RX & TX FIFO threshold (default to half fifo size) */ if (of_property_read_u32(pdev->dev.of_node, p, &bytes)) - bytes = 8; + bytes = fifo_size / 2; - for (i = 0; i < ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg); i++) - if (stm32h7_usart_fifo_thresh_cfg[i] >= bytes) + if (bytes < stm32_usart_get_thresh_value(fifo_size, 0)) { + *ftcfg = -EINVAL; + return fifo_size; + } + + for (i = 0; i < ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg); i++) { + if (stm32_usart_get_thresh_value(fifo_size, i) >= bytes) break; + } if (i >= ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg)) i = ARRAY_SIZE(stm32h7_usart_fifo_thresh_cfg) - 1; - dev_dbg(&pdev->dev, "%s set to %d bytes\n", p, - stm32h7_usart_fifo_thresh_cfg[i]); + dev_dbg(&pdev->dev, "%s set to %d/%d bytes\n", p, + stm32_usart_get_thresh_value(fifo_size, i), fifo_size); - /* Provide FIFO threshold ftcfg (1 is invalid: threshold irq unused) */ - if (i) - *ftcfg = i - 1; - else - *ftcfg = -EINVAL; + *ftcfg = i; + return fifo_size; } static void stm32_usart_deinit_port(struct stm32_port *stm32port) @@ -1540,7 +1589,6 @@ static int stm32_usart_init_port(struct stm32_port *stm32port, port->flags = UPF_BOOT_AUTOCONF; port->ops = &stm32_uart_ops; port->dev = &pdev->dev; - port->fifosize = stm32port->info->cfg.fifosize; port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_STM32_CONSOLE); port->irq = irq; port->rs485_config = stm32_usart_config_rs485; @@ -1556,14 +1604,6 @@ static int stm32_usart_init_port(struct stm32_port *stm32port, stm32port->swap = stm32port->info->cfg.has_swap && of_property_read_bool(pdev->dev.of_node, "rx-tx-swap"); - stm32port->fifoen = stm32port->info->cfg.has_fifo; - if (stm32port->fifoen) { - stm32_usart_get_ftcfg(pdev, "rx-threshold", - &stm32port->rxftcfg); - stm32_usart_get_ftcfg(pdev, "tx-threshold", - &stm32port->txftcfg); - } - port->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(port->membase)) return PTR_ERR(port->membase); @@ -1586,6 +1626,15 @@ static int stm32_usart_init_port(struct stm32_port *stm32port, goto err_clk; } + stm32port->fifoen = stm32port->info->cfg.has_fifo; + if (stm32port->fifoen) { + stm32_usart_get_ftcfg(pdev, stm32port, "rx-threshold", &stm32port->rxftcfg); + port->fifosize = stm32_usart_get_ftcfg(pdev, stm32port, "tx-threshold", + &stm32port->txftcfg); + } else { + port->fifosize = 1; + } + stm32port->gpios = mctrl_gpio_init(&stm32port->port, 0); if (IS_ERR(stm32port->gpios)) { ret = PTR_ERR(stm32port->gpios); diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h index f59f831b2a..af20258ccc 100644 --- a/drivers/tty/serial/stm32-usart.h +++ b/drivers/tty/serial/stm32-usart.h @@ -9,17 +9,19 @@ #define DRIVER_NAME "stm32-usart" struct stm32_usart_offsets { - u8 cr1; - u8 cr2; - u8 cr3; - u8 brr; - u8 gtpr; - u8 rtor; - u8 rqr; - u8 isr; - u8 icr; - u8 rdr; - u8 tdr; + u16 cr1; + u16 cr2; + u16 cr3; + u16 brr; + u16 gtpr; + u16 rtor; + u16 rqr; + u16 isr; + u16 icr; + u16 rdr; + u16 tdr; + u16 presc; + u16 hwcfgr1; }; struct stm32_usart_config { @@ -28,7 +30,6 @@ struct stm32_usart_config { bool has_swap; bool has_wakeup; bool has_fifo; - int fifosize; }; struct stm32_usart_info { @@ -36,7 +37,7 @@ struct stm32_usart_info { struct stm32_usart_config cfg; }; -#define UNDEF_REG 0xff +#define UNDEF_REG 0xffff /* USART_SR (F4) / USART_ISR (F7) */ #define USART_SR_PE BIT(0) @@ -71,6 +72,7 @@ struct stm32_usart_info { #define USART_BRR_DIV_M_MASK GENMASK(15, 4) #define USART_BRR_DIV_M_SHIFT 4 #define USART_BRR_04_R_SHIFT 1 +#define USART_BRR_MASK (USART_BRR_DIV_M_MASK | USART_BRR_DIV_F_MASK) /* USART_CR1 */ #define USART_CR1_SBK BIT(0) @@ -176,8 +178,16 @@ struct stm32_usart_info { #define USART_ICR_CMCF BIT(17) /* F7 */ #define USART_ICR_WUCF BIT(20) /* H7 */ +/* USART_PRESC */ +#define USART_PRESC GENMASK(3, 0) /* H7 */ +#define USART_PRESC_MAX 0b1011 + +/* USART_HWCFCR1 */ +#define USART_HWCFGR1_CFG8 GENMASK(31, 28) /* MP1 */ + #define STM32_SERIAL_NAME "ttySTM" -#define STM32_MAX_PORTS 8 +#define STM32_MAX_PORTS 9 +#define STM32H7_USART_FIFO_SIZE 16 #define RX_BUF_L 4096 /* dma rx buffer length */ #define RX_BUF_P (RX_BUF_L / 2) /* dma rx buffer period */ diff --git a/drivers/tty/serial/sunplus-uart.c b/drivers/tty/serial/sunplus-uart.c index 99f5285819..f5e29eb4a4 100644 --- a/drivers/tty/serial/sunplus-uart.c +++ b/drivers/tty/serial/sunplus-uart.c @@ -260,7 +260,7 @@ static void receive_chars(struct uart_port *port) if (port->ignore_status_mask & SUP_DUMMY_READ) goto ignore_char; - if (uart_handle_sysrq_char(port, ch)) + if (uart_prepare_sysrq_char(port, ch)) goto ignore_char; uart_insert_char(port, lsr, SUP_UART_LSR_OE, ch, flag); @@ -287,7 +287,7 @@ static irqreturn_t sunplus_uart_irq(int irq, void *args) if (isc & SUP_UART_ISC_TX) transmit_chars(port); - uart_port_unlock(port); + uart_unlock_and_check_sysrq(port); return IRQ_HANDLED; } @@ -512,22 +512,16 @@ static void sunplus_console_write(struct console *co, unsigned long flags; int locked = 1; - local_irq_save(flags); - - if (sunplus_console_ports[co->index]->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = uart_port_trylock(&sunplus_console_ports[co->index]->port); + if (oops_in_progress) + locked = uart_port_trylock_irqsave(&sunplus_console_ports[co->index]->port, &flags); else - uart_port_lock(&sunplus_console_ports[co->index]->port); + uart_port_lock_irqsave(&sunplus_console_ports[co->index]->port, &flags); uart_console_write(&sunplus_console_ports[co->index]->port, s, count, sunplus_uart_console_putchar); if (locked) - uart_port_unlock(&sunplus_console_ports[co->index]->port); - - local_irq_restore(flags); + uart_port_unlock_irqrestore(&sunplus_console_ports[co->index]->port, flags); } static int __init sunplus_console_setup(struct console *co, char *options) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 920762d7b4..5f48ec37cb 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -22,7 +22,9 @@ #include <linux/of.h> #include <linux/module.h> #include <linux/pm_runtime.h> -#include <linux/iopoll.h> +#include <linux/gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/delay.h> #define CDNS_UART_TTY_NAME "ttyPS" #define CDNS_UART_NAME "xuartps" @@ -193,6 +195,9 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); * @clk_rate_change_nb: Notifier block for clock changes * @quirks: Flags for RXBS support. * @cts_override: Modem control state override + * @gpiod_rts: Pointer to the gpio descriptor + * @rs485_tx_started: RS485 tx state + * @tx_timer: Timer for tx */ struct cdns_uart { struct uart_port *port; @@ -203,10 +208,21 @@ struct cdns_uart { struct notifier_block clk_rate_change_nb; u32 quirks; bool cts_override; + struct gpio_desc *gpiod_rts; + bool rs485_tx_started; + struct hrtimer tx_timer; }; struct cdns_platform_data { u32 quirks; }; + +struct serial_rs485 cdns_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | + SER_RS485_RTS_AFTER_SEND, + .delay_rts_before_send = 1, + .delay_rts_after_send = 1, +}; + #define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ clk_rate_change_nb) @@ -306,17 +322,114 @@ static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus) } /** - * cdns_uart_handle_tx - Handle the bytes to be Txed. + * cdns_rts_gpio_enable - Configure RTS/GPIO to high/low + * @cdns_uart: Handle to the cdns_uart + * @enable: Value to be set to RTS/GPIO + */ +static void cdns_rts_gpio_enable(struct cdns_uart *cdns_uart, bool enable) +{ + u32 val; + + if (cdns_uart->gpiod_rts) { + gpiod_set_value(cdns_uart->gpiod_rts, enable); + } else { + val = readl(cdns_uart->port->membase + CDNS_UART_MODEMCR); + if (enable) + val |= CDNS_UART_MODEMCR_RTS; + else + val &= ~CDNS_UART_MODEMCR_RTS; + writel(val, cdns_uart->port->membase + CDNS_UART_MODEMCR); + } +} + +/** + * cdns_rs485_tx_setup - Tx setup specific to rs485 + * @cdns_uart: Handle to the cdns_uart + */ +static void cdns_rs485_tx_setup(struct cdns_uart *cdns_uart) +{ + bool enable; + + enable = cdns_uart->port->rs485.flags & SER_RS485_RTS_ON_SEND; + cdns_rts_gpio_enable(cdns_uart, enable); + + cdns_uart->rs485_tx_started = true; +} + +/** + * cdns_rs485_rx_setup - Rx setup specific to rs485 + * @cdns_uart: Handle to the cdns_uart + */ +static void cdns_rs485_rx_setup(struct cdns_uart *cdns_uart) +{ + bool enable; + + enable = cdns_uart->port->rs485.flags & SER_RS485_RTS_AFTER_SEND; + cdns_rts_gpio_enable(cdns_uart, enable); + + cdns_uart->rs485_tx_started = false; +} + +/** + * cdns_uart_tx_empty - Check whether TX is empty + * @port: Handle to the uart port structure + * + * Return: TIOCSER_TEMT on success, 0 otherwise + */ +static unsigned int cdns_uart_tx_empty(struct uart_port *port) +{ + unsigned int status; + + status = readl(port->membase + CDNS_UART_SR); + status &= (CDNS_UART_SR_TXEMPTY | CDNS_UART_SR_TACTIVE); + return (status == CDNS_UART_SR_TXEMPTY) ? TIOCSER_TEMT : 0; +} + +/** + * cdns_rs485_rx_callback - Timer rx callback handler for rs485. + * @t: Handle to the hrtimer structure + */ +static enum hrtimer_restart cdns_rs485_rx_callback(struct hrtimer *t) +{ + struct cdns_uart *cdns_uart = container_of(t, struct cdns_uart, tx_timer); + + /* + * Default Rx should be setup, because Rx signaling path + * need to enable to receive data. + */ + cdns_rs485_rx_setup(cdns_uart); + + return HRTIMER_NORESTART; +} + +/** + * cdns_calc_after_tx_delay - calculate delay required for after tx. + * @cdns_uart: Handle to the cdns_uart + */ +static u64 cdns_calc_after_tx_delay(struct cdns_uart *cdns_uart) +{ + /* + * Frame time + stop bit time + rs485.delay_rts_after_send + */ + return cdns_uart->port->frame_time + + DIV_ROUND_UP(cdns_uart->port->frame_time, 7) + + (u64)cdns_uart->port->rs485.delay_rts_after_send * NSEC_PER_MSEC; +} + +/** + * cdns_uart_handle_tx - Handle the bytes to be transmitted. * @dev_id: Id of the UART port * Return: None */ static void cdns_uart_handle_tx(void *dev_id) { struct uart_port *port = (struct uart_port *)dev_id; + struct cdns_uart *cdns_uart = port->private_data; struct circ_buf *xmit = &port->state->xmit; unsigned int numbytes; - if (uart_circ_empty(xmit)) { + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + /* Disable the TX Empty interrupt */ writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR); return; } @@ -332,6 +445,16 @@ static void cdns_uart_handle_tx(void *dev_id) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); + + /* Enable the TX Empty interrupt */ + writel(CDNS_UART_IXR_TXEMPTY, cdns_uart->port->membase + CDNS_UART_IER); + + if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED && + (uart_circ_empty(xmit) || uart_tx_stopped(port))) { + cdns_uart->tx_timer.function = &cdns_rs485_rx_callback; + hrtimer_start(&cdns_uart->tx_timer, + ns_to_ktime(cdns_calc_after_tx_delay(cdns_uart)), HRTIMER_MODE_REL); + } } /** @@ -565,12 +688,28 @@ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, #endif /** + * cdns_rs485_tx_callback - Timer tx callback handler for rs485. + * @t: Handle to the hrtimer structure + */ +static enum hrtimer_restart cdns_rs485_tx_callback(struct hrtimer *t) +{ + struct cdns_uart *cdns_uart = container_of(t, struct cdns_uart, tx_timer); + + uart_port_lock(cdns_uart->port); + cdns_uart_handle_tx(cdns_uart->port); + uart_port_unlock(cdns_uart->port); + + return HRTIMER_NORESTART; +} + +/** * cdns_uart_start_tx - Start transmitting bytes * @port: Handle to the uart port structure */ static void cdns_uart_start_tx(struct uart_port *port) { unsigned int status; + struct cdns_uart *cdns_uart = port->private_data; if (uart_tx_stopped(port)) return; @@ -587,12 +726,19 @@ static void cdns_uart_start_tx(struct uart_port *port) if (uart_circ_empty(&port->state->xmit)) return; + /* Clear the TX Empty interrupt */ writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_ISR); + if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) { + if (!cdns_uart->rs485_tx_started) { + cdns_uart->tx_timer.function = &cdns_rs485_tx_callback; + cdns_rs485_tx_setup(cdns_uart); + return hrtimer_start(&cdns_uart->tx_timer, + ms_to_ktime(port->rs485.delay_rts_before_send), + HRTIMER_MODE_REL); + } + } cdns_uart_handle_tx(port); - - /* Enable the TX Empty interrupt */ - writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IER); } /** @@ -602,6 +748,10 @@ static void cdns_uart_start_tx(struct uart_port *port) static void cdns_uart_stop_tx(struct uart_port *port) { unsigned int regval; + struct cdns_uart *cdns_uart = port->private_data; + + if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) + cdns_rs485_rx_setup(cdns_uart); regval = readl(port->membase + CDNS_UART_CR); regval |= CDNS_UART_CR_TX_DIS; @@ -627,21 +777,6 @@ static void cdns_uart_stop_rx(struct uart_port *port) } /** - * cdns_uart_tx_empty - Check whether TX is empty - * @port: Handle to the uart port structure - * - * Return: TIOCSER_TEMT on success, 0 otherwise - */ -static unsigned int cdns_uart_tx_empty(struct uart_port *port) -{ - unsigned int status; - - status = readl(port->membase + CDNS_UART_SR) & - (CDNS_UART_SR_TXEMPTY | CDNS_UART_SR_TACTIVE); - return (status == CDNS_UART_SR_TXEMPTY) ? TIOCSER_TEMT : 0; -} - -/** * cdns_uart_break_ctl - Based on the input ctl we have to start or stop * transmitting char breaks * @port: Handle to the uart port structure @@ -829,6 +964,9 @@ static int cdns_uart_startup(struct uart_port *port) (CDNS_UART_CR_TXRST | CDNS_UART_CR_RXRST)) cpu_relax(); + if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) + cdns_rs485_rx_setup(cdns_uart); + /* * Clear the RX disable bit and then set the RX enable bit to enable * the receiver. @@ -888,6 +1026,10 @@ static void cdns_uart_shutdown(struct uart_port *port) { int status; unsigned long flags; + struct cdns_uart *cdns_uart = port->private_data; + + if (cdns_uart->port->rs485.flags & SER_RS485_ENABLED) + hrtimer_cancel(&cdns_uart->tx_timer); uart_port_lock_irqsave(port, &flags); @@ -1033,6 +1175,8 @@ static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) if (mctrl & TIOCM_RTS) val |= CDNS_UART_MODEMCR_RTS; + if (cdns_uart_data->gpiod_rts) + gpiod_set_value(cdns_uart_data->gpiod_rts, !(mctrl & TIOCM_RTS)); if (mctrl & TIOCM_DTR) val |= CDNS_UART_MODEMCR_DTR; if (mctrl & TIOCM_LOOP) @@ -1456,6 +1600,39 @@ MODULE_DEVICE_TABLE(of, cdns_uart_of_match); static int instances; /** + * cdns_rs485_config - Called when an application calls TIOCSRS485 ioctl. + * @port: Pointer to the uart_port structure + * @termios: Pointer to the ktermios structure + * @rs485: Pointer to the serial_rs485 structure + * + * Return: 0 + */ +static int cdns_rs485_config(struct uart_port *port, struct ktermios *termios, + struct serial_rs485 *rs485) +{ + u32 val; + struct cdns_uart *cdns_uart = port->private_data; + + if (rs485->flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + /* Make sure auto RTS is disabled */ + val = readl(port->membase + CDNS_UART_MODEMCR); + val &= ~CDNS_UART_MODEMCR_FCM; + writel(val, port->membase + CDNS_UART_MODEMCR); + + /* Timer setup */ + hrtimer_init(&cdns_uart->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + cdns_uart->tx_timer.function = &cdns_rs485_tx_callback; + + /* Disable transmitter and make Rx setup*/ + cdns_uart_stop_tx(port); + } else { + hrtimer_cancel(&cdns_uart->tx_timer); + } + return 0; +} + +/** * cdns_uart_probe - Platform driver probe * @pdev: Pointer to the platform device structure * @@ -1597,9 +1774,23 @@ static int cdns_uart_probe(struct platform_device *pdev) port->private_data = cdns_uart_data; port->read_status_mask = CDNS_UART_IXR_TXEMPTY | CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_OVERRUN | CDNS_UART_IXR_TOUT; + port->rs485_config = cdns_rs485_config; + port->rs485_supported = cdns_rs485_supported; cdns_uart_data->port = port; platform_set_drvdata(pdev, port); + rc = uart_get_rs485_mode(port); + if (rc) + goto err_out_clk_notifier; + + cdns_uart_data->gpiod_rts = devm_gpiod_get_optional(&pdev->dev, "rts", + GPIOD_OUT_LOW); + if (IS_ERR(cdns_uart_data->gpiod_rts)) { + rc = PTR_ERR(cdns_uart_data->gpiod_rts); + dev_err(port->dev, "xuartps: devm_gpiod_get_optional failed\n"); + goto err_out_clk_notifier; + } + pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT); pm_runtime_set_active(&pdev->dev); @@ -1618,6 +1809,8 @@ static int cdns_uart_probe(struct platform_device *pdev) console_port = port; } #endif + if (cdns_uart_data->port->rs485.flags & SER_RS485_ENABLED) + cdns_rs485_rx_setup(cdns_uart_data); rc = uart_add_one_port(&cdns_uart_uart_driver, port); if (rc) { @@ -1646,6 +1839,7 @@ err_out_pm_disable: pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); +err_out_clk_notifier: #ifdef CONFIG_COMMON_CLK clk_notifier_unregister(cdns_uart_data->uartclk, &cdns_uart_data->clk_rate_change_nb); diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index f8883afbee..79f0ff94ce 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -7,6 +7,7 @@ #include <linux/errno.h> #include <linux/minmax.h> #include <linux/tty.h> +#include <linux/tty_buffer.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> #include <linux/timer.h> diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 3f68e213df..d80e9d4c97 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -545,6 +545,12 @@ int tty_set_ldisc(struct tty_struct *tty, int disc) goto out; } + if (tty->ops->ldisc_ok) { + retval = tty->ops->ldisc_ok(tty, disc); + if (retval) + goto out; + } + old_ldisc = tty->ldisc; /* Shutdown the old discipline. */ diff --git a/drivers/tty/vt/Makefile b/drivers/tty/vt/Makefile index b3dfe9d571..2c8ce8b592 100644 --- a/drivers/tty/vt/Makefile +++ b/drivers/tty/vt/Makefile @@ -5,9 +5,9 @@ FONTMAPFILE = cp437.uni obj-$(CONFIG_VT) += vt_ioctl.o vc_screen.o \ - selection.o keyboard.o + selection.o keyboard.o \ + vt.o defkeymap.o obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o -obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o # Files generated that shall be removed upon make clean clean-files := consolemap_deftbl.c defkeymap.c diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 8967c3a0d9..564341f1a7 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -7,7 +7,7 @@ * 'int set_selection_kernel(struct tiocl_selection *, struct tty_struct *)' * 'void clear_selection(void)' * 'int paste_selection(struct tty_struct *)' - * 'int sel_loadlut(char __user *)' + * 'int sel_loadlut(u32 __user *)' * * Now that /dev/vcs exists, most of this can disappear again. */ @@ -73,10 +73,12 @@ sel_pos(int n, bool unicode) } /** - * clear_selection - remove current selection + * clear_selection - remove current selection * - * Remove the current selection highlight, if any from the console - * holding the selection. The caller must hold the console lock. + * Remove the current selection highlight, if any from the console holding the + * selection. + * + * Locking: The caller must hold the console lock. */ void clear_selection(void) { @@ -88,7 +90,7 @@ void clear_selection(void) } EXPORT_SYMBOL_GPL(clear_selection); -bool vc_is_sel(struct vc_data *vc) +bool vc_is_sel(const struct vc_data *vc) { return vc == vc_sel.cons; } @@ -110,18 +112,25 @@ static inline int inword(const u32 c) } /** - * sel_loadlut() - load the LUT table - * @p: user table + * sel_loadlut() - load the LUT table + * @lut: user table + * + * Load the LUT table from user space. Make a temporary copy so a partial + * update doesn't make a mess. * - * Load the LUT table from user space. The caller must hold the console - * lock. Make a temporary copy so a partial update doesn't make a mess. + * Locking: The console lock is acquired. */ -int sel_loadlut(char __user *p) +int sel_loadlut(u32 __user *lut) { u32 tmplut[ARRAY_SIZE(inwordLut)]; - if (copy_from_user(tmplut, (u32 __user *)(p+4), sizeof(inwordLut))) + + if (copy_from_user(tmplut, lut, sizeof(inwordLut))) return -EFAULT; + + console_lock(); memcpy(inwordLut, tmplut, sizeof(inwordLut)); + console_unlock(); + return 0; } @@ -166,14 +175,14 @@ static int store_utf8(u32 c, char *p) } /** - * set_selection_user - set the current selection. - * @sel: user selection info - * @tty: the console tty + * set_selection_user - set the current selection. + * @sel: user selection info + * @tty: the console tty * - * Invoked by the ioctl handle for the vt layer. + * Invoked by the ioctl handle for the vt layer. * - * The entire selection process is managed under the console_lock. It's - * a lot under the lock but its hardly a performance path + * Locking: The entire selection process is managed under the console_lock. + * It's a lot under the lock but its hardly a performance path. */ int set_selection_user(const struct tiocl_selection __user *sel, struct tty_struct *tty) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 52e6ca1ba2..cd87e3d129 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -145,7 +145,7 @@ static void gotoxy(struct vc_data *vc, int new_x, int new_y); static void save_cur(struct vc_data *vc); static void reset_terminal(struct vc_data *vc, int do_clear); static void con_flush_chars(struct tty_struct *tty); -static int set_vesa_blanking(char __user *p); +static int set_vesa_blanking(u8 __user *mode); static void set_cursor(struct vc_data *vc); static void hide_cursor(struct vc_data *vc); static void console_callback(struct work_struct *ignored); @@ -175,7 +175,7 @@ int do_poke_blanked_console; int console_blanked; EXPORT_SYMBOL(console_blanked); -static int vesa_blank_mode; /* 0:none 1:suspendV 2:suspendH 3:powerdown */ +static enum vesa_blank_mode vesa_blank_mode; static int vesa_off_interval; static int blankinterval; core_param(consoleblank, blankinterval, int, 0444); @@ -286,18 +286,20 @@ static inline bool con_should_update(const struct vc_data *vc) return con_is_visible(vc) && !console_blanked; } -static inline unsigned short *screenpos(const struct vc_data *vc, int offset, - bool viewed) +static inline u16 *screenpos(const struct vc_data *vc, unsigned int offset, + bool viewed) { - unsigned short *p; - - if (!viewed) - p = (unsigned short *)(vc->vc_origin + offset); - else if (!vc->vc_sw->con_screen_pos) - p = (unsigned short *)(vc->vc_visible_origin + offset); + unsigned long origin = viewed ? vc->vc_visible_origin : vc->vc_origin; + + return (u16 *)(origin + offset); +} + +static void con_putc(struct vc_data *vc, u16 ca, unsigned int y, unsigned int x) +{ + if (vc->vc_sw->con_putc) + vc->vc_sw->con_putc(vc, ca, y, x); else - p = vc->vc_sw->con_screen_pos(vc, offset); - return p; + vc->vc_sw->con_putcs(vc, &ca, 1, y, x); } /* Called from the keyboard irq path.. */ @@ -591,18 +593,12 @@ static void con_scroll(struct vc_data *vc, unsigned int top, static void do_update_region(struct vc_data *vc, unsigned long start, int count) { unsigned int xx, yy, offset; - u16 *p; + u16 *p = (u16 *)start; + + offset = (start - vc->vc_origin) / 2; + xx = offset % vc->vc_cols; + yy = offset / vc->vc_cols; - p = (u16 *) start; - if (!vc->vc_sw->con_getxy) { - offset = (start - vc->vc_origin) / 2; - xx = offset % vc->vc_cols; - yy = offset / vc->vc_cols; - } else { - int nxx, nyy; - start = vc->vc_sw->con_getxy(vc, start, &nxx, &nyy); - xx = nxx; yy = nyy; - } for(;;) { u16 attrib = scr_readw(p) & 0xff00; int startx = xx; @@ -625,10 +621,6 @@ static void do_update_region(struct vc_data *vc, unsigned long start, int count) break; xx = 0; yy++; - if (vc->vc_sw->con_getxy) { - p = (u16 *)start; - start = vc->vc_sw->con_getxy(vc, start, NULL, NULL); - } } } @@ -703,7 +695,7 @@ static void update_attr(struct vc_data *vc) /* Note: inverting the screen twice should revert to the original state */ void invert_screen(struct vc_data *vc, int offset, int count, bool viewed) { - unsigned short *p; + u16 *p; WARN_CONSOLE_UNLOCKED(); @@ -762,7 +754,7 @@ void complement_pos(struct vc_data *vc, int offset) old_offset < vc->vc_screenbuf_size) { scr_writew(old, screenpos(vc, old_offset, true)); if (con_should_update(vc)) - vc->vc_sw->con_putc(vc, old, oldy, oldx); + con_putc(vc, old, oldy, oldx); notify_update(vc); } @@ -771,15 +763,14 @@ void complement_pos(struct vc_data *vc, int offset) if (offset != -1 && offset >= 0 && offset < vc->vc_screenbuf_size) { unsigned short new; - unsigned short *p; - p = screenpos(vc, offset, true); + u16 *p = screenpos(vc, offset, true); old = scr_readw(p); new = old ^ vc->vc_complement_mask; scr_writew(new, p); if (con_should_update(vc)) { oldx = (offset >> 1) % vc->vc_cols; oldy = (offset >> 1) / vc->vc_cols; - vc->vc_sw->con_putc(vc, new, oldy, oldx); + con_putc(vc, new, oldy, oldx); } notify_update(vc); } @@ -833,7 +824,7 @@ static void add_softcursor(struct vc_data *vc) i ^= CUR_FG; scr_writew(i, (u16 *)vc->vc_pos); if (con_should_update(vc)) - vc->vc_sw->con_putc(vc, i, vc->state.y, vc->state.x); + con_putc(vc, i, vc->state.y, vc->state.x); } static void hide_softcursor(struct vc_data *vc) @@ -841,8 +832,8 @@ static void hide_softcursor(struct vc_data *vc) if (softcursor_original != -1) { scr_writew(softcursor_original, (u16 *)vc->vc_pos); if (con_should_update(vc)) - vc->vc_sw->con_putc(vc, softcursor_original, - vc->state.y, vc->state.x); + con_putc(vc, softcursor_original, vc->state.y, + vc->state.x); softcursor_original = -1; } } @@ -852,7 +843,7 @@ static void hide_cursor(struct vc_data *vc) if (vc_is_sel(vc)) clear_selection(); - vc->vc_sw->con_cursor(vc, CM_ERASE); + vc->vc_sw->con_cursor(vc, false); hide_softcursor(vc); } @@ -865,7 +856,7 @@ static void set_cursor(struct vc_data *vc) clear_selection(); add_softcursor(vc); if (CUR_SIZE(vc->vc_cursor_type) != CUR_NONE) - vc->vc_sw->con_cursor(vc, CM_DRAW); + vc->vc_sw->con_cursor(vc, true); } else hide_cursor(vc); } @@ -897,21 +888,18 @@ static void flush_scrollback(struct vc_data *vc) WARN_CONSOLE_UNLOCKED(); set_origin(vc); - if (vc->vc_sw->con_flush_scrollback) { - vc->vc_sw->con_flush_scrollback(vc); - } else if (con_is_visible(vc)) { - /* - * When no con_flush_scrollback method is provided then the - * legacy way for flushing the scrollback buffer is to use - * a side effect of the con_switch method. We do it only on - * the foreground console as background consoles have no - * scrollback buffers in that case and we obviously don't - * want to switch to them. - */ - hide_cursor(vc); - vc->vc_sw->con_switch(vc); - set_cursor(vc); - } + if (!con_is_visible(vc)) + return; + + /* + * The legacy way for flushing the scrollback buffer is to use a side + * effect of the con_switch method. We do it only on the foreground + * console as background consoles have no scrollback buffers in that + * case and we obviously don't want to switch to them. + */ + hide_cursor(vc); + vc->vc_sw->con_switch(vc); + set_cursor(vc); } /* @@ -962,7 +950,7 @@ void redraw_screen(struct vc_data *vc, int is_switch) } if (redraw) { - int update; + bool update; int old_was_color = vc->vc_can_do_color; set_origin(vc); @@ -999,7 +987,7 @@ int vc_cons_allocated(unsigned int i) return (i < MAX_NR_CONSOLES && vc_cons[i].d); } -static void visual_init(struct vc_data *vc, int num, int init) +static void visual_init(struct vc_data *vc, int num, bool init) { /* ++Geert: vc->vc_sw->con_init determines console size */ if (vc->vc_sw) @@ -1083,7 +1071,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ vc->port.ops = &vc_port_ops; INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); - visual_init(vc, currcons, 1); + visual_init(vc, currcons, true); if (!*vc->uni_pagedict_loc) con_set_default_unimap(vc); @@ -1115,51 +1103,44 @@ err_free: } static inline int resize_screen(struct vc_data *vc, int width, int height, - int user) + bool from_user) { /* Resizes the resolution of the display adapater */ int err = 0; if (vc->vc_sw->con_resize) - err = vc->vc_sw->con_resize(vc, width, height, user); + err = vc->vc_sw->con_resize(vc, width, height, from_user); return err; } /** - * vc_do_resize - resizing method for the tty - * @tty: tty being resized - * @vc: virtual console private data - * @cols: columns - * @lines: lines + * vc_do_resize - resizing method for the tty + * @tty: tty being resized + * @vc: virtual console private data + * @cols: columns + * @lines: lines + * @from_user: invoked by a user? * - * Resize a virtual console, clipping according to the actual constraints. - * If the caller passes a tty structure then update the termios winsize - * information and perform any necessary signal handling. + * Resize a virtual console, clipping according to the actual constraints. If + * the caller passes a tty structure then update the termios winsize + * information and perform any necessary signal handling. * - * Caller must hold the console semaphore. Takes the termios rwsem and - * ctrl.lock of the tty IFF a tty is passed. + * Locking: Caller must hold the console semaphore. Takes the termios rwsem and + * ctrl.lock of the tty IFF a tty is passed. */ - static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, - unsigned int cols, unsigned int lines) + unsigned int cols, unsigned int lines, bool from_user) { unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; unsigned long end; unsigned int old_rows, old_row_size, first_copied_row; unsigned int new_cols, new_rows, new_row_size, new_screen_size; - unsigned int user; unsigned short *oldscreen, *newscreen; u32 **new_uniscr = NULL; WARN_CONSOLE_UNLOCKED(); - if (!vc) - return -ENXIO; - - user = vc->vc_resize_user; - vc->vc_resize_user = 0; - if (cols > VC_MAXCOL || lines > VC_MAXROW) return -EINVAL; @@ -1185,7 +1166,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, * to deal with possible errors from the code below, we call * the resize_screen here as well. */ - return resize_screen(vc, new_cols, new_rows, user); + return resize_screen(vc, new_cols, new_rows, from_user); } if (new_screen_size > KMALLOC_MAX_SIZE || !new_screen_size) @@ -1208,7 +1189,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, old_rows = vc->vc_rows; old_row_size = vc->vc_size_row; - err = resize_screen(vc, new_cols, new_rows, user); + err = resize_screen(vc, new_cols, new_rows, from_user); if (err) { kfree(newscreen); vc_uniscr_free(new_uniscr); @@ -1295,34 +1276,35 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, } /** - * vc_resize - resize a VT - * @vc: virtual console - * @cols: columns - * @rows: rows + * __vc_resize - resize a VT + * @vc: virtual console + * @cols: columns + * @rows: rows + * @from_user: invoked by a user? * - * Resize a virtual console as seen from the console end of things. We - * use the common vc_do_resize methods to update the structures. The - * caller must hold the console sem to protect console internals and - * vc->port.tty + * Resize a virtual console as seen from the console end of things. We use the + * common vc_do_resize() method to update the structures. + * + * Locking: The caller must hold the console sem to protect console internals + * and @vc->port.tty. */ - -int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows) +int __vc_resize(struct vc_data *vc, unsigned int cols, unsigned int rows, + bool from_user) { - return vc_do_resize(vc->port.tty, vc, cols, rows); + return vc_do_resize(vc->port.tty, vc, cols, rows, from_user); } -EXPORT_SYMBOL(vc_resize); +EXPORT_SYMBOL(__vc_resize); /** - * vt_resize - resize a VT - * @tty: tty to resize - * @ws: winsize attributes + * vt_resize - resize a VT + * @tty: tty to resize + * @ws: winsize attributes * - * Resize a virtual terminal. This is called by the tty layer as we - * register our own handler for resizing. The mutual helper does all - * the actual work. + * Resize a virtual terminal. This is called by the tty layer as we register + * our own handler for resizing. The mutual helper does all the actual work. * - * Takes the console sem and the called methods then take the tty - * termios_rwsem and the tty ctrl.lock in that order. + * Locking: Takes the console sem and the called methods then take the tty + * termios_rwsem and the tty ctrl.lock in that order. */ static int vt_resize(struct tty_struct *tty, struct winsize *ws) { @@ -1330,7 +1312,7 @@ static int vt_resize(struct tty_struct *tty, struct winsize *ws) int ret; console_lock(); - ret = vc_do_resize(tty, vc, ws->ws_col, ws->ws_row); + ret = vc_do_resize(tty, vc, ws->ws_col, ws->ws_row, false); console_unlock(); return ret; } @@ -1503,36 +1485,43 @@ static inline void del(struct vc_data *vc) /* ignored */ } -static void csi_J(struct vc_data *vc, int vpar) +enum CSI_J { + CSI_J_CURSOR_TO_END = 0, + CSI_J_START_TO_CURSOR = 1, + CSI_J_VISIBLE = 2, + CSI_J_FULL = 3, +}; + +static void csi_J(struct vc_data *vc, enum CSI_J vpar) { + unsigned short *start; unsigned int count; - unsigned short * start; switch (vpar) { - case 0: /* erase from cursor to end of display */ - vc_uniscr_clear_line(vc, vc->state.x, - vc->vc_cols - vc->state.x); - vc_uniscr_clear_lines(vc, vc->state.y + 1, - vc->vc_rows - vc->state.y - 1); - count = (vc->vc_scr_end - vc->vc_pos) >> 1; - start = (unsigned short *)vc->vc_pos; - break; - case 1: /* erase from start to cursor */ - vc_uniscr_clear_line(vc, 0, vc->state.x + 1); - vc_uniscr_clear_lines(vc, 0, vc->state.y); - count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1; - start = (unsigned short *)vc->vc_origin; - break; - case 3: /* include scrollback */ - flush_scrollback(vc); - fallthrough; - case 2: /* erase whole display */ - vc_uniscr_clear_lines(vc, 0, vc->vc_rows); - count = vc->vc_cols * vc->vc_rows; - start = (unsigned short *)vc->vc_origin; - break; - default: - return; + case CSI_J_CURSOR_TO_END: + vc_uniscr_clear_line(vc, vc->state.x, + vc->vc_cols - vc->state.x); + vc_uniscr_clear_lines(vc, vc->state.y + 1, + vc->vc_rows - vc->state.y - 1); + count = (vc->vc_scr_end - vc->vc_pos) >> 1; + start = (unsigned short *)vc->vc_pos; + break; + case CSI_J_START_TO_CURSOR: + vc_uniscr_clear_line(vc, 0, vc->state.x + 1); + vc_uniscr_clear_lines(vc, 0, vc->state.y); + count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1; + start = (unsigned short *)vc->vc_origin; + break; + case CSI_J_FULL: + flush_scrollback(vc); + fallthrough; + case CSI_J_VISIBLE: + vc_uniscr_clear_lines(vc, 0, vc->vc_rows); + count = vc->vc_cols * vc->vc_rows; + start = (unsigned short *)vc->vc_origin; + break; + default: + return; } scr_memsetw(start, vc->vc_video_erase_char, 2 * count); if (con_should_update(vc)) @@ -1540,27 +1529,33 @@ static void csi_J(struct vc_data *vc, int vpar) vc->vc_need_wrap = 0; } -static void csi_K(struct vc_data *vc, int vpar) +enum { + CSI_K_CURSOR_TO_LINEEND = 0, + CSI_K_LINESTART_TO_CURSOR = 1, + CSI_K_LINE = 2, +}; + +static void csi_K(struct vc_data *vc) { unsigned int count; unsigned short *start = (unsigned short *)vc->vc_pos; int offset; - switch (vpar) { - case 0: /* erase from cursor to end of line */ - offset = 0; - count = vc->vc_cols - vc->state.x; - break; - case 1: /* erase from start of line to cursor */ - offset = -vc->state.x; - count = vc->state.x + 1; - break; - case 2: /* erase whole line */ - offset = -vc->state.x; - count = vc->vc_cols; - break; - default: - return; + switch (vc->vc_par[0]) { + case CSI_K_CURSOR_TO_LINEEND: + offset = 0; + count = vc->vc_cols - vc->state.x; + break; + case CSI_K_LINESTART_TO_CURSOR: + offset = -vc->state.x; + count = vc->state.x + 1; + break; + case CSI_K_LINE: + offset = -vc->state.x; + count = vc->vc_cols; + break; + default: + return; } vc_uniscr_clear_line(vc, vc->state.x + offset, count); scr_memsetw(start + offset, vc->vc_video_erase_char, 2 * count); @@ -1569,20 +1564,15 @@ static void csi_K(struct vc_data *vc, int vpar) do_update_region(vc, (unsigned long)(start + offset), count); } -/* erase the following vpar positions */ -static void csi_X(struct vc_data *vc, unsigned int vpar) +/* erase the following count positions */ +static void csi_X(struct vc_data *vc) { /* not vt100? */ - unsigned int count; - - if (!vpar) - vpar++; - - count = min(vpar, vc->vc_cols - vc->state.x); + unsigned int count = clamp(vc->vc_par[0], 1, vc->vc_cols - vc->state.x); vc_uniscr_clear_line(vc, vc->state.x, count); scr_memsetw((unsigned short *)vc->vc_pos, vc->vc_video_erase_char, 2 * count); if (con_should_update(vc)) - vc->vc_sw->con_clear(vc, vc->state.y, vc->state.x, 1, count); + vc->vc_sw->con_clear(vc, vc->state.y, vc->state.x, count); vc->vc_need_wrap = 0; } @@ -1598,7 +1588,7 @@ static void default_attr(struct vc_data *vc) struct rgb { u8 r; u8 g; u8 b; }; -static void rgb_from_256(int i, struct rgb *c) +static void rgb_from_256(unsigned int i, struct rgb *c) { if (i < 8) { /* Standard colours. */ c->r = i&1 ? 0xaa : 0x00; @@ -1609,9 +1599,12 @@ static void rgb_from_256(int i, struct rgb *c) c->g = i&2 ? 0xff : 0x55; c->b = i&4 ? 0xff : 0x55; } else if (i < 232) { /* 6x6x6 colour cube. */ - c->r = (i - 16) / 36 * 85 / 2; - c->g = (i - 16) / 6 % 6 * 85 / 2; - c->b = (i - 16) % 6 * 85 / 2; + i -= 16; + c->b = i % 6 * 255 / 6; + i /= 6; + c->g = i % 6 * 255 / 6; + i /= 6; + c->r = i * 255 / 6; } else /* Grayscale ramp. */ c->r = c->g = c->b = i * 10 - 2312; } @@ -1681,6 +1674,39 @@ static int vc_t416_color(struct vc_data *vc, int i, return i; } +enum { + CSI_m_DEFAULT = 0, + CSI_m_BOLD = 1, + CSI_m_HALF_BRIGHT = 2, + CSI_m_ITALIC = 3, + CSI_m_UNDERLINE = 4, + CSI_m_BLINK = 5, + CSI_m_REVERSE = 7, + CSI_m_PRI_FONT = 10, + CSI_m_ALT_FONT1 = 11, + CSI_m_ALT_FONT2 = 12, + CSI_m_DOUBLE_UNDERLINE = 21, + CSI_m_NORMAL_INTENSITY = 22, + CSI_m_NO_ITALIC = 23, + CSI_m_NO_UNDERLINE = 24, + CSI_m_NO_BLINK = 25, + CSI_m_NO_REVERSE = 27, + CSI_m_FG_COLOR_BEG = 30, + CSI_m_FG_COLOR_END = 37, + CSI_m_FG_COLOR = 38, + CSI_m_DEFAULT_FG_COLOR = 39, + CSI_m_BG_COLOR_BEG = 40, + CSI_m_BG_COLOR_END = 47, + CSI_m_BG_COLOR = 48, + CSI_m_DEFAULT_BG_COLOR = 49, + CSI_m_BRIGHT_FG_COLOR_BEG = 90, + CSI_m_BRIGHT_FG_COLOR_END = 97, + CSI_m_BRIGHT_FG_COLOR_OFF = CSI_m_BRIGHT_FG_COLOR_BEG - CSI_m_FG_COLOR_BEG, + CSI_m_BRIGHT_BG_COLOR_BEG = 100, + CSI_m_BRIGHT_BG_COLOR_END = 107, + CSI_m_BRIGHT_BG_COLOR_OFF = CSI_m_BRIGHT_BG_COLOR_BEG - CSI_m_BG_COLOR_BEG, +}; + /* console_lock is held */ static void csi_m(struct vc_data *vc) { @@ -1688,33 +1714,33 @@ static void csi_m(struct vc_data *vc) for (i = 0; i <= vc->vc_npar; i++) switch (vc->vc_par[i]) { - case 0: /* all attributes off */ + case CSI_m_DEFAULT: /* all attributes off */ default_attr(vc); break; - case 1: + case CSI_m_BOLD: vc->state.intensity = VCI_BOLD; break; - case 2: + case CSI_m_HALF_BRIGHT: vc->state.intensity = VCI_HALF_BRIGHT; break; - case 3: + case CSI_m_ITALIC: vc->state.italic = true; break; - case 21: + case CSI_m_DOUBLE_UNDERLINE: /* * No console drivers support double underline, so * convert it to a single underline. */ - case 4: + case CSI_m_UNDERLINE: vc->state.underline = true; break; - case 5: + case CSI_m_BLINK: vc->state.blink = true; break; - case 7: + case CSI_m_REVERSE: vc->state.reverse = true; break; - case 10: /* ANSI X3.64-1979 (SCO-ish?) + case CSI_m_PRI_FONT: /* ANSI X3.64-1979 (SCO-ish?) * Select primary font, don't display control chars if * defined, don't set bit 8 on output. */ @@ -1722,7 +1748,7 @@ static void csi_m(struct vc_data *vc) vc->vc_disp_ctrl = 0; vc->vc_toggle_meta = 0; break; - case 11: /* ANSI X3.64-1979 (SCO-ish?) + case CSI_m_ALT_FONT1: /* ANSI X3.64-1979 (SCO-ish?) * Select first alternate font, lets chars < 32 be * displayed as ROM chars. */ @@ -1730,7 +1756,7 @@ static void csi_m(struct vc_data *vc) vc->vc_disp_ctrl = 1; vc->vc_toggle_meta = 0; break; - case 12: /* ANSI X3.64-1979 (SCO-ish?) + case CSI_m_ALT_FONT2: /* ANSI X3.64-1979 (SCO-ish?) * Select second alternate font, toggle high bit * before displaying as ROM char. */ @@ -1738,47 +1764,51 @@ static void csi_m(struct vc_data *vc) vc->vc_disp_ctrl = 1; vc->vc_toggle_meta = 1; break; - case 22: + case CSI_m_NORMAL_INTENSITY: vc->state.intensity = VCI_NORMAL; break; - case 23: + case CSI_m_NO_ITALIC: vc->state.italic = false; break; - case 24: + case CSI_m_NO_UNDERLINE: vc->state.underline = false; break; - case 25: + case CSI_m_NO_BLINK: vc->state.blink = false; break; - case 27: + case CSI_m_NO_REVERSE: vc->state.reverse = false; break; - case 38: + case CSI_m_FG_COLOR: i = vc_t416_color(vc, i, rgb_foreground); break; - case 48: + case CSI_m_BG_COLOR: i = vc_t416_color(vc, i, rgb_background); break; - case 39: + case CSI_m_DEFAULT_FG_COLOR: vc->state.color = (vc->vc_def_color & 0x0f) | (vc->state.color & 0xf0); break; - case 49: + case CSI_m_DEFAULT_BG_COLOR: vc->state.color = (vc->vc_def_color & 0xf0) | (vc->state.color & 0x0f); break; - default: - if (vc->vc_par[i] >= 90 && vc->vc_par[i] <= 107) { - if (vc->vc_par[i] < 100) - vc->state.intensity = VCI_BOLD; - vc->vc_par[i] -= 60; - } - if (vc->vc_par[i] >= 30 && vc->vc_par[i] <= 37) - vc->state.color = color_table[vc->vc_par[i] - 30] - | (vc->state.color & 0xf0); - else if (vc->vc_par[i] >= 40 && vc->vc_par[i] <= 47) - vc->state.color = (color_table[vc->vc_par[i] - 40] << 4) - | (vc->state.color & 0x0f); + case CSI_m_BRIGHT_FG_COLOR_BEG ... CSI_m_BRIGHT_FG_COLOR_END: + vc->state.intensity = VCI_BOLD; + vc->vc_par[i] -= CSI_m_BRIGHT_FG_COLOR_OFF; + fallthrough; + case CSI_m_FG_COLOR_BEG ... CSI_m_FG_COLOR_END: + vc->vc_par[i] -= CSI_m_FG_COLOR_BEG; + vc->state.color = color_table[vc->vc_par[i]] | + (vc->state.color & 0xf0); + break; + case CSI_m_BRIGHT_BG_COLOR_BEG ... CSI_m_BRIGHT_BG_COLOR_END: + vc->vc_par[i] -= CSI_m_BRIGHT_BG_COLOR_OFF; + fallthrough; + case CSI_m_BG_COLOR_BEG ... CSI_m_BG_COLOR_END: + vc->vc_par[i] -= CSI_m_BG_COLOR_BEG; + vc->state.color = (color_table[vc->vc_par[i]] << 4) | + (vc->state.color & 0x0f); break; } update_attr(vc); @@ -1832,133 +1862,175 @@ int mouse_reporting(void) return vc_cons[fg_console].d->vc_report_mouse; } +enum { + CSI_DEC_hl_CURSOR_KEYS = 1, /* CKM: cursor keys send ^[Ox/^[[x */ + CSI_DEC_hl_132_COLUMNS = 3, /* COLM: 80/132 mode switch */ + CSI_DEC_hl_REVERSE_VIDEO = 5, /* SCNM */ + CSI_DEC_hl_ORIGIN_MODE = 6, /* OM: origin relative/absolute */ + CSI_DEC_hl_AUTOWRAP = 7, /* AWM */ + CSI_DEC_hl_AUTOREPEAT = 8, /* ARM */ + CSI_DEC_hl_MOUSE_X10 = 9, + CSI_DEC_hl_SHOW_CURSOR = 25, /* TCEM */ + CSI_DEC_hl_MOUSE_VT200 = 1000, +}; + /* console_lock is held */ -static void set_mode(struct vc_data *vc, int on_off) +static void csi_DEC_hl(struct vc_data *vc, bool on_off) { - int i; + unsigned int i; for (i = 0; i <= vc->vc_npar; i++) - if (vc->vc_priv == EPdec) { - switch(vc->vc_par[i]) { /* DEC private modes set/reset */ - case 1: /* Cursor keys send ^[Ox/^[[x */ - if (on_off) - set_kbd(vc, decckm); - else - clr_kbd(vc, decckm); - break; - case 3: /* 80/132 mode switch unimplemented */ + switch (vc->vc_par[i]) { + case CSI_DEC_hl_CURSOR_KEYS: + if (on_off) + set_kbd(vc, decckm); + else + clr_kbd(vc, decckm); + break; + case CSI_DEC_hl_132_COLUMNS: /* unimplemented */ #if 0 - vc_resize(deccolm ? 132 : 80, vc->vc_rows); - /* this alone does not suffice; some user mode - utility has to change the hardware regs */ + vc_resize(deccolm ? 132 : 80, vc->vc_rows); + /* this alone does not suffice; some user mode + utility has to change the hardware regs */ #endif - break; - case 5: /* Inverted screen on/off */ - if (vc->vc_decscnm != on_off) { - vc->vc_decscnm = on_off; - invert_screen(vc, 0, - vc->vc_screenbuf_size, - false); - update_attr(vc); - } - break; - case 6: /* Origin relative/absolute */ - vc->vc_decom = on_off; - gotoxay(vc, 0, 0); - break; - case 7: /* Autowrap on/off */ - vc->vc_decawm = on_off; - break; - case 8: /* Autorepeat on/off */ - if (on_off) - set_kbd(vc, decarm); - else - clr_kbd(vc, decarm); - break; - case 9: - vc->vc_report_mouse = on_off ? 1 : 0; - break; - case 25: /* Cursor on/off */ - vc->vc_deccm = on_off; - break; - case 1000: - vc->vc_report_mouse = on_off ? 2 : 0; - break; - } - } else { - switch(vc->vc_par[i]) { /* ANSI modes set/reset */ - case 3: /* Monitor (display ctrls) */ - vc->vc_disp_ctrl = on_off; - break; - case 4: /* Insert Mode on/off */ - vc->vc_decim = on_off; - break; - case 20: /* Lf, Enter == CrLf/Lf */ - if (on_off) - set_kbd(vc, lnm); - else - clr_kbd(vc, lnm); - break; + break; + case CSI_DEC_hl_REVERSE_VIDEO: + if (vc->vc_decscnm != on_off) { + vc->vc_decscnm = on_off; + invert_screen(vc, 0, vc->vc_screenbuf_size, + false); + update_attr(vc); } + break; + case CSI_DEC_hl_ORIGIN_MODE: + vc->vc_decom = on_off; + gotoxay(vc, 0, 0); + break; + case CSI_DEC_hl_AUTOWRAP: + vc->vc_decawm = on_off; + break; + case CSI_DEC_hl_AUTOREPEAT: + if (on_off) + set_kbd(vc, decarm); + else + clr_kbd(vc, decarm); + break; + case CSI_DEC_hl_MOUSE_X10: + vc->vc_report_mouse = on_off ? 1 : 0; + break; + case CSI_DEC_hl_SHOW_CURSOR: + vc->vc_deccm = on_off; + break; + case CSI_DEC_hl_MOUSE_VT200: + vc->vc_report_mouse = on_off ? 2 : 0; + break; } } +enum { + CSI_hl_DISPLAY_CTRL = 3, /* handle ansi control chars */ + CSI_hl_INSERT = 4, /* IRM: insert/replace */ + CSI_hl_AUTO_NL = 20, /* LNM: Enter == CrLf/Lf */ +}; + /* console_lock is held */ -static void setterm_command(struct vc_data *vc) +static void csi_hl(struct vc_data *vc, bool on_off) +{ + unsigned int i; + + for (i = 0; i <= vc->vc_npar; i++) + switch (vc->vc_par[i]) { /* ANSI modes set/reset */ + case CSI_hl_DISPLAY_CTRL: + vc->vc_disp_ctrl = on_off; + break; + case CSI_hl_INSERT: + vc->vc_decim = on_off; + break; + case CSI_hl_AUTO_NL: + if (on_off) + set_kbd(vc, lnm); + else + clr_kbd(vc, lnm); + break; + } +} + +enum CSI_right_square_bracket { + CSI_RSB_COLOR_FOR_UNDERLINE = 1, + CSI_RSB_COLOR_FOR_HALF_BRIGHT = 2, + CSI_RSB_MAKE_CUR_COLOR_DEFAULT = 8, + CSI_RSB_BLANKING_INTERVAL = 9, + CSI_RSB_BELL_FREQUENCY = 10, + CSI_RSB_BELL_DURATION = 11, + CSI_RSB_BRING_CONSOLE_TO_FRONT = 12, + CSI_RSB_UNBLANK = 13, + CSI_RSB_VESA_OFF_INTERVAL = 14, + CSI_RSB_BRING_PREV_CONSOLE_TO_FRONT = 15, + CSI_RSB_CURSOR_BLINK_INTERVAL = 16, +}; + +/* + * csi_RSB - csi+] (Right Square Bracket) handler + * + * These are linux console private sequences. + * + * console_lock is held + */ +static void csi_RSB(struct vc_data *vc) { switch (vc->vc_par[0]) { - case 1: /* set color for underline mode */ + case CSI_RSB_COLOR_FOR_UNDERLINE: if (vc->vc_can_do_color && vc->vc_par[1] < 16) { vc->vc_ulcolor = color_table[vc->vc_par[1]]; if (vc->state.underline) update_attr(vc); } break; - case 2: /* set color for half intensity mode */ + case CSI_RSB_COLOR_FOR_HALF_BRIGHT: if (vc->vc_can_do_color && vc->vc_par[1] < 16) { vc->vc_halfcolor = color_table[vc->vc_par[1]]; if (vc->state.intensity == VCI_HALF_BRIGHT) update_attr(vc); } break; - case 8: /* store colors as defaults */ + case CSI_RSB_MAKE_CUR_COLOR_DEFAULT: vc->vc_def_color = vc->vc_attr; if (vc->vc_hi_font_mask == 0x100) vc->vc_def_color >>= 1; default_attr(vc); update_attr(vc); break; - case 9: /* set blanking interval */ + case CSI_RSB_BLANKING_INTERVAL: blankinterval = min(vc->vc_par[1], 60U) * 60; poke_blanked_console(); break; - case 10: /* set bell frequency in Hz */ + case CSI_RSB_BELL_FREQUENCY: if (vc->vc_npar >= 1) vc->vc_bell_pitch = vc->vc_par[1]; else vc->vc_bell_pitch = DEFAULT_BELL_PITCH; break; - case 11: /* set bell duration in msec */ + case CSI_RSB_BELL_DURATION: if (vc->vc_npar >= 1) vc->vc_bell_duration = (vc->vc_par[1] < 2000) ? msecs_to_jiffies(vc->vc_par[1]) : 0; else vc->vc_bell_duration = DEFAULT_BELL_DURATION; break; - case 12: /* bring specified console to the front */ + case CSI_RSB_BRING_CONSOLE_TO_FRONT: if (vc->vc_par[1] >= 1 && vc_cons_allocated(vc->vc_par[1] - 1)) set_console(vc->vc_par[1] - 1); break; - case 13: /* unblank the screen */ + case CSI_RSB_UNBLANK: poke_blanked_console(); break; - case 14: /* set vesa powerdown interval */ + case CSI_RSB_VESA_OFF_INTERVAL: vesa_off_interval = min(vc->vc_par[1], 60U) * 60 * HZ; break; - case 15: /* activate the previous console */ + case CSI_RSB_BRING_PREV_CONSOLE_TO_FRONT: set_console(last_console); break; - case 16: /* set cursor blink duration in msec */ + case CSI_RSB_CURSOR_BLINK_INTERVAL: if (vc->vc_npar >= 1 && vc->vc_par[1] >= 50 && vc->vc_par[1] <= USHRT_MAX) vc->vc_cur_blink_ms = vc->vc_par[1]; @@ -1971,41 +2043,32 @@ static void setterm_command(struct vc_data *vc) /* console_lock is held */ static void csi_at(struct vc_data *vc, unsigned int nr) { - if (nr > vc->vc_cols - vc->state.x) - nr = vc->vc_cols - vc->state.x; - else if (!nr) - nr = 1; + nr = clamp(nr, 1, vc->vc_cols - vc->state.x); insert_char(vc, nr); } /* console_lock is held */ -static void csi_L(struct vc_data *vc, unsigned int nr) +static void csi_L(struct vc_data *vc) { - if (nr > vc->vc_rows - vc->state.y) - nr = vc->vc_rows - vc->state.y; - else if (!nr) - nr = 1; + unsigned int nr = clamp(vc->vc_par[0], 1, vc->vc_rows - vc->state.y); + con_scroll(vc, vc->state.y, vc->vc_bottom, SM_DOWN, nr); vc->vc_need_wrap = 0; } /* console_lock is held */ -static void csi_P(struct vc_data *vc, unsigned int nr) +static void csi_P(struct vc_data *vc) { - if (nr > vc->vc_cols - vc->state.x) - nr = vc->vc_cols - vc->state.x; - else if (!nr) - nr = 1; + unsigned int nr = clamp(vc->vc_par[0], 1, vc->vc_cols - vc->state.x); + delete_char(vc, nr); } /* console_lock is held */ -static void csi_M(struct vc_data *vc, unsigned int nr) +static void csi_M(struct vc_data *vc) { - if (nr > vc->vc_rows - vc->state.y) - nr = vc->vc_rows - vc->state.y; - else if (!nr) - nr=1; + unsigned int nr = clamp(vc->vc_par[0], 1, vc->vc_rows - vc->state.y); + con_scroll(vc, vc->state.y, vc->vc_bottom, SM_UP, nr); vc->vc_need_wrap = 0; } @@ -2028,9 +2091,48 @@ static void restore_cur(struct vc_data *vc) vc->vc_need_wrap = 0; } -enum { ESnormal, ESesc, ESsquare, ESgetpars, ESfunckey, - EShash, ESsetG0, ESsetG1, ESpercent, EScsiignore, ESnonstd, - ESpalette, ESosc, ESapc, ESpm, ESdcs }; +/** + * enum vc_ctl_state - control characters state of a vt + * + * @ESnormal: initial state, no control characters parsed + * @ESesc: ESC parsed + * @ESsquare: CSI parsed -- modifiers/parameters/ctrl chars expected + * @ESgetpars: CSI parsed -- parameters/ctrl chars expected + * @ESfunckey: CSI [ parsed + * @EShash: ESC # parsed + * @ESsetG0: ESC ( parsed + * @ESsetG1: ESC ) parsed + * @ESpercent: ESC % parsed + * @EScsiignore: CSI [0x20-0x3f] parsed + * @ESnonstd: OSC parsed + * @ESpalette: OSC P parsed + * @ESosc: OSC [0-9] parsed + * @ESANSI_first: first state for ignoring ansi control sequences + * @ESapc: ESC _ parsed + * @ESpm: ESC ^ parsed + * @ESdcs: ESC P parsed + * @ESANSI_last: last state for ignoring ansi control sequences + */ +enum vc_ctl_state { + ESnormal, + ESesc, + ESsquare, + ESgetpars, + ESfunckey, + EShash, + ESsetG0, + ESsetG1, + ESpercent, + EScsiignore, + ESnonstd, + ESpalette, + ESosc, + ESANSI_first = ESosc, + ESapc, + ESpm, + ESdcs, + ESANSI_last = ESdcs, +}; /* console_lock is held (except via vc_init()) */ static void reset_terminal(struct vc_data *vc, int do_clear) @@ -2078,10 +2180,10 @@ static void reset_terminal(struct vc_data *vc, int do_clear) gotoxy(vc, 0, 0); save_cur(vc); if (do_clear) - csi_J(vc, 2); + csi_J(vc, CSI_J_VISIBLE); } -static void vc_setGx(struct vc_data *vc, unsigned int which, int c) +static void vc_setGx(struct vc_data *vc, unsigned int which, u8 c) { unsigned char *charset = &vc->state.Gx_charset[which]; @@ -2104,36 +2206,54 @@ static void vc_setGx(struct vc_data *vc, unsigned int which, int c) vc->vc_translate = set_translate(*charset, vc); } -/* is this state an ANSI control string? */ -static bool ansi_control_string(unsigned int state) +static bool ansi_control_string(enum vc_ctl_state state) { - if (state == ESosc || state == ESapc || state == ESpm || state == ESdcs) - return true; - return false; + return state >= ESANSI_first && state <= ESANSI_last; } -/* console_lock is held */ -static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) +enum { + ASCII_NULL = 0, + ASCII_BELL = 7, + ASCII_BACKSPACE = 8, + ASCII_IGNORE_FIRST = ASCII_BACKSPACE, + ASCII_HTAB = 9, + ASCII_LINEFEED = 10, + ASCII_VTAB = 11, + ASCII_FORMFEED = 12, + ASCII_CAR_RET = 13, + ASCII_IGNORE_LAST = ASCII_CAR_RET, + ASCII_SHIFTOUT = 14, + ASCII_SHIFTIN = 15, + ASCII_CANCEL = 24, + ASCII_SUBSTITUTE = 26, + ASCII_ESCAPE = 27, + ASCII_CSI_IGNORE_FIRST = ' ', /* 0x2x, 0x3a and 0x3c - 0x3f */ + ASCII_CSI_IGNORE_LAST = '?', + ASCII_DEL = 127, + ASCII_EXT_CSI = 128 + ASCII_ESCAPE, +}; + +/* + * Handle ascii characters in control sequences and change states accordingly. + * E.g. ESC sets the state of vc to ESesc. + * + * Returns: true if @c handled. + */ +static bool handle_ascii(struct tty_struct *tty, struct vc_data *vc, u8 c) { - /* - * Control characters can be used in the _middle_ - * of an escape sequence, aside from ANSI control strings. - */ - if (ansi_control_string(vc->vc_state) && c >= 8 && c <= 13) - return; switch (c) { - case 0: - return; - case 7: + case ASCII_NULL: + return true; + case ASCII_BELL: if (ansi_control_string(vc->vc_state)) vc->vc_state = ESnormal; else if (vc->vc_bell_duration) kd_mksound(vc->vc_bell_pitch, vc->vc_bell_duration); - return; - case 8: + return true; + case ASCII_BACKSPACE: bs(vc); - return; - case 9: + return true; + case ASCII_HTAB: vc->vc_pos -= (vc->state.x << 1); vc->state.x = find_next_bit(vc->vc_tab_stop, @@ -2144,119 +2264,330 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) vc->vc_pos += (vc->state.x << 1); notify_write(vc, '\t'); - return; - case 10: case 11: case 12: + return true; + case ASCII_LINEFEED: + case ASCII_VTAB: + case ASCII_FORMFEED: lf(vc); if (!is_kbd(vc, lnm)) - return; + return true; fallthrough; - case 13: + case ASCII_CAR_RET: cr(vc); - return; - case 14: + return true; + case ASCII_SHIFTOUT: vc->state.charset = 1; vc->vc_translate = set_translate(vc->state.Gx_charset[1], vc); vc->vc_disp_ctrl = 1; - return; - case 15: + return true; + case ASCII_SHIFTIN: vc->state.charset = 0; vc->vc_translate = set_translate(vc->state.Gx_charset[0], vc); vc->vc_disp_ctrl = 0; - return; - case 24: case 26: + return true; + case ASCII_CANCEL: + case ASCII_SUBSTITUTE: vc->vc_state = ESnormal; - return; - case 27: + return true; + case ASCII_ESCAPE: vc->vc_state = ESesc; - return; - case 127: + return true; + case ASCII_DEL: del(vc); - return; - case 128+27: + return true; + case ASCII_EXT_CSI: vc->vc_state = ESsquare; - return; + return true; } - switch(vc->vc_state) { - case ESesc: - vc->vc_state = ESnormal; - switch (c) { - case '[': - vc->vc_state = ESsquare; - return; - case ']': - vc->vc_state = ESnonstd; - return; - case '_': - vc->vc_state = ESapc; - return; - case '^': - vc->vc_state = ESpm; - return; - case '%': - vc->vc_state = ESpercent; - return; - case 'E': - cr(vc); - lf(vc); - return; - case 'M': - ri(vc); - return; - case 'D': - lf(vc); - return; - case 'H': - if (vc->state.x < VC_TABSTOPS_COUNT) - set_bit(vc->state.x, vc->vc_tab_stop); - return; - case 'P': - vc->vc_state = ESdcs; - return; - case 'Z': + + return false; +} + +/* + * Handle a character (@c) following an ESC (when @vc is in the ESesc state). + * E.g. previous ESC with @c == '[' here yields the ESsquare state (that is: + * CSI). + */ +static void handle_esc(struct tty_struct *tty, struct vc_data *vc, u8 c) +{ + vc->vc_state = ESnormal; + switch (c) { + case '[': + vc->vc_state = ESsquare; + break; + case ']': + vc->vc_state = ESnonstd; + break; + case '_': + vc->vc_state = ESapc; + break; + case '^': + vc->vc_state = ESpm; + break; + case '%': + vc->vc_state = ESpercent; + break; + case 'E': + cr(vc); + lf(vc); + break; + case 'M': + ri(vc); + break; + case 'D': + lf(vc); + break; + case 'H': + if (vc->state.x < VC_TABSTOPS_COUNT) + set_bit(vc->state.x, vc->vc_tab_stop); + break; + case 'P': + vc->vc_state = ESdcs; + break; + case 'Z': + respond_ID(tty); + break; + case '7': + save_cur(vc); + break; + case '8': + restore_cur(vc); + break; + case '(': + vc->vc_state = ESsetG0; + break; + case ')': + vc->vc_state = ESsetG1; + break; + case '#': + vc->vc_state = EShash; + break; + case 'c': + reset_terminal(vc, 1); + break; + case '>': /* Numeric keypad */ + clr_kbd(vc, kbdapplic); + break; + case '=': /* Appl. keypad */ + set_kbd(vc, kbdapplic); + break; + } +} + +/* + * Handle special DEC control sequences ("ESC [ ? parameters char"). Parameters + * are in @vc->vc_par and the char is in @c here. + */ +static void csi_DEC(struct tty_struct *tty, struct vc_data *vc, u8 c) +{ + switch (c) { + case 'h': + csi_DEC_hl(vc, true); + break; + case 'l': + csi_DEC_hl(vc, false); + break; + case 'c': + if (vc->vc_par[0]) + vc->vc_cursor_type = CUR_MAKE(vc->vc_par[0], + vc->vc_par[1], + vc->vc_par[2]); + else + vc->vc_cursor_type = cur_default; + break; + case 'm': + clear_selection(); + if (vc->vc_par[0]) + vc->vc_complement_mask = vc->vc_par[0] << 8 | vc->vc_par[1]; + else + vc->vc_complement_mask = vc->vc_s_complement_mask; + break; + case 'n': + if (vc->vc_par[0] == 5) + status_report(tty); + else if (vc->vc_par[0] == 6) + cursor_report(vc, tty); + break; + } +} + +/* + * Handle Control Sequence Introducer control characters. That is + * "ESC [ parameters char". Parameters are in @vc->vc_par and the char is in + * @c here. + */ +static void csi_ECMA(struct tty_struct *tty, struct vc_data *vc, u8 c) +{ + switch (c) { + case 'G': + case '`': + if (vc->vc_par[0]) + vc->vc_par[0]--; + gotoxy(vc, vc->vc_par[0], vc->state.y); + break; + case 'A': + if (!vc->vc_par[0]) + vc->vc_par[0]++; + gotoxy(vc, vc->state.x, vc->state.y - vc->vc_par[0]); + break; + case 'B': + case 'e': + if (!vc->vc_par[0]) + vc->vc_par[0]++; + gotoxy(vc, vc->state.x, vc->state.y + vc->vc_par[0]); + break; + case 'C': + case 'a': + if (!vc->vc_par[0]) + vc->vc_par[0]++; + gotoxy(vc, vc->state.x + vc->vc_par[0], vc->state.y); + break; + case 'D': + if (!vc->vc_par[0]) + vc->vc_par[0]++; + gotoxy(vc, vc->state.x - vc->vc_par[0], vc->state.y); + break; + case 'E': + if (!vc->vc_par[0]) + vc->vc_par[0]++; + gotoxy(vc, 0, vc->state.y + vc->vc_par[0]); + break; + case 'F': + if (!vc->vc_par[0]) + vc->vc_par[0]++; + gotoxy(vc, 0, vc->state.y - vc->vc_par[0]); + break; + case 'd': + if (vc->vc_par[0]) + vc->vc_par[0]--; + gotoxay(vc, vc->state.x ,vc->vc_par[0]); + break; + case 'H': + case 'f': + if (vc->vc_par[0]) + vc->vc_par[0]--; + if (vc->vc_par[1]) + vc->vc_par[1]--; + gotoxay(vc, vc->vc_par[1], vc->vc_par[0]); + break; + case 'J': + csi_J(vc, vc->vc_par[0]); + break; + case 'K': + csi_K(vc); + break; + case 'L': + csi_L(vc); + break; + case 'M': + csi_M(vc); + break; + case 'P': + csi_P(vc); + break; + case 'c': + if (!vc->vc_par[0]) respond_ID(tty); - return; - case '7': - save_cur(vc); - return; - case '8': - restore_cur(vc); - return; - case '(': - vc->vc_state = ESsetG0; - return; - case ')': - vc->vc_state = ESsetG1; - return; - case '#': - vc->vc_state = EShash; - return; - case 'c': - reset_terminal(vc, 1); - return; - case '>': /* Numeric keypad */ - clr_kbd(vc, kbdapplic); - return; - case '=': /* Appl. keypad */ - set_kbd(vc, kbdapplic); - return; + break; + case 'g': + if (!vc->vc_par[0] && vc->state.x < VC_TABSTOPS_COUNT) + set_bit(vc->state.x, vc->vc_tab_stop); + else if (vc->vc_par[0] == 3) + bitmap_zero(vc->vc_tab_stop, VC_TABSTOPS_COUNT); + break; + case 'h': + csi_hl(vc, true); + break; + case 'l': + csi_hl(vc, false); + break; + case 'm': + csi_m(vc); + break; + case 'n': + if (vc->vc_par[0] == 5) + status_report(tty); + else if (vc->vc_par[0] == 6) + cursor_report(vc, tty); + break; + case 'q': /* DECLL - but only 3 leds */ + /* map 0,1,2,3 to 0,1,2,4 */ + if (vc->vc_par[0] < 4) + vt_set_led_state(vc->vc_num, + (vc->vc_par[0] < 3) ? vc->vc_par[0] : 4); + break; + case 'r': + if (!vc->vc_par[0]) + vc->vc_par[0]++; + if (!vc->vc_par[1]) + vc->vc_par[1] = vc->vc_rows; + /* Minimum allowed region is 2 lines */ + if (vc->vc_par[0] < vc->vc_par[1] && + vc->vc_par[1] <= vc->vc_rows) { + vc->vc_top = vc->vc_par[0] - 1; + vc->vc_bottom = vc->vc_par[1]; + gotoxay(vc, 0, 0); } + break; + case 's': + save_cur(vc); + break; + case 'u': + restore_cur(vc); + break; + case 'X': + csi_X(vc); + break; + case '@': + csi_at(vc, vc->vc_par[0]); + break; + case ']': + csi_RSB(vc); + break; + } + +} + +static void vc_reset_params(struct vc_data *vc) +{ + memset(vc->vc_par, 0, sizeof(vc->vc_par)); + vc->vc_npar = 0; +} + +/* console_lock is held */ +static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c) +{ + /* + * Control characters can be used in the _middle_ + * of an escape sequence, aside from ANSI control strings. + */ + if (ansi_control_string(vc->vc_state) && c >= ASCII_IGNORE_FIRST && + c <= ASCII_IGNORE_LAST) + return; + + if (handle_ascii(tty, vc, c)) + return; + + switch(vc->vc_state) { + case ESesc: /* ESC */ + handle_esc(tty, vc, c); return; - case ESnonstd: - if (c=='P') { /* palette escape sequence */ - for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++) - vc->vc_par[vc->vc_npar] = 0; - vc->vc_npar = 0; + case ESnonstd: /* ESC ] aka OSC */ + switch (c) { + case 'P': /* palette escape sequence */ + vc_reset_params(vc); vc->vc_state = ESpalette; return; - } else if (c=='R') { /* reset palette */ + case 'R': /* reset palette */ reset_palette(vc); - vc->vc_state = ESnormal; - } else if (c>='0' && c<='9') + break; + case '0' ... '9': vc->vc_state = ESosc; - else - vc->vc_state = ESnormal; + return; + } + vc->vc_state = ESnormal; return; - case ESpalette: + case ESpalette: /* ESC ] P aka OSC P */ if (isxdigit(c)) { vc->vc_par[vc->vc_npar++] = hex_to_bin(c); if (vc->vc_npar == 7) { @@ -2273,16 +2604,14 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) } else vc->vc_state = ESnormal; return; - case ESsquare: - for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++) - vc->vc_par[vc->vc_npar] = 0; - vc->vc_npar = 0; + case ESsquare: /* ESC [ aka CSI, parameters or modifiers expected */ + vc_reset_params(vc); + vc->vc_state = ESgetpars; - if (c == '[') { /* Function key */ - vc->vc_state=ESfunckey; - return; - } switch (c) { + case '[': /* Function key */ + vc->vc_state = ESfunckey; + return; case '?': vc->vc_priv = EPdec; return; @@ -2298,182 +2627,44 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) } vc->vc_priv = EPecma; fallthrough; - case ESgetpars: - if (c == ';' && vc->vc_npar < NPAR - 1) { - vc->vc_npar++; - return; - } else if (c>='0' && c<='9') { + case ESgetpars: /* ESC [ aka CSI, parameters expected */ + switch (c) { + case ';': + if (vc->vc_npar < NPAR - 1) { + vc->vc_npar++; + return; + } + break; + case '0' ... '9': vc->vc_par[vc->vc_npar] *= 10; vc->vc_par[vc->vc_npar] += c - '0'; return; } - if (c >= 0x20 && c <= 0x3f) { /* 0x2x, 0x3a and 0x3c - 0x3f */ + if (c >= ASCII_CSI_IGNORE_FIRST && c <= ASCII_CSI_IGNORE_LAST) { vc->vc_state = EScsiignore; return; } + + /* parameters done, handle the control char @c */ + vc->vc_state = ESnormal; - switch(c) { - case 'h': - if (vc->vc_priv <= EPdec) - set_mode(vc, 1); - return; - case 'l': - if (vc->vc_priv <= EPdec) - set_mode(vc, 0); - return; - case 'c': - if (vc->vc_priv == EPdec) { - if (vc->vc_par[0]) - vc->vc_cursor_type = - CUR_MAKE(vc->vc_par[0], - vc->vc_par[1], - vc->vc_par[2]); - else - vc->vc_cursor_type = cur_default; - return; - } - break; - case 'm': - if (vc->vc_priv == EPdec) { - clear_selection(); - if (vc->vc_par[0]) - vc->vc_complement_mask = vc->vc_par[0] << 8 | vc->vc_par[1]; - else - vc->vc_complement_mask = vc->vc_s_complement_mask; - return; - } - break; - case 'n': - if (vc->vc_priv == EPecma) { - if (vc->vc_par[0] == 5) - status_report(tty); - else if (vc->vc_par[0] == 6) - cursor_report(vc, tty); - } - return; - } - if (vc->vc_priv != EPecma) { - vc->vc_priv = EPecma; - return; - } - switch(c) { - case 'G': case '`': - if (vc->vc_par[0]) - vc->vc_par[0]--; - gotoxy(vc, vc->vc_par[0], vc->state.y); - return; - case 'A': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - gotoxy(vc, vc->state.x, vc->state.y - vc->vc_par[0]); - return; - case 'B': case 'e': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - gotoxy(vc, vc->state.x, vc->state.y + vc->vc_par[0]); - return; - case 'C': case 'a': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - gotoxy(vc, vc->state.x + vc->vc_par[0], vc->state.y); - return; - case 'D': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - gotoxy(vc, vc->state.x - vc->vc_par[0], vc->state.y); - return; - case 'E': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - gotoxy(vc, 0, vc->state.y + vc->vc_par[0]); - return; - case 'F': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - gotoxy(vc, 0, vc->state.y - vc->vc_par[0]); - return; - case 'd': - if (vc->vc_par[0]) - vc->vc_par[0]--; - gotoxay(vc, vc->state.x ,vc->vc_par[0]); - return; - case 'H': case 'f': - if (vc->vc_par[0]) - vc->vc_par[0]--; - if (vc->vc_par[1]) - vc->vc_par[1]--; - gotoxay(vc, vc->vc_par[1], vc->vc_par[0]); - return; - case 'J': - csi_J(vc, vc->vc_par[0]); - return; - case 'K': - csi_K(vc, vc->vc_par[0]); - return; - case 'L': - csi_L(vc, vc->vc_par[0]); - return; - case 'M': - csi_M(vc, vc->vc_par[0]); - return; - case 'P': - csi_P(vc, vc->vc_par[0]); - return; - case 'c': - if (!vc->vc_par[0]) - respond_ID(tty); - return; - case 'g': - if (!vc->vc_par[0] && vc->state.x < VC_TABSTOPS_COUNT) - set_bit(vc->state.x, vc->vc_tab_stop); - else if (vc->vc_par[0] == 3) - bitmap_zero(vc->vc_tab_stop, VC_TABSTOPS_COUNT); - return; - case 'm': - csi_m(vc); - return; - case 'q': /* DECLL - but only 3 leds */ - /* map 0,1,2,3 to 0,1,2,4 */ - if (vc->vc_par[0] < 4) - vt_set_led_state(vc->vc_num, - (vc->vc_par[0] < 3) ? vc->vc_par[0] : 4); - return; - case 'r': - if (!vc->vc_par[0]) - vc->vc_par[0]++; - if (!vc->vc_par[1]) - vc->vc_par[1] = vc->vc_rows; - /* Minimum allowed region is 2 lines */ - if (vc->vc_par[0] < vc->vc_par[1] && - vc->vc_par[1] <= vc->vc_rows) { - vc->vc_top = vc->vc_par[0] - 1; - vc->vc_bottom = vc->vc_par[1]; - gotoxay(vc, 0, 0); - } - return; - case 's': - save_cur(vc); - return; - case 'u': - restore_cur(vc); - return; - case 'X': - csi_X(vc, vc->vc_par[0]); + + switch (vc->vc_priv) { + case EPdec: + csi_DEC(tty, vc, c); return; - case '@': - csi_at(vc, vc->vc_par[0]); + case EPecma: + csi_ECMA(tty, vc, c); return; - case ']': /* setterm functions */ - setterm_command(vc); + default: return; } - return; case EScsiignore: - if (c >= 0x20 && c <= 0x3f) + if (c >= ASCII_CSI_IGNORE_FIRST && c <= ASCII_CSI_IGNORE_LAST) return; vc->vc_state = ESnormal; return; - case ESpercent: + case ESpercent: /* ESC % */ vc->vc_state = ESnormal; switch (c) { case '@': /* defined in ISO 2022 */ @@ -2485,36 +2676,36 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c) return; } return; - case ESfunckey: + case ESfunckey: /* ESC [ [ aka CSI [ */ vc->vc_state = ESnormal; return; - case EShash: + case EShash: /* ESC # */ vc->vc_state = ESnormal; if (c == '8') { /* DEC screen alignment test. kludge :-) */ vc->vc_video_erase_char = (vc->vc_video_erase_char & 0xff00) | 'E'; - csi_J(vc, 2); + csi_J(vc, CSI_J_VISIBLE); vc->vc_video_erase_char = (vc->vc_video_erase_char & 0xff00) | ' '; do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2); } return; - case ESsetG0: + case ESsetG0: /* ESC ( */ vc_setGx(vc, 0, c); vc->vc_state = ESnormal; return; - case ESsetG1: + case ESsetG1: /* ESC ) */ vc_setGx(vc, 1, c); vc->vc_state = ESnormal; return; - case ESapc: + case ESapc: /* ESC _ */ return; - case ESosc: + case ESosc: /* ESC ] [0-9] aka OSC [0-9] */ return; - case ESpm: + case ESpm: /* ESC ^ */ return; - case ESdcs: + case ESdcs: /* ESC P */ return; default: vc->vc_state = ESnormal; @@ -2588,33 +2779,39 @@ static inline int vc_translate_ascii(const struct vc_data *vc, int c) /** - * vc_sanitize_unicode - Replace invalid Unicode code points with U+FFFD - * @c: the received character, or U+FFFD for invalid sequences. + * vc_sanitize_unicode - Replace invalid Unicode code points with ``U+FFFD`` + * @c: the received code point */ static inline int vc_sanitize_unicode(const int c) { - if ((c >= 0xd800 && c <= 0xdfff) || c == 0xfffe || c == 0xffff) + if (c >= 0xd800 && c <= 0xdfff) return 0xfffd; return c; } /** - * vc_translate_unicode - Combine UTF-8 into Unicode in @vc_utf_char + * vc_translate_unicode - Combine UTF-8 into Unicode in &vc_data.vc_utf_char * @vc: virtual console - * @c: character to translate - * @rescan: we return true if we need more (continuation) data + * @c: UTF-8 byte to translate + * @rescan: set to true iff @c wasn't consumed here and needs to be re-processed + * + * * &vc_data.vc_utf_char is the being-constructed Unicode code point. + * * &vc_data.vc_utf_count is the number of continuation bytes still expected to + * arrive. + * * &vc_data.vc_npar is the number of continuation bytes arrived so far. * - * @vc_utf_char is the being-constructed unicode character. - * @vc_utf_count is the number of continuation bytes still expected to arrive. - * @vc_npar is the number of continuation bytes arrived so far. + * Return: + * * %-1 - Input OK so far, @c consumed, further bytes expected. + * * %0xFFFD - Possibility 1: input invalid, @c may have been consumed (see + * desc. of @rescan). Possibility 2: input OK, @c consumed, + * ``U+FFFD`` is the resulting code point. ``U+FFFD`` is valid, + * ``REPLACEMENT CHARACTER``. + * * otherwise - Input OK, @c consumed, resulting code point returned. */ static int vc_translate_unicode(struct vc_data *vc, int c, bool *rescan) { - static const u32 utf8_length_changes[] = { - 0x0000007f, 0x000007ff, 0x0000ffff, - 0x001fffff, 0x03ffffff, 0x7fffffff - }; + static const u32 utf8_length_changes[] = {0x7f, 0x7ff, 0xffff, 0x10ffff}; /* Continuation byte received */ if ((c & 0xc0) == 0x80) { @@ -2660,14 +2857,7 @@ static int vc_translate_unicode(struct vc_data *vc, int c, bool *rescan) } else if ((c & 0xf8) == 0xf0) { vc->vc_utf_count = 3; vc->vc_utf_char = (c & 0x07); - } else if ((c & 0xfc) == 0xf8) { - vc->vc_utf_count = 4; - vc->vc_utf_char = (c & 0x03); - } else if ((c & 0xfe) == 0xfc) { - vc->vc_utf_count = 5; - vc->vc_utf_char = (c & 0x01); } else { - /* 254 and 255 are invalid */ return 0xfffd; } @@ -2711,9 +2901,13 @@ static bool vc_is_control(struct vc_data *vc, int tc, int c) * as cursor movement) and should not be displayed as a glyph unless * the disp_ctrl mode is explicitly enabled. */ - static const u32 CTRL_ACTION = 0x0d00ff81; + static const u32 CTRL_ACTION = BIT(ASCII_NULL) | + GENMASK(ASCII_SHIFTIN, ASCII_BELL) | BIT(ASCII_CANCEL) | + BIT(ASCII_SUBSTITUTE) | BIT(ASCII_ESCAPE); /* Cannot be overridden by disp_ctrl */ - static const u32 CTRL_ALWAYS = 0x0800f501; + static const u32 CTRL_ALWAYS = BIT(ASCII_NULL) | BIT(ASCII_BACKSPACE) | + BIT(ASCII_LINEFEED) | BIT(ASCII_SHIFTIN) | BIT(ASCII_SHIFTOUT) | + BIT(ASCII_CAR_RET) | BIT(ASCII_FORMFEED) | BIT(ASCII_ESCAPE); if (vc->vc_state != ESnormal) return true; @@ -2730,17 +2924,17 @@ static bool vc_is_control(struct vc_data *vc, int tc, int c) * useless without them; to display an arbitrary font position use the * direct-to-font zone in UTF-8 mode. */ - if (c < 32) { + if (c < BITS_PER_TYPE(CTRL_ALWAYS)) { if (vc->vc_disp_ctrl) return CTRL_ALWAYS & BIT(c); else return vc->vc_utf || (CTRL_ACTION & BIT(c)); } - if (c == 127 && !vc->vc_disp_ctrl) + if (c == ASCII_DEL && !vc->vc_disp_ctrl) return true; - if (c == 128 + 27) + if (c == ASCII_EXT_CSI) return true; return false; @@ -2852,7 +3046,7 @@ static int do_con_write(struct tty_struct *tty, const u8 *buf, int count) }; int c, tc, n = 0; unsigned int currcons; - struct vc_data *vc; + struct vc_data *vc = tty->driver_data; struct vt_notifier_param param; bool rescan; @@ -2860,13 +3054,6 @@ static int do_con_write(struct tty_struct *tty, const u8 *buf, int count) return count; console_lock(); - vc = tty->driver_data; - if (vc == NULL) { - pr_err("vt: argh, driver_data is NULL !\n"); - console_unlock(); - return 0; - } - currcons = vc->vc_num; if (!vc_cons_allocated(currcons)) { /* could this happen? */ @@ -2883,7 +3070,7 @@ static int do_con_write(struct tty_struct *tty, const u8 *buf, int count) param.vc = vc; while (!tty->flow.stopped && count) { - int orig = *buf; + u8 orig = *buf; buf++; n++; count--; @@ -2992,16 +3179,16 @@ struct tty_driver *console_driver; #ifdef CONFIG_VT_CONSOLE /** - * vt_kmsg_redirect() - Sets/gets the kernel message console - * @new: The new virtual terminal number or -1 if the console should stay - * unchanged + * vt_kmsg_redirect() - sets/gets the kernel message console + * @new: the new virtual terminal number or -1 if the console should stay + * unchanged * * By default, the kernel messages are always printed on the current virtual * console. However, the user may modify that default with the - * TIOCL_SETKMSGREDIRECT ioctl call. + * %TIOCL_SETKMSGREDIRECT ioctl call. * * This function sets the kernel message console to be @new. It returns the old - * virtual console number. The virtual terminal number 0 (both as parameter and + * virtual console number. The virtual terminal number %0 (both as parameter and * return value) means no redirection (i.e. always printed on the currently * active console). * @@ -3009,8 +3196,8 @@ struct tty_driver *console_driver; * value is not modified. You may use the macro vt_get_kmsg_redirect() in that * case to make the code more understandable. * - * When the kernel is compiled without CONFIG_VT_CONSOLE, this function ignores - * the parameter and always returns 0. + * When the kernel is compiled without %CONFIG_VT_CONSOLE, this function ignores + * the parameter and always returns %0. */ int vt_kmsg_redirect(int new) { @@ -3065,22 +3252,23 @@ static void vt_console_print(struct console *co, const char *b, unsigned count) cnt = 0; while (count--) { c = *b++; - if (c == 10 || c == 13 || c == 8 || vc->vc_need_wrap) { + if (c == ASCII_LINEFEED || c == ASCII_CAR_RET || + c == ASCII_BACKSPACE || vc->vc_need_wrap) { if (cnt && con_is_visible(vc)) vc->vc_sw->con_putcs(vc, start, cnt, vc->state.y, start_x); cnt = 0; - if (c == 8) { /* backspace */ + if (c == ASCII_BACKSPACE) { bs(vc); start = (ushort *)vc->vc_pos; start_x = vc->state.x; continue; } - if (c != 13) + if (c != ASCII_CAR_RET) lf(vc); cr(vc); start = (ushort *)vc->vc_pos; start_x = vc->state.x; - if (c == 10 || c == 13) + if (c == ASCII_LINEFEED || c == ASCII_CAR_RET) continue; } vc_uniscr_putc(vc, c); @@ -3144,6 +3332,8 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) { char type, data; char __user *p = (char __user *)arg; + void __user *param_aligned32 = (u32 __user *)arg + 1; + void __user *param = (void __user *)arg + 1; int lines; int ret; @@ -3157,8 +3347,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) case TIOCL_SETSEL: if (!capable(CAP_SYS_ADMIN)) return -EPERM; - return set_selection_user((struct tiocl_selection - __user *)(p+1), tty); + return set_selection_user(param, tty); case TIOCL_PASTESEL: if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -3171,10 +3360,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) case TIOCL_SELLOADLUT: if (!capable(CAP_SYS_ADMIN)) return -EPERM; - console_lock(); - ret = sel_loadlut(p); - console_unlock(); - break; + return sel_loadlut(param_aligned32); case TIOCL_GETSHIFTSTATE: /* * Make it possible to react to Shift+Mousebutton. Note that @@ -3190,10 +3376,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) console_unlock(); return put_user(data, p); case TIOCL_SETVESABLANK: - console_lock(); - ret = set_vesa_blanking(p); - console_unlock(); - break; + return set_vesa_blanking(param); case TIOCL_GETKMSGREDIRECT: data = vt_get_kmsg_redirect(); return put_user(data, p); @@ -3214,7 +3397,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) */ return fg_console; case TIOCL_SCROLLCONSOLE: - if (get_user(lines, (s32 __user *)(p+4))) + if (get_user(lines, (s32 __user *)param_aligned32)) return -EFAULT; /* @@ -3312,16 +3495,13 @@ static void con_start(struct tty_struct *tty) static void con_flush_chars(struct tty_struct *tty) { - struct vc_data *vc; + struct vc_data *vc = tty->driver_data; if (in_interrupt()) /* from flush_to_ldisc */ return; - /* if we race with con_close(), vt may be null */ console_lock(); - vc = tty->driver_data; - if (vc) - set_cursor(vc); + set_cursor(vc); console_unlock(); } @@ -3396,6 +3576,15 @@ static void con_cleanup(struct tty_struct *tty) tty_port_put(&vc->port); } +/* + * We can't deal with anything but the N_TTY ldisc, + * because we can sleep in our write() routine. + */ +static int con_ldisc_ok(struct tty_struct *tty, int ldisc) +{ + return ldisc == N_TTY ? 0 : -EINVAL; +} + static int default_color = 7; /* white */ static int default_italic_color = 2; // green (ASCII) static int default_underline_color = 3; // cyan (ASCII) @@ -3471,7 +3660,7 @@ static int __init con_init(void) vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT); INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); tty_port_init(&vc->port); - visual_init(vc, currcons, 1); + visual_init(vc, currcons, true); /* Assuming vc->vc_{cols,rows,screenbuf_size} are sane here. */ vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT); vc_init(vc, currcons || !vc->vc_sw->con_save_screen); @@ -3481,7 +3670,7 @@ static int __init con_init(void) set_origin(vc); save_screen(vc); gotoxy(vc, vc->state.x, vc->state.y); - csi_J(vc, 0); + csi_J(vc, CSI_J_CURSOR_TO_END); update_screen(vc); pr_info("Console: %s %s %dx%d\n", vc->vc_can_do_color ? "colour" : "mono", @@ -3515,6 +3704,7 @@ static const struct tty_operations con_ops = { .resize = vt_resize, .shutdown = con_shutdown, .cleanup = con_cleanup, + .ldisc_ok = con_ldisc_ok, }; static struct cdev vc0_cdev; @@ -3640,7 +3830,7 @@ static int do_bind_con_driver(const struct consw *csw, int first, int last, old_was_color = vc->vc_can_do_color; vc->vc_sw->con_deinit(vc); vc->vc_origin = (unsigned long)vc->vc_screenbuf; - visual_init(vc, i, 0); + visual_init(vc, i, false); set_origin(vc); update_attr(vc); @@ -3930,7 +4120,7 @@ static void vtconsole_deinit_device(struct con_driver *con) * RETURNS: zero if unbound, nonzero if bound * * Drivers can call this and if zero, they should release - * all resources allocated on con_startup() + * all resources allocated on &consw.con_startup() */ int con_is_bound(const struct consw *csw) { @@ -3970,15 +4160,9 @@ EXPORT_SYMBOL(con_is_visible); * Called when the console is taken over by the kernel debugger, this * function needs to save the current console state, then put the console * into a state suitable for the kernel debugger. - * - * RETURNS: - * Zero on success, nonzero if a failure occurred when trying to prepare - * the console for the debugger. */ -int con_debug_enter(struct vc_data *vc) +void con_debug_enter(struct vc_data *vc) { - int ret = 0; - saved_fg_console = fg_console; saved_last_console = last_console; saved_want_console = want_console; @@ -3987,7 +4171,7 @@ int con_debug_enter(struct vc_data *vc) vc->vc_mode = KD_TEXT; console_blanked = 0; if (vc->vc_sw->con_debug_enter) - ret = vc->vc_sw->con_debug_enter(vc); + vc->vc_sw->con_debug_enter(vc); #ifdef CONFIG_KGDB_KDB /* Set the initial LINES variable if it is not already set */ if (vc->vc_rows < 999) { @@ -4017,7 +4201,6 @@ int con_debug_enter(struct vc_data *vc) } } #endif /* CONFIG_KGDB_KDB */ - return ret; } EXPORT_SYMBOL_GPL(con_debug_enter); @@ -4026,15 +4209,10 @@ EXPORT_SYMBOL_GPL(con_debug_enter); * * Restore the console state to what it was before the kernel debugger * was invoked. - * - * RETURNS: - * Zero on success, nonzero if a failure occurred when trying to restore - * the console. */ -int con_debug_leave(void) +void con_debug_leave(void) { struct vc_data *vc; - int ret = 0; fg_console = saved_fg_console; last_console = saved_last_console; @@ -4044,8 +4222,7 @@ int con_debug_leave(void) vc = vc_cons[fg_console].d; if (vc->vc_sw->con_debug_leave) - ret = vc->vc_sw->con_debug_leave(vc); - return ret; + vc->vc_sw->con_debug_leave(vc); } EXPORT_SYMBOL_GPL(con_debug_leave); @@ -4275,14 +4452,17 @@ postcore_initcall(vtconsole_class_init); * Screen blanking */ -static int set_vesa_blanking(char __user *p) +static int set_vesa_blanking(u8 __user *mode_user) { - unsigned int mode; + u8 mode; - if (get_user(mode, p + 1)) + if (get_user(mode, mode_user)) return -EFAULT; - vesa_blank_mode = (mode < 4) ? mode : 0; + console_lock(); + vesa_blank_mode = (mode <= VESA_BLANK_MAX) ? mode : VESA_NO_BLANKING; + console_unlock(); + return 0; } @@ -4307,7 +4487,7 @@ void do_blank_screen(int entering_gfx) if (entering_gfx) { hide_cursor(vc); save_screen(vc); - vc->vc_sw->con_blank(vc, -1, 1); + vc->vc_sw->con_blank(vc, VESA_VSYNC_SUSPEND, 1); console_blanked = fg_console + 1; blank_state = blank_off; set_origin(vc); @@ -4328,7 +4508,8 @@ void do_blank_screen(int entering_gfx) save_screen(vc); /* In case we need to reset origin, blanking hook returns 1 */ - i = vc->vc_sw->con_blank(vc, vesa_off_interval ? 1 : (vesa_blank_mode + 1), 0); + i = vc->vc_sw->con_blank(vc, vesa_off_interval ? VESA_VSYNC_SUSPEND : + (vesa_blank_mode + 1), 0); console_blanked = fg_console + 1; if (i) set_origin(vc); @@ -4379,7 +4560,7 @@ void do_unblank_screen(int leaving_gfx) } console_blanked = 0; - if (vc->vc_sw->con_blank(vc, 0, leaving_gfx)) + if (vc->vc_sw->con_blank(vc, VESA_NO_BLANKING, leaving_gfx)) /* Low-level driver cannot restore -> do it ourselves */ update_screen(vc); if (console_blank_hook) @@ -4584,7 +4765,7 @@ out: return rc; } -static int con_font_set(struct vc_data *vc, struct console_font_op *op) +static int con_font_set(struct vc_data *vc, const struct console_font_op *op) { struct console_font font; int rc = -EINVAL; @@ -4748,43 +4929,3 @@ void vcs_scr_updated(struct vc_data *vc) { notify_update(vc); } - -void vc_scrolldelta_helper(struct vc_data *c, int lines, - unsigned int rolled_over, void *base, unsigned int size) -{ - unsigned long ubase = (unsigned long)base; - ptrdiff_t scr_end = (void *)c->vc_scr_end - base; - ptrdiff_t vorigin = (void *)c->vc_visible_origin - base; - ptrdiff_t origin = (void *)c->vc_origin - base; - int margin = c->vc_size_row * 4; - int from, wrap, from_off, avail; - - /* Turn scrollback off */ - if (!lines) { - c->vc_visible_origin = c->vc_origin; - return; - } - - /* Do we have already enough to allow jumping from 0 to the end? */ - if (rolled_over > scr_end + margin) { - from = scr_end; - wrap = rolled_over + c->vc_size_row; - } else { - from = 0; - wrap = size; - } - - from_off = (vorigin - from + wrap) % wrap + lines * c->vc_size_row; - avail = (origin - from + wrap) % wrap; - - /* Only a little piece would be left? Show all incl. the piece! */ - if (avail < 2 * margin) - margin = 0; - if (from_off < margin) - from_off = 0; - if (from_off > avail - margin) - from_off = avail; - - c->vc_visible_origin = ubase + (from + from_off) % wrap; -} -EXPORT_SYMBOL_GPL(vc_scrolldelta_helper); diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 8c685b5014..4b91072f3a 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -714,8 +714,7 @@ static int vt_resizex(struct vc_data *vc, struct vt_consize __user *cs) vcp->vc_scan_lines = v.v_vlin; if (v.v_clin) vcp->vc_cell_height = v.v_clin; - vcp->vc_resize_user = 1; - ret = vc_resize(vcp, v.v_cols, v.v_rows); + ret = __vc_resize(vcp, v.v_cols, v.v_rows, true); if (ret) { vcp->vc_scan_lines = save_scan_lines; vcp->vc_cell_height = save_cell_height; @@ -923,9 +922,8 @@ int vt_ioctl(struct tty_struct *tty, vc = vc_cons[i].d; if (vc) { - vc->vc_resize_user = 1; /* FIXME: review v tty lock */ - vc_resize(vc_cons[i].d, cc, ll); + __vc_resize(vc_cons[i].d, cc, ll, true); } } console_unlock(); |