Centos-kernel-stream-9/drivers/tty/serial/apbuart.c

663 lines
15 KiB
C
Raw Normal View History

tty: add SPDX identifiers to all remaining files in drivers/tty/ It's good to have SPDX identifiers in all files to make it easier to audit the kernel tree for correct licenses. Update the drivers/tty files files with the correct SPDX license identifier based on the license text in the file itself. The SPDX identifier is a legally binding shorthand, which can be used instead of the full boiler plate text. This work is based on a script and data from Thomas Gleixner, Philippe Ombredanne, and Kate Stewart. Cc: Jiri Slaby <jslaby@suse.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Chris Metcalf <cmetcalf@mellanox.com> Cc: Jiri Kosina <jikos@kernel.org> Cc: David Sterba <dsterba@suse.com> Cc: James Hogan <jhogan@kernel.org> Cc: Rob Herring <robh@kernel.org> Cc: Eric Anholt <eric@anholt.net> Cc: Stefan Wahren <stefan.wahren@i2se.com> Cc: Florian Fainelli <f.fainelli@gmail.com> Cc: Ray Jui <rjui@broadcom.com> Cc: Scott Branden <sbranden@broadcom.com> Cc: bcm-kernel-feedback-list@broadcom.com Cc: "James E.J. Bottomley" <jejb@parisc-linux.org> Cc: Helge Deller <deller@gmx.de> Cc: Joachim Eastwood <manabian@gmail.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Masahiro Yamada <yamada.masahiro@socionext.com> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@synopsys.com> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@linux-mips.org> Cc: "Uwe Kleine-König" <kernel@pengutronix.de> Cc: Pat Gefre <pfg@sgi.com> Cc: "Guilherme G. Piccoli" <gpiccoli@linux.vnet.ibm.com> Cc: Jason Wessel <jason.wessel@windriver.com> Cc: Vladimir Zapolskiy <vz@mleia.com> Cc: Sylvain Lemieux <slemieux.tyco@gmail.com> Cc: Carlo Caione <carlo@caione.org> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Liviu Dudau <liviu.dudau@arm.com> Cc: Sudeep Holla <sudeep.holla@arm.com> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: Andy Gross <andy.gross@linaro.org> Cc: David Brown <david.brown@linaro.org> Cc: "Andreas Färber" <afaerber@suse.de> Cc: Kevin Cernekee <cernekee@gmail.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Barry Song <baohua@kernel.org> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@st.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: Peter Korsgaard <jacmet@sunsite.dk> Cc: Timur Tabi <timur@tabi.org> Cc: Tony Prisk <linux@prisktech.co.nz> Cc: Michal Simek <michal.simek@xilinx.com> Cc: "Sören Brinkmann" <soren.brinkmann@xilinx.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Kate Stewart <kstewart@linuxfoundation.org> Cc: Philippe Ombredanne <pombredanne@nexb.com> Cc: Jiri Slaby <jslaby@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-06 17:11:51 +00:00
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for GRLIB serial ports (APBUART)
*
* Based on linux/drivers/serial/amba.c
*
* Copyright (C) 2000 Deep Blue Solutions Ltd.
* Copyright (C) 2003 Konrad Eisele <eiselekd@web.de>
* Copyright (C) 2006 Daniel Hellstrom <daniel@gaisler.com>, Aeroflex Gaisler AB
* Copyright (C) 2008 Gilead Kutnick <kutnickg@zin-tech.com>
* Copyright (C) 2009 Kristoffer Glembo <kristoffer@gaisler.com>, Aeroflex Gaisler AB
*/
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/kthread.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/serial_core.h>
#include <asm/irq.h>
#include "apbuart.h"
#define SERIAL_APBUART_MAJOR TTY_MAJOR
#define SERIAL_APBUART_MINOR 64
#define UART_DUMMY_RSR_RX 0x8000 /* for ignore all read */
static void apbuart_tx_chars(struct uart_port *port);
static void apbuart_stop_tx(struct uart_port *port)
{
unsigned int cr;
cr = UART_GET_CTRL(port);
cr &= ~UART_CTRL_TI;
UART_PUT_CTRL(port, cr);
}
static void apbuart_start_tx(struct uart_port *port)
{
unsigned int cr;
cr = UART_GET_CTRL(port);
cr |= UART_CTRL_TI;
UART_PUT_CTRL(port, cr);
if (UART_GET_STATUS(port) & UART_STATUS_THE)
apbuart_tx_chars(port);
}
static void apbuart_stop_rx(struct uart_port *port)
{
unsigned int cr;
cr = UART_GET_CTRL(port);
cr &= ~(UART_CTRL_RI);
UART_PUT_CTRL(port, cr);
}
static void apbuart_rx_chars(struct uart_port *port)
{
serial: drivers: switch ch and flag to u8 JIRA: https://issues.redhat.com/browse/RHEL-24205 commit fd2b55f86b8b25afc5b6e7dff53dddb3fd0dd211 Author: Jiri Slaby <jirislaby@kernel.org> Date: Wed Jul 12 10:18:11 2023 +0200 serial: drivers: switch ch and flag to u8 Now that the serial layer explicitly expects 'u8' for flags and characters, propagate this type to drivers' (RX) routines. Note that amba-pl011's, clps711x's and st-asc's 'ch' are left unchanged because 'ch' contains not only a character, but whole status. Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Claudiu Beznea <claudiu.beznea@microchip.com> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Kevin Cernekee <cernekee@gmail.com> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Alim Akhtar <alim.akhtar@samsung.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Paul Walmsley <paul.walmsley@sifive.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: Hammer Hsieh <hammerh0314@gmail.com> Acked-by: Richard GENOUD <richard.genoud@gmail.com> Acked-by: Tobias Klauser <tklauser@distanz.ch> Acked-by: Thierry Reding <treding@nvidia.com> Acked-by: Maciej W. Rozycki <macro@orcam.me.uk> Link: https://lore.kernel.org/r/20230712081811.29004-11-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Andrew Halaney <ahalaney@redhat.com>
2024-02-27 20:55:40 +00:00
unsigned int status, rsr;
unsigned int max_chars = port->fifosize;
serial: drivers: switch ch and flag to u8 JIRA: https://issues.redhat.com/browse/RHEL-24205 commit fd2b55f86b8b25afc5b6e7dff53dddb3fd0dd211 Author: Jiri Slaby <jirislaby@kernel.org> Date: Wed Jul 12 10:18:11 2023 +0200 serial: drivers: switch ch and flag to u8 Now that the serial layer explicitly expects 'u8' for flags and characters, propagate this type to drivers' (RX) routines. Note that amba-pl011's, clps711x's and st-asc's 'ch' are left unchanged because 'ch' contains not only a character, but whole status. Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Claudiu Beznea <claudiu.beznea@microchip.com> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Kevin Cernekee <cernekee@gmail.com> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Alim Akhtar <alim.akhtar@samsung.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Paul Walmsley <paul.walmsley@sifive.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: Hammer Hsieh <hammerh0314@gmail.com> Acked-by: Richard GENOUD <richard.genoud@gmail.com> Acked-by: Tobias Klauser <tklauser@distanz.ch> Acked-by: Thierry Reding <treding@nvidia.com> Acked-by: Maciej W. Rozycki <macro@orcam.me.uk> Link: https://lore.kernel.org/r/20230712081811.29004-11-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Andrew Halaney <ahalaney@redhat.com>
2024-02-27 20:55:40 +00:00
u8 ch, flag;
status = UART_GET_STATUS(port);
while (UART_RX_DATA(status) && (max_chars--)) {
ch = UART_GET_CHAR(port);
flag = TTY_NORMAL;
port->icount.rx++;
rsr = UART_GET_STATUS(port) | UART_DUMMY_RSR_RX;
UART_PUT_STATUS(port, 0);
if (rsr & UART_STATUS_ERR) {
if (rsr & UART_STATUS_BR) {
rsr &= ~(UART_STATUS_FE | UART_STATUS_PE);
port->icount.brk++;
if (uart_handle_break(port))
goto ignore_char;
} else if (rsr & UART_STATUS_PE) {
port->icount.parity++;
} else if (rsr & UART_STATUS_FE) {
port->icount.frame++;
}
if (rsr & UART_STATUS_OE)
port->icount.overrun++;
rsr &= port->read_status_mask;
if (rsr & UART_STATUS_PE)
flag = TTY_PARITY;
else if (rsr & UART_STATUS_FE)
flag = TTY_FRAME;
}
if (uart_handle_sysrq_char(port, ch))
goto ignore_char;
uart_insert_char(port, rsr, UART_STATUS_OE, ch, flag);
ignore_char:
status = UART_GET_STATUS(port);
}
tty_flip_buffer_push(&port->state->port);
}
static void apbuart_tx_chars(struct uart_port *port)
{
u8 ch;
serial: apbuart: fix console prompt on qemu JIRA: https://issues.redhat.com/browse/RHEL-24205 commit c6dcd8050fb7c2efec6946ae9c49bc186b0a7475 Author: Sam Ravnborg <sam@ravnborg.org> Date: Tue Dec 26 13:16:07 2023 +0100 serial: apbuart: fix console prompt on qemu When using a leon kernel with qemu there where no console prompt. The root cause is the handling of the fifo size in the tx part of the apbuart driver. The qemu uart driver only have a very rudimentary status handling and do not report the number of chars queued in the tx fifo in the status register. So the driver ends up with a fifo size of 1. In the tx path the fifo size is divided by 2 - resulting in a fifo size of zero. The original implementation would always try to send one char, but after the introduction of uart_port_tx_limited() the fifo size is respected even for the first char. There seems to be no good reason to divide the fifo size with two - so remove this. It looks like something copied from the original amba driver. With qemu we now have a minimum fifo size of one char, so we show the prompt. Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Fixes: d11cc8c3c4b6 ("tty: serial: use uart_port_tx_limited()") Cc: Andreas Larsson <andreas@gaisler.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Jiri Slaby <jirislaby@kernel.org> Cc: <linux-serial@vger.kernel.org> Cc: <sparclinux@vger.kernel.org> Link: https://lore.kernel.org/r/20231226121607.GA2622970@ravnborg.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Andrew Halaney <ahalaney@redhat.com>
2024-02-27 21:44:03 +00:00
uart_port_tx_limited(port, ch, port->fifosize,
true,
UART_PUT_CHAR(port, ch),
({}));
}
static irqreturn_t apbuart_int(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
unsigned int status;
serial: apb: Use port lock wrappers JIRA: https://issues.redhat.com/browse/RHEL-24205 commit 5412c394d5c8e052b656afaa7d0c6bb2a0d0bdc6 Author: Thomas Gleixner <tglx@linutronix.de> Date: Thu Sep 14 20:43:35 2023 +0206 serial: apb: Use port lock wrappers When a serial port is used for kernel console output, then all modifications to the UART registers which are done from other contexts, e.g. getty, termios, are interference points for the kernel console. So far this has been ignored and the printk output is based on the principle of hope. The rework of the console infrastructure which aims to support threaded and atomic consoles, requires to mark sections which modify the UART registers as unsafe. This allows the atomic write function to make informed decisions and eventually to restore operational state. It also allows to prevent the regular UART code from modifying UART registers while printk output is in progress. All modifications of UART registers are guarded by the UART port lock, which provides an obvious synchronization point with the console infrastructure. To avoid adding this functionality to all UART drivers, wrap the spin_[un]lock*() invocations for uart_port::lock into helper functions which just contain the spin_[un]lock*() invocations for now. In a subsequent step these helpers will gain the console synchronization mechanisms. Converted with coccinelle. No functional change. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-19-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Andrew Halaney <ahalaney@redhat.com>
2024-03-11 20:12:01 +00:00
uart_port_lock(port);
status = UART_GET_STATUS(port);
if (status & UART_STATUS_DR)
apbuart_rx_chars(port);
if (status & UART_STATUS_THE)
apbuart_tx_chars(port);
serial: apb: Use port lock wrappers JIRA: https://issues.redhat.com/browse/RHEL-24205 commit 5412c394d5c8e052b656afaa7d0c6bb2a0d0bdc6 Author: Thomas Gleixner <tglx@linutronix.de> Date: Thu Sep 14 20:43:35 2023 +0206 serial: apb: Use port lock wrappers When a serial port is used for kernel console output, then all modifications to the UART registers which are done from other contexts, e.g. getty, termios, are interference points for the kernel console. So far this has been ignored and the printk output is based on the principle of hope. The rework of the console infrastructure which aims to support threaded and atomic consoles, requires to mark sections which modify the UART registers as unsafe. This allows the atomic write function to make informed decisions and eventually to restore operational state. It also allows to prevent the regular UART code from modifying UART registers while printk output is in progress. All modifications of UART registers are guarded by the UART port lock, which provides an obvious synchronization point with the console infrastructure. To avoid adding this functionality to all UART drivers, wrap the spin_[un]lock*() invocations for uart_port::lock into helper functions which just contain the spin_[un]lock*() invocations for now. In a subsequent step these helpers will gain the console synchronization mechanisms. Converted with coccinelle. No functional change. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-19-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Andrew Halaney <ahalaney@redhat.com>
2024-03-11 20:12:01 +00:00
uart_port_unlock(port);
return IRQ_HANDLED;
}
static unsigned int apbuart_tx_empty(struct uart_port *port)
{
unsigned int status = UART_GET_STATUS(port);
return status & UART_STATUS_THE ? TIOCSER_TEMT : 0;
}
static unsigned int apbuart_get_mctrl(struct uart_port *port)
{
/* The GRLIB APBUART handles flow control in hardware */
return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
}
static void apbuart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
/* The GRLIB APBUART handles flow control in hardware */
}
static void apbuart_break_ctl(struct uart_port *port, int break_state)
{
/* We don't support sending break */
}
static int apbuart_startup(struct uart_port *port)
{
int retval;
unsigned int cr;
/* Allocate the IRQ */
retval = request_irq(port->irq, apbuart_int, 0, "apbuart", port);
if (retval)
return retval;
/* Finally, enable interrupts */
cr = UART_GET_CTRL(port);
UART_PUT_CTRL(port,
cr | UART_CTRL_RE | UART_CTRL_TE |
UART_CTRL_RI | UART_CTRL_TI);
return 0;
}
static void apbuart_shutdown(struct uart_port *port)
{
unsigned int cr;
/* disable all interrupts, disable the port */
cr = UART_GET_CTRL(port);
UART_PUT_CTRL(port,
cr & ~(UART_CTRL_RE | UART_CTRL_TE |
UART_CTRL_RI | UART_CTRL_TI));
/* Free the interrupt */
free_irq(port->irq, port);
}
static void apbuart_set_termios(struct uart_port *port,
struct ktermios *termios, const struct ktermios *old)
{
unsigned int cr;
unsigned long flags;
unsigned int baud, quot;
/* Ask the core to calculate the divisor for us. */
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
if (baud == 0)
panic("invalid baudrate %i\n", port->uartclk / 16);
/* uart_get_divisor calc a *16 uart freq, apbuart is *8 */
quot = (uart_get_divisor(port, baud)) * 2;
cr = UART_GET_CTRL(port);
cr &= ~(UART_CTRL_PE | UART_CTRL_PS);
if (termios->c_cflag & PARENB) {
cr |= UART_CTRL_PE;
if ((termios->c_cflag & PARODD))
cr |= UART_CTRL_PS;
}
/* Enable flow control. */
if (termios->c_cflag & CRTSCTS)
cr |= UART_CTRL_FL;
serial: apb: Use port lock wrappers JIRA: https://issues.redhat.com/browse/RHEL-24205 commit 5412c394d5c8e052b656afaa7d0c6bb2a0d0bdc6 Author: Thomas Gleixner <tglx@linutronix.de> Date: Thu Sep 14 20:43:35 2023 +0206 serial: apb: Use port lock wrappers When a serial port is used for kernel console output, then all modifications to the UART registers which are done from other contexts, e.g. getty, termios, are interference points for the kernel console. So far this has been ignored and the printk output is based on the principle of hope. The rework of the console infrastructure which aims to support threaded and atomic consoles, requires to mark sections which modify the UART registers as unsafe. This allows the atomic write function to make informed decisions and eventually to restore operational state. It also allows to prevent the regular UART code from modifying UART registers while printk output is in progress. All modifications of UART registers are guarded by the UART port lock, which provides an obvious synchronization point with the console infrastructure. To avoid adding this functionality to all UART drivers, wrap the spin_[un]lock*() invocations for uart_port::lock into helper functions which just contain the spin_[un]lock*() invocations for now. In a subsequent step these helpers will gain the console synchronization mechanisms. Converted with coccinelle. No functional change. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-19-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Andrew Halaney <ahalaney@redhat.com>
2024-03-11 20:12:01 +00:00
uart_port_lock_irqsave(port, &flags);
/* Update the per-port timeout. */
uart_update_timeout(port, termios->c_cflag, baud);
port->read_status_mask = UART_STATUS_OE;
if (termios->c_iflag & INPCK)
port->read_status_mask |= UART_STATUS_FE | UART_STATUS_PE;
/* Characters to ignore */
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= UART_STATUS_FE | UART_STATUS_PE;
/* Ignore all characters if CREAD is not set. */
if ((termios->c_cflag & CREAD) == 0)
port->ignore_status_mask |= UART_DUMMY_RSR_RX;
/* Set baud rate */
quot -= 1;
UART_PUT_SCAL(port, quot);
UART_PUT_CTRL(port, cr);
serial: apb: Use port lock wrappers JIRA: https://issues.redhat.com/browse/RHEL-24205 commit 5412c394d5c8e052b656afaa7d0c6bb2a0d0bdc6 Author: Thomas Gleixner <tglx@linutronix.de> Date: Thu Sep 14 20:43:35 2023 +0206 serial: apb: Use port lock wrappers When a serial port is used for kernel console output, then all modifications to the UART registers which are done from other contexts, e.g. getty, termios, are interference points for the kernel console. So far this has been ignored and the printk output is based on the principle of hope. The rework of the console infrastructure which aims to support threaded and atomic consoles, requires to mark sections which modify the UART registers as unsafe. This allows the atomic write function to make informed decisions and eventually to restore operational state. It also allows to prevent the regular UART code from modifying UART registers while printk output is in progress. All modifications of UART registers are guarded by the UART port lock, which provides an obvious synchronization point with the console infrastructure. To avoid adding this functionality to all UART drivers, wrap the spin_[un]lock*() invocations for uart_port::lock into helper functions which just contain the spin_[un]lock*() invocations for now. In a subsequent step these helpers will gain the console synchronization mechanisms. Converted with coccinelle. No functional change. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-19-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Andrew Halaney <ahalaney@redhat.com>
2024-03-11 20:12:01 +00:00
uart_port_unlock_irqrestore(port, flags);
}
static const char *apbuart_type(struct uart_port *port)
{
return port->type == PORT_APBUART ? "GRLIB/APBUART" : NULL;
}
static void apbuart_release_port(struct uart_port *port)
{
release_mem_region(port->mapbase, 0x100);
}
static int apbuart_request_port(struct uart_port *port)
{
return request_mem_region(port->mapbase, 0x100, "grlib-apbuart")
!= NULL ? 0 : -EBUSY;
return 0;
}
/* Configure/autoconfigure the port */
static void apbuart_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE) {
port->type = PORT_APBUART;
apbuart_request_port(port);
}
}
/* Verify the new serial_struct (for TIOCSSERIAL) */
static int apbuart_verify_port(struct uart_port *port,
struct serial_struct *ser)
{
int ret = 0;
if (ser->type != PORT_UNKNOWN && ser->type != PORT_APBUART)
ret = -EINVAL;
if (ser->irq < 0 || ser->irq >= NR_IRQS)
ret = -EINVAL;
if (ser->baud_base < 9600)
ret = -EINVAL;
return ret;
}
static const struct uart_ops grlib_apbuart_ops = {
.tx_empty = apbuart_tx_empty,
.set_mctrl = apbuart_set_mctrl,
.get_mctrl = apbuart_get_mctrl,
.stop_tx = apbuart_stop_tx,
.start_tx = apbuart_start_tx,
.stop_rx = apbuart_stop_rx,
.break_ctl = apbuart_break_ctl,
.startup = apbuart_startup,
.shutdown = apbuart_shutdown,
.set_termios = apbuart_set_termios,
.type = apbuart_type,
.release_port = apbuart_release_port,
.request_port = apbuart_request_port,
.config_port = apbuart_config_port,
.verify_port = apbuart_verify_port,
};
static struct uart_port grlib_apbuart_ports[UART_NR];
static struct device_node *grlib_apbuart_nodes[UART_NR];
static int apbuart_scan_fifo_size(struct uart_port *port, int portnumber)
{
int ctrl, loop = 0;
int status;
int fifosize;
unsigned long flags;
ctrl = UART_GET_CTRL(port);
/*
* Enable the transceiver and wait for it to be ready to send data.
* Clear interrupts so that this process will not be externally
* interrupted in the middle (which can cause the transceiver to
* drain prematurely).
*/
local_irq_save(flags);
UART_PUT_CTRL(port, ctrl | UART_CTRL_TE);
while (!UART_TX_READY(UART_GET_STATUS(port)))
loop++;
/*
* Disable the transceiver so data isn't actually sent during the
* actual test.
*/
UART_PUT_CTRL(port, ctrl & ~(UART_CTRL_TE));
fifosize = 1;
UART_PUT_CHAR(port, 0);
/*
* So long as transmitting a character increments the tranceivier FIFO
* length the FIFO must be at least that big. These bytes will
* automatically drain off of the FIFO.
*/
status = UART_GET_STATUS(port);
while (((status >> 20) & 0x3F) == fifosize) {
fifosize++;
UART_PUT_CHAR(port, 0);
status = UART_GET_STATUS(port);
}
fifosize--;
UART_PUT_CTRL(port, ctrl);
local_irq_restore(flags);
if (fifosize == 0)
fifosize = 1;
return fifosize;
}
static void apbuart_flush_fifo(struct uart_port *port)
{
int i;
for (i = 0; i < port->fifosize; i++)
UART_GET_CHAR(port);
}
/* ======================================================================== */
/* Console driver, if enabled */
/* ======================================================================== */
#ifdef CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE
serial: make uart_console_write->putchar()'s character an unsigned char Bugzilla: https://bugzilla.redhat.com/2162957 Conflicts: drivers/tty/serial/sunplus-uart.c is not part of CS9 commit 3f8bab174cb26aa5a8053c4457cc733881e3ad88 Author: Jiri Slaby <jirislaby@kernel.org> Date: Thu Mar 3 09:08:31 2022 +0100 serial: make uart_console_write->putchar()'s character an unsigned char Currently, uart_console_write->putchar's second parameter (the character) is of type int. It makes little sense, provided uart_console_write() accepts the input string as "const char *s" and passes its content -- the characters -- to putchar(). So switch the character's type to unsigned char. We don't use char as that is signed on some platforms. That would cause troubles for drivers which (implicitly) cast the char to u16 when writing to the device. Sign extension would happen in that case and the value written would be completely different to the provided char. DZ is an example of such a driver -- on MIPS, it uses u16 for dz_out in dz_console_putchar(). Note we do the char -> uchar conversion implicitly in uart_console_write(). Provided we do not change size of the data type, sign extension does not happen there, so the problem is void. This makes the types consistent and unified with the rest of the uart layer, which uses unsigned char in most places already. One exception is xmit_buf, but that is going to be converted later. Cc: Paul Cercueil <paul@crapouillou.net> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Ludovic Desroches <ludovic.desroches@microchip.com> Cc: Florian Fainelli <f.fainelli@gmail.com> Cc: bcm-kernel-feedback-list@broadcom.com Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Cc: Paul Walmsley <paul.walmsley@sifive.com> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Albert Ou <aou@eecs.berkeley.edu> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Karol Gugala <kgugala@antmicro.com> Cc: Mateusz Holenko <mholenko@antmicro.com> Cc: Vladimir Zapolskiy <vz@mleia.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Liviu Dudau <liviu.dudau@arm.com> Cc: Sudeep Holla <sudeep.holla@arm.com> Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Cc: "Andreas Färber" <afaerber@suse.de> Cc: Manivannan Sadhasivam <mani@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Cc: Andy Gross <agross@kernel.org> Cc: Bjorn Andersson <bjorn.andersson@linaro.org> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang7@gmail.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: Peter Korsgaard <peter@korsgaard.com> Cc: Michal Simek <michal.simek@xilinx.com> Acked-by: Richard Genoud <richard.genoud@gmail.com> [atmel_serial] Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Acked-by: Paul Cercueil <paul@crapouillou.net> Acked-by: Neil Armstrong <narmstrong@baylibre.com> # meson_serial Signed-off-by: Jiri Slaby <jslaby@suse.cz> Link: https://lore.kernel.org/r/20220303080831.21783-1-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Alessandro Carminati <acarmina@redhat.com>
2023-01-25 14:22:30 +00:00
static void apbuart_console_putchar(struct uart_port *port, unsigned char ch)
{
unsigned int status;
do {
status = UART_GET_STATUS(port);
} while (!UART_TX_READY(status));
UART_PUT_CHAR(port, ch);
}
static void
apbuart_console_write(struct console *co, const char *s, unsigned int count)
{
struct uart_port *port = &grlib_apbuart_ports[co->index];
unsigned int status, old_cr, new_cr;
/* First save the CR then disable the interrupts */
old_cr = UART_GET_CTRL(port);
new_cr = old_cr & ~(UART_CTRL_RI | UART_CTRL_TI);
UART_PUT_CTRL(port, new_cr);
uart_console_write(port, s, count, apbuart_console_putchar);
/*
* Finally, wait for transmitter to become empty
* and restore the TCR
*/
do {
status = UART_GET_STATUS(port);
} while (!UART_TX_READY(status));
UART_PUT_CTRL(port, old_cr);
}
static void __init
apbuart_console_get_options(struct uart_port *port, int *baud,
int *parity, int *bits)
{
if (UART_GET_CTRL(port) & (UART_CTRL_RE | UART_CTRL_TE)) {
unsigned int quot, status;
status = UART_GET_STATUS(port);
*parity = 'n';
if (status & UART_CTRL_PE) {
if ((status & UART_CTRL_PS) == 0)
*parity = 'e';
else
*parity = 'o';
}
*bits = 8;
quot = UART_GET_SCAL(port) / 8;
*baud = port->uartclk / (16 * (quot + 1));
}
}
static int __init apbuart_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = 38400;
int bits = 8;
int parity = 'n';
int flow = 'n';
pr_debug("apbuart_console_setup co=%p, co->index=%i, options=%s\n",
co, co->index, options);
/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
* console support.
*/
if (co->index >= grlib_apbuart_port_nr)
co->index = 0;
port = &grlib_apbuart_ports[co->index];
spin_lock_init(&port->lock);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
apbuart_console_get_options(port, &baud, &parity, &bits);
return uart_set_options(port, co, baud, parity, bits, flow);
}
static struct uart_driver grlib_apbuart_driver;
static struct console grlib_apbuart_console = {
.name = "ttyS",
.write = apbuart_console_write,
.device = uart_console_device,
.setup = apbuart_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &grlib_apbuart_driver,
};
static int grlib_apbuart_configure(void);
static int __init apbuart_console_init(void)
{
if (grlib_apbuart_configure())
return -ENODEV;
register_console(&grlib_apbuart_console);
return 0;
}
console_initcall(apbuart_console_init);
#define APBUART_CONSOLE (&grlib_apbuart_console)
#else
#define APBUART_CONSOLE NULL
#endif
static struct uart_driver grlib_apbuart_driver = {
.owner = THIS_MODULE,
.driver_name = "serial",
.dev_name = "ttyS",
.major = SERIAL_APBUART_MAJOR,
.minor = SERIAL_APBUART_MINOR,
.nr = UART_NR,
.cons = APBUART_CONSOLE,
};
/* ======================================================================== */
/* OF Platform Driver */
/* ======================================================================== */
static int apbuart_probe(struct platform_device *op)
{
int i;
struct uart_port *port = NULL;
for (i = 0; i < grlib_apbuart_port_nr; i++) {
if (op->dev.of_node == grlib_apbuart_nodes[i])
break;
}
port = &grlib_apbuart_ports[i];
port->dev = &op->dev;
port->irq = op->archdata.irqs[0];
uart_add_one_port(&grlib_apbuart_driver, (struct uart_port *) port);
apbuart_flush_fifo((struct uart_port *) port);
printk(KERN_INFO "grlib-apbuart at 0x%llx, irq %d\n",
(unsigned long long) port->mapbase, port->irq);
return 0;
}
static const struct of_device_id apbuart_match[] = {
{
.name = "GAISLER_APBUART",
},
{
.name = "01_00c",
},
{},
};
MODULE_DEVICE_TABLE(of, apbuart_match);
static struct platform_driver grlib_apbuart_of_driver = {
.probe = apbuart_probe,
.driver = {
.name = "grlib-apbuart",
.of_match_table = apbuart_match,
},
};
static int __init grlib_apbuart_configure(void)
{
struct device_node *np;
int line = 0;
for_each_matching_node(np, apbuart_match) {
const int *ampopts;
const u32 *freq_hz;
const struct amba_prom_registers *regs;
struct uart_port *port;
unsigned long addr;
ampopts = of_get_property(np, "ampopts", NULL);
if (ampopts && (*ampopts == 0))
continue; /* Ignore if used by another OS instance */
regs = of_get_property(np, "reg", NULL);
/* Frequency of APB Bus is frequency of UART */
freq_hz = of_get_property(np, "freq", NULL);
if (!regs || !freq_hz || (*freq_hz == 0))
continue;
grlib_apbuart_nodes[line] = np;
addr = regs->phys_addr;
port = &grlib_apbuart_ports[line];
port->mapbase = addr;
port->membase = ioremap(addr, sizeof(struct grlib_apbuart_regs_map));
port->irq = 0;
port->iotype = UPIO_MEM;
port->ops = &grlib_apbuart_ops;
port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE);
port->flags = UPF_BOOT_AUTOCONF;
port->line = line;
port->uartclk = *freq_hz;
port->fifosize = apbuart_scan_fifo_size((struct uart_port *) port, line);
line++;
/* We support maximum UART_NR uarts ... */
if (line == UART_NR)
break;
}
grlib_apbuart_driver.nr = grlib_apbuart_port_nr = line;
return line ? 0 : -ENODEV;
}
static int __init grlib_apbuart_init(void)
{
int ret;
/* Find all APBUARTS in device the tree and initialize their ports */
ret = grlib_apbuart_configure();
if (ret)
return ret;
printk(KERN_INFO "Serial: GRLIB APBUART driver\n");
ret = uart_register_driver(&grlib_apbuart_driver);
if (ret) {
printk(KERN_ERR "%s: uart_register_driver failed (%i)\n",
__FILE__, ret);
return ret;
}
ret = platform_driver_register(&grlib_apbuart_of_driver);
if (ret) {
printk(KERN_ERR
"%s: platform_driver_register failed (%i)\n",
__FILE__, ret);
uart_unregister_driver(&grlib_apbuart_driver);
return ret;
}
return ret;
}
static void __exit grlib_apbuart_exit(void)
{
int i;
for (i = 0; i < grlib_apbuart_port_nr; i++)
uart_remove_one_port(&grlib_apbuart_driver,
&grlib_apbuart_ports[i]);
uart_unregister_driver(&grlib_apbuart_driver);
platform_driver_unregister(&grlib_apbuart_of_driver);
}
module_init(grlib_apbuart_init);
module_exit(grlib_apbuart_exit);
MODULE_AUTHOR("Aeroflex Gaisler AB");
MODULE_DESCRIPTION("GRLIB APBUART serial driver");
MODULE_VERSION("2.1");
MODULE_LICENSE("GPL");