linux-stable/drivers/tty/serial/msm_serial.c

1936 lines
47 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 msm7k serial device and console
*
* Copyright (C) 2007 Google, Inc.
* Author: Robert Love <rlove@google.com>
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/atomic.h>
#include <linux/dma/qcom_adm.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/wait.h>
#define MSM_UART_MR1 0x0000
#define MSM_UART_MR1_AUTO_RFR_LEVEL0 0x3F
#define MSM_UART_MR1_AUTO_RFR_LEVEL1 0x3FF00
#define MSM_UART_DM_MR1_AUTO_RFR_LEVEL1 0xFFFFFF00
#define MSM_UART_MR1_RX_RDY_CTL BIT(7)
#define MSM_UART_MR1_CTS_CTL BIT(6)
#define MSM_UART_MR2 0x0004
#define MSM_UART_MR2_ERROR_MODE BIT(6)
#define MSM_UART_MR2_BITS_PER_CHAR 0x30
#define MSM_UART_MR2_BITS_PER_CHAR_5 (0x0 << 4)
#define MSM_UART_MR2_BITS_PER_CHAR_6 (0x1 << 4)
#define MSM_UART_MR2_BITS_PER_CHAR_7 (0x2 << 4)
#define MSM_UART_MR2_BITS_PER_CHAR_8 (0x3 << 4)
#define MSM_UART_MR2_STOP_BIT_LEN_ONE (0x1 << 2)
#define MSM_UART_MR2_STOP_BIT_LEN_TWO (0x3 << 2)
#define MSM_UART_MR2_PARITY_MODE_NONE 0x0
#define MSM_UART_MR2_PARITY_MODE_ODD 0x1
#define MSM_UART_MR2_PARITY_MODE_EVEN 0x2
#define MSM_UART_MR2_PARITY_MODE_SPACE 0x3
#define MSM_UART_MR2_PARITY_MODE 0x3
#define MSM_UART_CSR 0x0008
#define MSM_UART_TF 0x000C
#define UARTDM_TF 0x0070
#define MSM_UART_CR 0x0010
#define MSM_UART_CR_CMD_NULL (0 << 4)
#define MSM_UART_CR_CMD_RESET_RX (1 << 4)
#define MSM_UART_CR_CMD_RESET_TX (2 << 4)
#define MSM_UART_CR_CMD_RESET_ERR (3 << 4)
#define MSM_UART_CR_CMD_RESET_BREAK_INT (4 << 4)
#define MSM_UART_CR_CMD_START_BREAK (5 << 4)
#define MSM_UART_CR_CMD_STOP_BREAK (6 << 4)
#define MSM_UART_CR_CMD_RESET_CTS (7 << 4)
#define MSM_UART_CR_CMD_RESET_STALE_INT (8 << 4)
#define MSM_UART_CR_CMD_PACKET_MODE (9 << 4)
#define MSM_UART_CR_CMD_MODE_RESET (12 << 4)
#define MSM_UART_CR_CMD_SET_RFR (13 << 4)
#define MSM_UART_CR_CMD_RESET_RFR (14 << 4)
#define MSM_UART_CR_CMD_PROTECTION_EN (16 << 4)
#define MSM_UART_CR_CMD_STALE_EVENT_DISABLE (6 << 8)
#define MSM_UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4)
#define MSM_UART_CR_CMD_FORCE_STALE (4 << 8)
#define MSM_UART_CR_CMD_RESET_TX_READY (3 << 8)
#define MSM_UART_CR_TX_DISABLE BIT(3)
#define MSM_UART_CR_TX_ENABLE BIT(2)
#define MSM_UART_CR_RX_DISABLE BIT(1)
#define MSM_UART_CR_RX_ENABLE BIT(0)
#define MSM_UART_CR_CMD_RESET_RXBREAK_START ((1 << 11) | (2 << 4))
#define MSM_UART_IMR 0x0014
#define MSM_UART_IMR_TXLEV BIT(0)
#define MSM_UART_IMR_RXSTALE BIT(3)
#define MSM_UART_IMR_RXLEV BIT(4)
#define MSM_UART_IMR_DELTA_CTS BIT(5)
#define MSM_UART_IMR_CURRENT_CTS BIT(6)
#define MSM_UART_IMR_RXBREAK_START BIT(10)
#define MSM_UART_IPR_RXSTALE_LAST 0x20
#define MSM_UART_IPR_STALE_LSB 0x1F
#define MSM_UART_IPR_STALE_TIMEOUT_MSB 0x3FF80
#define MSM_UART_DM_IPR_STALE_TIMEOUT_MSB 0xFFFFFF80
#define MSM_UART_IPR 0x0018
#define MSM_UART_TFWR 0x001C
#define MSM_UART_RFWR 0x0020
#define MSM_UART_HCR 0x0024
#define MSM_UART_MREG 0x0028
#define MSM_UART_NREG 0x002C
#define MSM_UART_DREG 0x0030
#define MSM_UART_MNDREG 0x0034
#define MSM_UART_IRDA 0x0038
#define MSM_UART_MISR_MODE 0x0040
#define MSM_UART_MISR_RESET 0x0044
#define MSM_UART_MISR_EXPORT 0x0048
#define MSM_UART_MISR_VAL 0x004C
#define MSM_UART_TEST_CTRL 0x0050
#define MSM_UART_SR 0x0008
#define MSM_UART_SR_HUNT_CHAR BIT(7)
#define MSM_UART_SR_RX_BREAK BIT(6)
#define MSM_UART_SR_PAR_FRAME_ERR BIT(5)
#define MSM_UART_SR_OVERRUN BIT(4)
#define MSM_UART_SR_TX_EMPTY BIT(3)
#define MSM_UART_SR_TX_READY BIT(2)
#define MSM_UART_SR_RX_FULL BIT(1)
#define MSM_UART_SR_RX_READY BIT(0)
#define MSM_UART_RF 0x000C
#define UARTDM_RF 0x0070
#define MSM_UART_MISR 0x0010
#define MSM_UART_ISR 0x0014
#define MSM_UART_ISR_TX_READY BIT(7)
#define UARTDM_RXFS 0x50
#define UARTDM_RXFS_BUF_SHIFT 0x7
#define UARTDM_RXFS_BUF_MASK 0x7
#define UARTDM_DMEN 0x3C
#define UARTDM_DMEN_RX_SC_ENABLE BIT(5)
#define UARTDM_DMEN_TX_SC_ENABLE BIT(4)
#define UARTDM_DMEN_TX_BAM_ENABLE BIT(2) /* UARTDM_1P4 */
#define UARTDM_DMEN_TX_DM_ENABLE BIT(0) /* < UARTDM_1P4 */
#define UARTDM_DMEN_RX_BAM_ENABLE BIT(3) /* UARTDM_1P4 */
#define UARTDM_DMEN_RX_DM_ENABLE BIT(1) /* < UARTDM_1P4 */
#define UARTDM_DMRX 0x34
#define UARTDM_NCF_TX 0x40
#define UARTDM_RX_TOTAL_SNAP 0x38
#define UARTDM_BURST_SIZE 16 /* in bytes */
#define UARTDM_TX_AIGN(x) ((x) & ~0x3) /* valid for > 1p3 */
#define UARTDM_TX_MAX 256 /* in bytes, valid for <= 1p3 */
#define UARTDM_RX_SIZE (UART_XMIT_SIZE / 4)
enum {
UARTDM_1P1 = 1,
UARTDM_1P2,
UARTDM_1P3,
UARTDM_1P4,
};
struct msm_dma {
struct dma_chan *chan;
enum dma_data_direction dir;
dma_addr_t phys;
unsigned char *virt;
dma_cookie_t cookie;
u32 enable_bit;
unsigned int count;
struct dma_async_tx_descriptor *desc;
};
struct msm_port {
struct uart_port uart;
char name[16];
struct clk *clk;
struct clk *pclk;
unsigned int imr;
int is_uartdm;
unsigned int old_snap_state;
bool break_detected;
struct msm_dma tx_dma;
struct msm_dma rx_dma;
};
static inline struct msm_port *to_msm_port(struct uart_port *up)
{
return container_of(up, struct msm_port, uart);
}
static
void msm_write(struct uart_port *port, unsigned int val, unsigned int off)
{
writel_relaxed(val, port->membase + off);
}
static
unsigned int msm_read(struct uart_port *port, unsigned int off)
{
return readl_relaxed(port->membase + off);
}
/*
* Setup the MND registers to use the TCXO clock.
*/
static void msm_serial_set_mnd_regs_tcxo(struct uart_port *port)
{
msm_write(port, 0x06, MSM_UART_MREG);
msm_write(port, 0xF1, MSM_UART_NREG);
msm_write(port, 0x0F, MSM_UART_DREG);
msm_write(port, 0x1A, MSM_UART_MNDREG);
port->uartclk = 1843200;
}
/*
* Setup the MND registers to use the TCXO clock divided by 4.
*/
static void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port)
{
msm_write(port, 0x18, MSM_UART_MREG);
msm_write(port, 0xF6, MSM_UART_NREG);
msm_write(port, 0x0F, MSM_UART_DREG);
msm_write(port, 0x0A, MSM_UART_MNDREG);
port->uartclk = 1843200;
}
static void msm_serial_set_mnd_regs(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
/*
* These registers don't exist so we change the clk input rate
* on uartdm hardware instead
*/
if (msm_port->is_uartdm)
return;
if (port->uartclk == 19200000)
msm_serial_set_mnd_regs_tcxo(port);
else if (port->uartclk == 4800000)
msm_serial_set_mnd_regs_tcxoby4(port);
}
static void msm_handle_tx(struct uart_port *port);
static void msm_start_rx_dma(struct msm_port *msm_port);
static void msm_stop_dma(struct uart_port *port, struct msm_dma *dma)
{
struct device *dev = port->dev;
unsigned int mapped;
u32 val;
mapped = dma->count;
dma->count = 0;
dmaengine_terminate_all(dma->chan);
/*
* DMA Stall happens if enqueue and flush command happens concurrently.
* For example before changing the baud rate/protocol configuration and
* sending flush command to ADM, disable the channel of UARTDM.
* Note: should not reset the receiver here immediately as it is not
* suggested to do disable/reset or reset/disable at the same time.
*/
val = msm_read(port, UARTDM_DMEN);
val &= ~dma->enable_bit;
msm_write(port, val, UARTDM_DMEN);
if (mapped)
dma_unmap_single(dev, dma->phys, mapped, dma->dir);
}
static void msm_release_dma(struct msm_port *msm_port)
{
struct msm_dma *dma;
dma = &msm_port->tx_dma;
if (dma->chan) {
msm_stop_dma(&msm_port->uart, dma);
dma_release_channel(dma->chan);
}
memset(dma, 0, sizeof(*dma));
dma = &msm_port->rx_dma;
if (dma->chan) {
msm_stop_dma(&msm_port->uart, dma);
dma_release_channel(dma->chan);
kfree(dma->virt);
}
memset(dma, 0, sizeof(*dma));
}
static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base)
{
struct device *dev = msm_port->uart.dev;
struct dma_slave_config conf;
struct qcom_adm_peripheral_config periph_conf = {};
struct msm_dma *dma;
u32 crci = 0;
int ret;
dma = &msm_port->tx_dma;
/* allocate DMA resources, if available */
dma->chan = dma_request_chan(dev, "tx");
if (IS_ERR(dma->chan))
goto no_tx;
of_property_read_u32(dev->of_node, "qcom,tx-crci", &crci);
memset(&conf, 0, sizeof(conf));
conf.direction = DMA_MEM_TO_DEV;
conf.device_fc = true;
conf.dst_addr = base + UARTDM_TF;
conf.dst_maxburst = UARTDM_BURST_SIZE;
if (crci) {
conf.peripheral_config = &periph_conf;
conf.peripheral_size = sizeof(periph_conf);
periph_conf.crci = crci;
}
ret = dmaengine_slave_config(dma->chan, &conf);
if (ret)
goto rel_tx;
dma->dir = DMA_TO_DEVICE;
if (msm_port->is_uartdm < UARTDM_1P4)
dma->enable_bit = UARTDM_DMEN_TX_DM_ENABLE;
else
dma->enable_bit = UARTDM_DMEN_TX_BAM_ENABLE;
return;
rel_tx:
dma_release_channel(dma->chan);
no_tx:
memset(dma, 0, sizeof(*dma));
}
static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base)
{
struct device *dev = msm_port->uart.dev;
struct dma_slave_config conf;
struct qcom_adm_peripheral_config periph_conf = {};
struct msm_dma *dma;
u32 crci = 0;
int ret;
dma = &msm_port->rx_dma;
/* allocate DMA resources, if available */
dma->chan = dma_request_chan(dev, "rx");
if (IS_ERR(dma->chan))
goto no_rx;
of_property_read_u32(dev->of_node, "qcom,rx-crci", &crci);
dma->virt = kzalloc(UARTDM_RX_SIZE, GFP_KERNEL);
if (!dma->virt)
goto rel_rx;
memset(&conf, 0, sizeof(conf));
conf.direction = DMA_DEV_TO_MEM;
conf.device_fc = true;
conf.src_addr = base + UARTDM_RF;
conf.src_maxburst = UARTDM_BURST_SIZE;
if (crci) {
conf.peripheral_config = &periph_conf;
conf.peripheral_size = sizeof(periph_conf);
periph_conf.crci = crci;
}
ret = dmaengine_slave_config(dma->chan, &conf);
if (ret)
goto err;
dma->dir = DMA_FROM_DEVICE;
if (msm_port->is_uartdm < UARTDM_1P4)
dma->enable_bit = UARTDM_DMEN_RX_DM_ENABLE;
else
dma->enable_bit = UARTDM_DMEN_RX_BAM_ENABLE;
return;
err:
kfree(dma->virt);
rel_rx:
dma_release_channel(dma->chan);
no_rx:
memset(dma, 0, sizeof(*dma));
}
static inline void msm_wait_for_xmitr(struct uart_port *port)
{
unsigned int timeout = 500000;
while (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_EMPTY)) {
if (msm_read(port, MSM_UART_ISR) & MSM_UART_ISR_TX_READY)
break;
udelay(1);
if (!timeout--)
break;
}
msm_write(port, MSM_UART_CR_CMD_RESET_TX_READY, MSM_UART_CR);
}
static void msm_stop_tx(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
msm_port->imr &= ~MSM_UART_IMR_TXLEV;
msm_write(port, msm_port->imr, MSM_UART_IMR);
}
static void msm_start_tx(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
struct msm_dma *dma = &msm_port->tx_dma;
/* Already started in DMA mode */
if (dma->count)
return;
msm_port->imr |= MSM_UART_IMR_TXLEV;
msm_write(port, msm_port->imr, MSM_UART_IMR);
}
static void msm_reset_dm_count(struct uart_port *port, int count)
{
msm_wait_for_xmitr(port);
msm_write(port, count, UARTDM_NCF_TX);
msm_read(port, UARTDM_NCF_TX);
}
static void msm_complete_tx_dma(void *args)
{
struct msm_port *msm_port = args;
struct uart_port *port = &msm_port->uart;
struct circ_buf *xmit = &port->state->xmit;
struct msm_dma *dma = &msm_port->tx_dma;
struct dma_tx_state state;
unsigned long flags;
unsigned int count;
u32 val;
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_lock_irqsave(port, &flags);
/* Already stopped */
if (!dma->count)
goto done;
dmaengine_tx_status(dma->chan, dma->cookie, &state);
dma_unmap_single(port->dev, dma->phys, dma->count, dma->dir);
val = msm_read(port, UARTDM_DMEN);
val &= ~dma->enable_bit;
msm_write(port, val, UARTDM_DMEN);
if (msm_port->is_uartdm > UARTDM_1P3) {
msm_write(port, MSM_UART_CR_CMD_RESET_TX, MSM_UART_CR);
msm_write(port, MSM_UART_CR_TX_ENABLE, MSM_UART_CR);
}
count = dma->count - state.residue;
uart_xmit_advance(port, count);
dma->count = 0;
/* Restore "Tx FIFO below watermark" interrupt */
msm_port->imr |= MSM_UART_IMR_TXLEV;
msm_write(port, msm_port->imr, MSM_UART_IMR);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
msm_handle_tx(port);
done:
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_unlock_irqrestore(port, flags);
}
static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
{
struct circ_buf *xmit = &msm_port->uart.state->xmit;
struct uart_port *port = &msm_port->uart;
struct msm_dma *dma = &msm_port->tx_dma;
void *cpu_addr;
int ret;
u32 val;
cpu_addr = &xmit->buf[xmit->tail];
dma->phys = dma_map_single(port->dev, cpu_addr, count, dma->dir);
ret = dma_mapping_error(port->dev, dma->phys);
if (ret)
return ret;
dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys,
count, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT |
DMA_PREP_FENCE);
if (!dma->desc) {
ret = -EIO;
goto unmap;
}
dma->desc->callback = msm_complete_tx_dma;
dma->desc->callback_param = msm_port;
dma->cookie = dmaengine_submit(dma->desc);
ret = dma_submit_error(dma->cookie);
if (ret)
goto unmap;
/*
* Using DMA complete for Tx FIFO reload, no need for
* "Tx FIFO below watermark" one, disable it
*/
msm_port->imr &= ~MSM_UART_IMR_TXLEV;
msm_write(port, msm_port->imr, MSM_UART_IMR);
dma->count = count;
val = msm_read(port, UARTDM_DMEN);
val |= dma->enable_bit;
if (msm_port->is_uartdm < UARTDM_1P4)
msm_write(port, val, UARTDM_DMEN);
msm_reset_dm_count(port, count);
if (msm_port->is_uartdm > UARTDM_1P3)
msm_write(port, val, UARTDM_DMEN);
dma_async_issue_pending(dma->chan);
return 0;
unmap:
dma_unmap_single(port->dev, dma->phys, count, dma->dir);
return ret;
}
static void msm_complete_rx_dma(void *args)
{
struct msm_port *msm_port = args;
struct uart_port *port = &msm_port->uart;
struct tty_port *tport = &port->state->port;
struct msm_dma *dma = &msm_port->rx_dma;
int count = 0, i, sysrq;
unsigned long flags;
u32 val;
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_lock_irqsave(port, &flags);
/* Already stopped */
if (!dma->count)
goto done;
val = msm_read(port, UARTDM_DMEN);
val &= ~dma->enable_bit;
msm_write(port, val, UARTDM_DMEN);
if (msm_read(port, MSM_UART_SR) & MSM_UART_SR_OVERRUN) {
port->icount.overrun++;
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
msm_write(port, MSM_UART_CR_CMD_RESET_ERR, MSM_UART_CR);
}
count = msm_read(port, UARTDM_RX_TOTAL_SNAP);
port->icount.rx += count;
dma->count = 0;
dma_unmap_single(port->dev, dma->phys, UARTDM_RX_SIZE, dma->dir);
for (i = 0; i < count; i++) {
char flag = TTY_NORMAL;
if (msm_port->break_detected && dma->virt[i] == 0) {
port->icount.brk++;
flag = TTY_BREAK;
msm_port->break_detected = false;
if (uart_handle_break(port))
continue;
}
if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK))
flag = TTY_NORMAL;
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_unlock_irqrestore(port, flags);
sysrq = uart_handle_sysrq_char(port, dma->virt[i]);
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_lock_irqsave(port, &flags);
if (!sysrq)
tty_insert_flip_char(tport, dma->virt[i], flag);
}
msm_start_rx_dma(msm_port);
done:
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_unlock_irqrestore(port, flags);
if (count)
tty_flip_buffer_push(tport);
}
static void msm_start_rx_dma(struct msm_port *msm_port)
{
struct msm_dma *dma = &msm_port->rx_dma;
struct uart_port *uart = &msm_port->uart;
u32 val;
int ret;
2021-11-13 12:10:50 +00:00
if (IS_ENABLED(CONFIG_CONSOLE_POLL))
return;
if (!dma->chan)
return;
dma->phys = dma_map_single(uart->dev, dma->virt,
UARTDM_RX_SIZE, dma->dir);
ret = dma_mapping_error(uart->dev, dma->phys);
if (ret)
goto sw_mode;
dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys,
UARTDM_RX_SIZE, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
if (!dma->desc)
goto unmap;
dma->desc->callback = msm_complete_rx_dma;
dma->desc->callback_param = msm_port;
dma->cookie = dmaengine_submit(dma->desc);
ret = dma_submit_error(dma->cookie);
if (ret)
goto unmap;
/*
* Using DMA for FIFO off-load, no need for "Rx FIFO over
* watermark" or "stale" interrupts, disable them
*/
msm_port->imr &= ~(MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE);
/*
* Well, when DMA is ADM3 engine(implied by <= UARTDM v1.3),
* we need RXSTALE to flush input DMA fifo to memory
*/
if (msm_port->is_uartdm < UARTDM_1P4)
msm_port->imr |= MSM_UART_IMR_RXSTALE;
msm_write(uart, msm_port->imr, MSM_UART_IMR);
dma->count = UARTDM_RX_SIZE;
dma_async_issue_pending(dma->chan);
msm_write(uart, MSM_UART_CR_CMD_RESET_STALE_INT, MSM_UART_CR);
msm_write(uart, MSM_UART_CR_CMD_STALE_EVENT_ENABLE, MSM_UART_CR);
val = msm_read(uart, UARTDM_DMEN);
val |= dma->enable_bit;
if (msm_port->is_uartdm < UARTDM_1P4)
msm_write(uart, val, UARTDM_DMEN);
msm_write(uart, UARTDM_RX_SIZE, UARTDM_DMRX);
if (msm_port->is_uartdm > UARTDM_1P3)
msm_write(uart, val, UARTDM_DMEN);
return;
unmap:
dma_unmap_single(uart->dev, dma->phys, UARTDM_RX_SIZE, dma->dir);
sw_mode:
/*
* Switch from DMA to SW/FIFO mode. After clearing Rx BAM (UARTDM_DMEN),
* receiver must be reset.
*/
msm_write(uart, MSM_UART_CR_CMD_RESET_RX, MSM_UART_CR);
msm_write(uart, MSM_UART_CR_RX_ENABLE, MSM_UART_CR);
msm_write(uart, MSM_UART_CR_CMD_RESET_STALE_INT, MSM_UART_CR);
msm_write(uart, 0xFFFFFF, UARTDM_DMRX);
msm_write(uart, MSM_UART_CR_CMD_STALE_EVENT_ENABLE, MSM_UART_CR);
/* Re-enable RX interrupts */
msm_port->imr |= MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE;
msm_write(uart, msm_port->imr, MSM_UART_IMR);
}
static void msm_stop_rx(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
struct msm_dma *dma = &msm_port->rx_dma;
msm_port->imr &= ~(MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE);
msm_write(port, msm_port->imr, MSM_UART_IMR);
if (dma->chan)
msm_stop_dma(port, dma);
}
static void msm_enable_ms(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
msm_port->imr |= MSM_UART_IMR_DELTA_CTS;
msm_write(port, msm_port->imr, MSM_UART_IMR);
}
static void msm_handle_rx_dm(struct uart_port *port, unsigned int misr)
__must_hold(&port->lock)
{
struct tty_port *tport = &port->state->port;
unsigned int sr;
int count = 0;
struct msm_port *msm_port = to_msm_port(port);
if ((msm_read(port, MSM_UART_SR) & MSM_UART_SR_OVERRUN)) {
port->icount.overrun++;
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
msm_write(port, MSM_UART_CR_CMD_RESET_ERR, MSM_UART_CR);
}
if (misr & MSM_UART_IMR_RXSTALE) {
count = msm_read(port, UARTDM_RX_TOTAL_SNAP) -
msm_port->old_snap_state;
msm_port->old_snap_state = 0;
} else {
count = 4 * (msm_read(port, MSM_UART_RFWR));
msm_port->old_snap_state += count;
}
/* TODO: Precise error reporting */
port->icount.rx += count;
while (count > 0) {
unsigned char buf[4];
int sysrq, r_count, i;
sr = msm_read(port, MSM_UART_SR);
if ((sr & MSM_UART_SR_RX_READY) == 0) {
msm_port->old_snap_state -= count;
break;
}
ioread32_rep(port->membase + UARTDM_RF, buf, 1);
r_count = min_t(int, count, sizeof(buf));
for (i = 0; i < r_count; i++) {
char flag = TTY_NORMAL;
if (msm_port->break_detected && buf[i] == 0) {
port->icount.brk++;
flag = TTY_BREAK;
msm_port->break_detected = false;
if (uart_handle_break(port))
continue;
}
if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK))
flag = TTY_NORMAL;
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_unlock(port);
sysrq = uart_handle_sysrq_char(port, buf[i]);
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_lock(port);
if (!sysrq)
tty_insert_flip_char(tport, buf[i], flag);
}
count -= r_count;
}
tty_flip_buffer_push(tport);
tty: serial: msm: drop uart_port->lock before calling tty_flip_buffer_push() The current driver triggers a lockdep warning for if tty_flip_buffer_push() is called with uart_port->lock locked. This never shows up on UP kernels and comes up only on SMP kernels. Crash looks like this (produced with samsung.c driver): ----- [<c0014d58>] (unwind_backtrace+0x0/0xf8) from [<c0011908>] (show_stack+0x10/0x14) [<c0011908>] (show_stack+0x10/0x14) from [<c035da34>] (dump_stack+0x6c/0xac) [<c035da34>] (dump_stack+0x6c/0xac) from [<c01b59ac>] (do_raw_spin_unlock+0xc4/0xd8) [<c01b59ac>] (do_raw_spin_unlock+0xc4/0xd8) from [<c03627e4>] (_raw_spin_unlock_irqrestore+0xc/0) [<c03627e4>] (_raw_spin_unlock_irqrestore+0xc/0x38) from [<c020a1a8>] (s3c24xx_serial_rx_chars+0) [<c020a1a8>] (s3c24xx_serial_rx_chars+0x12c/0x260) from [<c020aae8>] (s3c64xx_serial_handle_irq+) [<c020aae8>] (s3c64xx_serial_handle_irq+0x48/0x60) from [<c006aaa0>] (handle_irq_event_percpu+0x) [<c006aaa0>] (handle_irq_event_percpu+0x50/0x194) from [<c006ac20>] (handle_irq_event+0x3c/0x5c) [<c006ac20>] (handle_irq_event+0x3c/0x5c) from [<c006d864>] (handle_fasteoi_irq+0x80/0x13c) [<c006d864>] (handle_fasteoi_irq+0x80/0x13c) from [<c006a4a4>] (generic_handle_irq+0x20/0x30) [<c006a4a4>] (generic_handle_irq+0x20/0x30) from [<c000f454>] (handle_IRQ+0x38/0x94) [<c000f454>] (handle_IRQ+0x38/0x94) from [<c0008538>] (gic_handle_irq+0x34/0x68) [<c0008538>] (gic_handle_irq+0x34/0x68) from [<c00123c0>] (__irq_svc+0x40/0x70) Exception stack(0xc04cdf70 to 0xc04cdfb8) df60: 00000000 00000000 0000166e 00000000 df80: c04cc000 c050278f c050278f 00000001 c04d444c 410fc0f4 c03649b0 00000000 dfa0: 00000001 c04cdfb8 c000f758 c000f75c 60070013 ffffffff [<c00123c0>] (__irq_svc+0x40/0x70) from [<c000f75c>] (arch_cpu_idle+0x28/0x30) [<c000f75c>] (arch_cpu_idle+0x28/0x30) from [<c0054888>] (cpu_startup_entry+0x5c/0x148) [<c0054888>] (cpu_startup_entry+0x5c/0x148) from [<c0497aa4>] (start_kernel+0x334/0x38c) BUG: spinlock lockup suspected on CPU#0, kworker/0:1/360 lock: s3c24xx_serial_ports+0x1d8/0x370, .magic: dead4ead, .owner: <none>/-1, .owner_cpu: -1 CPU: 0 PID: 360 Comm: kworker/0:1 Not tainted 3.11.0-rc6-next-20130819-00003-g75485f1 #2 Workqueue: events flush_to_ldisc [<c0014d58>] (unwind_backtrace+0x0/0xf8) from [<c0011908>] (show_stack+0x10/0x14) [<c0011908>] (show_stack+0x10/0x14) from [<c035da34>] (dump_stack+0x6c/0xac) [<c035da34>] (dump_stack+0x6c/0xac) from [<c01b581c>] (do_raw_spin_lock+0x100/0x17c) [<c01b581c>] (do_raw_spin_lock+0x100/0x17c) from [<c03628a0>] (_raw_spin_lock_irqsave+0x20/0x28) [<c03628a0>] (_raw_spin_lock_irqsave+0x20/0x28) from [<c0203224>] (uart_start+0x18/0x34) [<c0203224>] (uart_start+0x18/0x34) from [<c01ef890>] (__receive_buf+0x4b4/0x738) [<c01ef890>] (__receive_buf+0x4b4/0x738) from [<c01efb44>] (n_tty_receive_buf2+0x30/0x98) [<c01efb44>] (n_tty_receive_buf2+0x30/0x98) from [<c01f2ba8>] (flush_to_ldisc+0xec/0x138) [<c01f2ba8>] (flush_to_ldisc+0xec/0x138) from [<c0031af0>] (process_one_work+0xfc/0x348) [<c0031af0>] (process_one_work+0xfc/0x348) from [<c0032138>] (worker_thread+0x138/0x37c) [<c0032138>] (worker_thread+0x138/0x37c) from [<c0037a7c>] (kthread+0xa4/0xb0) [<c0037a7c>] (kthread+0xa4/0xb0) from [<c000e5f8>] (ret_from_fork+0x14/0x3c) ----- Release the port lock before calling tty_flip_buffer_push() and reacquire it after the call. Similar stuff was already done for few other drivers in the past, like: commit 2389b272168ceec056ca1d8a870a97fa9c26e11a Author: Thomas Gleixner <tglx@linutronix.de> Date: Tue May 29 21:53:50 2007 +0100 [ARM] 4417/1: Serial: Fix AMBA drivers locking Cc: David Brown <davidb@codeaurora.org> (maintainer:ARM/QUALCOMM MSM...) Cc: Daniel Walker <dwalker@fifo99.com> (maintainer:ARM/QUALCOMM MSM...) Cc: Bryan Huntsman <bryanh@codeaurora.org> (maintainer:ARM/QUALCOMM MSM...) Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-08-19 14:44:20 +00:00
if (misr & (MSM_UART_IMR_RXSTALE))
msm_write(port, MSM_UART_CR_CMD_RESET_STALE_INT, MSM_UART_CR);
msm_write(port, 0xFFFFFF, UARTDM_DMRX);
msm_write(port, MSM_UART_CR_CMD_STALE_EVENT_ENABLE, MSM_UART_CR);
/* Try to use DMA */
msm_start_rx_dma(msm_port);
}
static void msm_handle_rx(struct uart_port *port)
__must_hold(&port->lock)
{
struct tty_port *tport = &port->state->port;
unsigned int sr;
/*
* Handle overrun. My understanding of the hardware is that overrun
* is not tied to the RX buffer, so we handle the case out of band.
*/
if ((msm_read(port, MSM_UART_SR) & MSM_UART_SR_OVERRUN)) {
port->icount.overrun++;
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
msm_write(port, MSM_UART_CR_CMD_RESET_ERR, MSM_UART_CR);
}
/* and now the main RX loop */
while ((sr = msm_read(port, MSM_UART_SR)) & MSM_UART_SR_RX_READY) {
unsigned int c;
char flag = TTY_NORMAL;
tty: serial: msm: Fix sysrq spinlock recursion on non-DM The handle_rx() path calls uart_handle_sysrq_char() with the port lock held. This causes a spinlock recursion. Release and reacquire the lock here to avoid this. BUG: spinlock recursion on CPU#0, swapper/0 lock: msm_uart_ports+0x1e0/0x2d0, .magic: dead4ead, .owner: swapper/0, .owner_cpu: 0 CPU: 0 PID: 0 Comm: swapper Not tainted 3.17.0-rc7-00012-gb38ee8265941 #69 [<c0013964>] (unwind_backtrace) from [<c0011f74>] (show_stack+0x10/0x14) [<c0011f74>] (show_stack) from [<c004ed1c>] (do_raw_spin_lock+0x11c/0x13c) [<c004ed1c>] (do_raw_spin_lock) from [<c02d44c0>] (msm_console_write+0x78/0x188) [<c02d44c0>] (msm_console_write) from [<c0052880>] (call_console_drivers.constprop.22+0xb4/0x144) [<c0052880>] (call_console_drivers.constprop.22) from [<c0053570>] (console_unlock+0x27c/0x4ac) [<c0053570>] (console_unlock) from [<c0053bb4>] (vprintk_emit+0x1f4/0x5a8) [<c0053bb4>] (vprintk_emit) from [<c04ad0ac>] (printk+0x30/0x40) [<c04ad0ac>] (printk) from [<c02c2990>] (__handle_sysrq+0x58/0x1b8) [<c02c2990>] (__handle_sysrq) from [<c02d41b0>] (msm_irq+0x694/0x6f8) [<c02d41b0>] (msm_irq) from [<c0055740>] (handle_irq_event_percpu+0x58/0x270) [<c0055740>] (handle_irq_event_percpu) from [<c0055994>] (handle_irq_event+0x3c/0x5c) [<c0055994>] (handle_irq_event) from [<c0057e84>] (handle_level_irq+0x9c/0x138) [<c0057e84>] (handle_level_irq) from [<c005509c>] (generic_handle_irq+0x24/0x38) [<c005509c>] (generic_handle_irq) from [<c000f730>] (handle_IRQ+0x44/0xb0) [<c000f730>] (handle_IRQ) from [<c0008518>] (msm_vic_handle_irq+0x44/0x64) [<c0008518>] (msm_vic_handle_irq) from [<c04b5ac4>] (__irq_svc+0x44/0x7c) Exception stack(0xc0719f68 to 0xc0719fb0) 9f60: 00000001 00000001 00000000 c0722938 c0718000 c0769acc 9f80: 00000000 c0720098 c0769305 4117b362 c0769acc 00000000 01000000 c0719fb0 9fa0: c004cab0 c000f880 20000013 ffffffff [<c04b5ac4>] (__irq_svc) from [<c000f880>] (arch_cpu_idle+0x20/0x30) [<c000f880>] (arch_cpu_idle) from [<c004691c>] (cpu_startup_entry+0xf4/0x23c) [<c004691c>] (cpu_startup_entry) from [<c06d8b70>] (start_kernel+0x32c/0x394) Cc: Frank Rowand <frank.rowand@sonymobile.com> Cc: Daniel Thompson <daniel.thompson@linaro.org> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-10-29 18:14:37 +00:00
int sysrq;
c = msm_read(port, MSM_UART_RF);
if (sr & MSM_UART_SR_RX_BREAK) {
port->icount.brk++;
if (uart_handle_break(port))
continue;
} else if (sr & MSM_UART_SR_PAR_FRAME_ERR) {
port->icount.frame++;
} else {
port->icount.rx++;
}
/* Mask conditions we're ignoring. */
sr &= port->read_status_mask;
if (sr & MSM_UART_SR_RX_BREAK)
flag = TTY_BREAK;
else if (sr & MSM_UART_SR_PAR_FRAME_ERR)
flag = TTY_FRAME;
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_unlock(port);
tty: serial: msm: Fix sysrq spinlock recursion on non-DM The handle_rx() path calls uart_handle_sysrq_char() with the port lock held. This causes a spinlock recursion. Release and reacquire the lock here to avoid this. BUG: spinlock recursion on CPU#0, swapper/0 lock: msm_uart_ports+0x1e0/0x2d0, .magic: dead4ead, .owner: swapper/0, .owner_cpu: 0 CPU: 0 PID: 0 Comm: swapper Not tainted 3.17.0-rc7-00012-gb38ee8265941 #69 [<c0013964>] (unwind_backtrace) from [<c0011f74>] (show_stack+0x10/0x14) [<c0011f74>] (show_stack) from [<c004ed1c>] (do_raw_spin_lock+0x11c/0x13c) [<c004ed1c>] (do_raw_spin_lock) from [<c02d44c0>] (msm_console_write+0x78/0x188) [<c02d44c0>] (msm_console_write) from [<c0052880>] (call_console_drivers.constprop.22+0xb4/0x144) [<c0052880>] (call_console_drivers.constprop.22) from [<c0053570>] (console_unlock+0x27c/0x4ac) [<c0053570>] (console_unlock) from [<c0053bb4>] (vprintk_emit+0x1f4/0x5a8) [<c0053bb4>] (vprintk_emit) from [<c04ad0ac>] (printk+0x30/0x40) [<c04ad0ac>] (printk) from [<c02c2990>] (__handle_sysrq+0x58/0x1b8) [<c02c2990>] (__handle_sysrq) from [<c02d41b0>] (msm_irq+0x694/0x6f8) [<c02d41b0>] (msm_irq) from [<c0055740>] (handle_irq_event_percpu+0x58/0x270) [<c0055740>] (handle_irq_event_percpu) from [<c0055994>] (handle_irq_event+0x3c/0x5c) [<c0055994>] (handle_irq_event) from [<c0057e84>] (handle_level_irq+0x9c/0x138) [<c0057e84>] (handle_level_irq) from [<c005509c>] (generic_handle_irq+0x24/0x38) [<c005509c>] (generic_handle_irq) from [<c000f730>] (handle_IRQ+0x44/0xb0) [<c000f730>] (handle_IRQ) from [<c0008518>] (msm_vic_handle_irq+0x44/0x64) [<c0008518>] (msm_vic_handle_irq) from [<c04b5ac4>] (__irq_svc+0x44/0x7c) Exception stack(0xc0719f68 to 0xc0719fb0) 9f60: 00000001 00000001 00000000 c0722938 c0718000 c0769acc 9f80: 00000000 c0720098 c0769305 4117b362 c0769acc 00000000 01000000 c0719fb0 9fa0: c004cab0 c000f880 20000013 ffffffff [<c04b5ac4>] (__irq_svc) from [<c000f880>] (arch_cpu_idle+0x20/0x30) [<c000f880>] (arch_cpu_idle) from [<c004691c>] (cpu_startup_entry+0xf4/0x23c) [<c004691c>] (cpu_startup_entry) from [<c06d8b70>] (start_kernel+0x32c/0x394) Cc: Frank Rowand <frank.rowand@sonymobile.com> Cc: Daniel Thompson <daniel.thompson@linaro.org> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-10-29 18:14:37 +00:00
sysrq = uart_handle_sysrq_char(port, c);
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_lock(port);
tty: serial: msm: Fix sysrq spinlock recursion on non-DM The handle_rx() path calls uart_handle_sysrq_char() with the port lock held. This causes a spinlock recursion. Release and reacquire the lock here to avoid this. BUG: spinlock recursion on CPU#0, swapper/0 lock: msm_uart_ports+0x1e0/0x2d0, .magic: dead4ead, .owner: swapper/0, .owner_cpu: 0 CPU: 0 PID: 0 Comm: swapper Not tainted 3.17.0-rc7-00012-gb38ee8265941 #69 [<c0013964>] (unwind_backtrace) from [<c0011f74>] (show_stack+0x10/0x14) [<c0011f74>] (show_stack) from [<c004ed1c>] (do_raw_spin_lock+0x11c/0x13c) [<c004ed1c>] (do_raw_spin_lock) from [<c02d44c0>] (msm_console_write+0x78/0x188) [<c02d44c0>] (msm_console_write) from [<c0052880>] (call_console_drivers.constprop.22+0xb4/0x144) [<c0052880>] (call_console_drivers.constprop.22) from [<c0053570>] (console_unlock+0x27c/0x4ac) [<c0053570>] (console_unlock) from [<c0053bb4>] (vprintk_emit+0x1f4/0x5a8) [<c0053bb4>] (vprintk_emit) from [<c04ad0ac>] (printk+0x30/0x40) [<c04ad0ac>] (printk) from [<c02c2990>] (__handle_sysrq+0x58/0x1b8) [<c02c2990>] (__handle_sysrq) from [<c02d41b0>] (msm_irq+0x694/0x6f8) [<c02d41b0>] (msm_irq) from [<c0055740>] (handle_irq_event_percpu+0x58/0x270) [<c0055740>] (handle_irq_event_percpu) from [<c0055994>] (handle_irq_event+0x3c/0x5c) [<c0055994>] (handle_irq_event) from [<c0057e84>] (handle_level_irq+0x9c/0x138) [<c0057e84>] (handle_level_irq) from [<c005509c>] (generic_handle_irq+0x24/0x38) [<c005509c>] (generic_handle_irq) from [<c000f730>] (handle_IRQ+0x44/0xb0) [<c000f730>] (handle_IRQ) from [<c0008518>] (msm_vic_handle_irq+0x44/0x64) [<c0008518>] (msm_vic_handle_irq) from [<c04b5ac4>] (__irq_svc+0x44/0x7c) Exception stack(0xc0719f68 to 0xc0719fb0) 9f60: 00000001 00000001 00000000 c0722938 c0718000 c0769acc 9f80: 00000000 c0720098 c0769305 4117b362 c0769acc 00000000 01000000 c0719fb0 9fa0: c004cab0 c000f880 20000013 ffffffff [<c04b5ac4>] (__irq_svc) from [<c000f880>] (arch_cpu_idle+0x20/0x30) [<c000f880>] (arch_cpu_idle) from [<c004691c>] (cpu_startup_entry+0xf4/0x23c) [<c004691c>] (cpu_startup_entry) from [<c06d8b70>] (start_kernel+0x32c/0x394) Cc: Frank Rowand <frank.rowand@sonymobile.com> Cc: Daniel Thompson <daniel.thompson@linaro.org> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-10-29 18:14:37 +00:00
if (!sysrq)
tty_insert_flip_char(tport, c, flag);
}
tty_flip_buffer_push(tport);
}
static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count)
{
struct circ_buf *xmit = &port->state->xmit;
struct msm_port *msm_port = to_msm_port(port);
unsigned int num_chars;
unsigned int tf_pointer = 0;
void __iomem *tf;
if (msm_port->is_uartdm)
tf = port->membase + UARTDM_TF;
else
tf = port->membase + MSM_UART_TF;
if (tx_count && msm_port->is_uartdm)
msm_reset_dm_count(port, tx_count);
while (tf_pointer < tx_count) {
int i;
char buf[4] = { 0 };
if (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_READY))
break;
if (msm_port->is_uartdm)
num_chars = min(tx_count - tf_pointer,
(unsigned int)sizeof(buf));
else
num_chars = 1;
for (i = 0; i < num_chars; i++)
buf[i] = xmit->buf[xmit->tail + i];
iowrite32_rep(tf, buf, 1);
uart_xmit_advance(port, num_chars);
tf_pointer += num_chars;
}
/* disable tx interrupts if nothing more to send */
if (uart_circ_empty(xmit))
msm_stop_tx(port);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
}
static void msm_handle_tx(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
struct circ_buf *xmit = &msm_port->uart.state->xmit;
struct msm_dma *dma = &msm_port->tx_dma;
unsigned int pio_count, dma_count, dma_min;
char buf[4] = { 0 };
void __iomem *tf;
int err = 0;
if (port->x_char) {
if (msm_port->is_uartdm)
tf = port->membase + UARTDM_TF;
else
tf = port->membase + MSM_UART_TF;
buf[0] = port->x_char;
if (msm_port->is_uartdm)
msm_reset_dm_count(port, 1);
iowrite32_rep(tf, buf, 1);
port->icount.tx++;
port->x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
msm_stop_tx(port);
return;
}
pio_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
dma_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
dma_min = 1; /* Always DMA */
if (msm_port->is_uartdm > UARTDM_1P3) {
dma_count = UARTDM_TX_AIGN(dma_count);
dma_min = UARTDM_BURST_SIZE;
} else {
if (dma_count > UARTDM_TX_MAX)
dma_count = UARTDM_TX_MAX;
}
if (pio_count > port->fifosize)
pio_count = port->fifosize;
if (!dma->chan || dma_count < dma_min)
msm_handle_tx_pio(port, pio_count);
else
err = msm_handle_tx_dma(msm_port, dma_count);
if (err) /* fall back to PIO mode */
msm_handle_tx_pio(port, pio_count);
}
static void msm_handle_delta_cts(struct uart_port *port)
{
msm_write(port, MSM_UART_CR_CMD_RESET_CTS, MSM_UART_CR);
port->icount.cts++;
wake_up_interruptible(&port->state->port.delta_msr_wait);
}
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;
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_lock_irqsave(port, &flags);
misr = msm_read(port, MSM_UART_MISR);
msm_write(port, 0, MSM_UART_IMR); /* disable interrupt */
if (misr & MSM_UART_IMR_RXBREAK_START) {
msm_port->break_detected = true;
msm_write(port, MSM_UART_CR_CMD_RESET_RXBREAK_START, MSM_UART_CR);
}
if (misr & (MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE)) {
if (dma->count) {
val = MSM_UART_CR_CMD_STALE_EVENT_DISABLE;
msm_write(port, val, MSM_UART_CR);
val = MSM_UART_CR_CMD_RESET_STALE_INT;
msm_write(port, val, MSM_UART_CR);
/*
* Flush DMA input fifo to memory, this will also
* trigger DMA RX completion
*/
dmaengine_terminate_all(dma->chan);
} else if (msm_port->is_uartdm) {
msm_handle_rx_dm(port, misr);
} else {
msm_handle_rx(port);
}
}
if (misr & MSM_UART_IMR_TXLEV)
msm_handle_tx(port);
if (misr & MSM_UART_IMR_DELTA_CTS)
msm_handle_delta_cts(port);
msm_write(port, msm_port->imr, MSM_UART_IMR); /* restore interrupt */
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_unlock_irqrestore(port, flags);
return IRQ_HANDLED;
}
static unsigned int msm_tx_empty(struct uart_port *port)
{
return (msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0;
}
static unsigned int msm_get_mctrl(struct uart_port *port)
{
return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS;
}
static void msm_reset(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
tty: serial: msm_serial: Fix flow control hci_qca interfaces to the wcn3990 via a uart_dm on the msm8998 mtp and Lenovo Miix 630 laptop. As part of initializing the wcn3990, hci_qca disables flow, configures the uart baudrate, and then reenables flow - at which point an event is expected to be received over the uart from the wcn3990. It is observed that this event comes after the baudrate change but before hci_qca re-enables flow. This is unexpected, and is a result of msm_reset() being broken. According to the uart_dm hardware documentation, it is recommended that automatic hardware flow control be enabled by setting RX_RDY_CTL. Auto hw flow control will manage RFR based on the configured watermark. When there is space to receive data, the hw will assert RFR. When the watermark is hit, the hw will de-assert RFR. The hardware documentation indicates that RFR can me manually managed via CR when RX_RDY_CTL is not set. SET_RFR asserts RFR, and RESET_RFR de-asserts RFR. msm_reset() is broken because after resetting the hardware, it unconditionally asserts RFR via SET_RFR. This enables flow regardless of the current configuration, and would undo a previous flow disable operation. It should instead de-assert RFR via RESET_RFR to block flow until the hardware is reconfigured. msm_serial should rely on the client to specify that flow should be enabled, either via mctrl() or the termios structure, and only assert RFR in response to those triggers. Fixes: 04896a77a97b ("msm_serial: serial driver for MSM7K onboard serial peripheral.") Signed-off-by: Jeffrey Hugo <jeffrey.l.hugo@gmail.com> Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org> Cc: stable <stable@vger.kernel.org> Reviewed-by: Andy Gross <agross@kernel.org> Link: https://lore.kernel.org/r/20191021154616.25457-1-jeffrey.l.hugo@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-10-21 15:46:16 +00:00
unsigned int mr;
/* reset everything */
msm_write(port, MSM_UART_CR_CMD_RESET_RX, MSM_UART_CR);
msm_write(port, MSM_UART_CR_CMD_RESET_TX, MSM_UART_CR);
msm_write(port, MSM_UART_CR_CMD_RESET_ERR, MSM_UART_CR);
msm_write(port, MSM_UART_CR_CMD_RESET_BREAK_INT, MSM_UART_CR);
msm_write(port, MSM_UART_CR_CMD_RESET_CTS, MSM_UART_CR);
msm_write(port, MSM_UART_CR_CMD_RESET_RFR, MSM_UART_CR);
mr = msm_read(port, MSM_UART_MR1);
mr &= ~MSM_UART_MR1_RX_RDY_CTL;
msm_write(port, mr, MSM_UART_MR1);
/* Disable DM modes */
if (msm_port->is_uartdm)
msm_write(port, 0, UARTDM_DMEN);
}
static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
unsigned int mr;
mr = msm_read(port, MSM_UART_MR1);
if (!(mctrl & TIOCM_RTS)) {
mr &= ~MSM_UART_MR1_RX_RDY_CTL;
msm_write(port, mr, MSM_UART_MR1);
msm_write(port, MSM_UART_CR_CMD_RESET_RFR, MSM_UART_CR);
} else {
mr |= MSM_UART_MR1_RX_RDY_CTL;
msm_write(port, mr, MSM_UART_MR1);
}
}
static void msm_break_ctl(struct uart_port *port, int break_ctl)
{
if (break_ctl)
msm_write(port, MSM_UART_CR_CMD_START_BREAK, MSM_UART_CR);
else
msm_write(port, MSM_UART_CR_CMD_STOP_BREAK, MSM_UART_CR);
}
struct msm_baud_map {
u16 divisor;
u8 code;
u8 rxstale;
};
static const struct msm_baud_map *
tty: serial: msm: Support more bauds The msm_find_best_baud() function is written with the assumption that the port->uartclk rate is fixed to a particular rate at boot time, but now this driver changes that clk rate at runtime when the baud is changed. The way the hardware works is that an input clk rate comes from the clk controller into the uart hw block. That rate is typically 1843200 or 3686400 Hz. That rate can then be divided by an internal divider in the hw block to achieve a particular baud on the serial wire. msm_find_best_baud() is looking for that divider value. A few things are wrong with the way the code is written. First, it assumes that the maximum baud that the uart can support if the clk rate is fixed at boot is 460800, which would correspond to an input clk rate of 230400 * 16 == 3686400 Hz. Except some devices have a boot rate of 1843200 Hz or max baud of 115200, so achieving 230400 on those devices doesn't work at all because we don't increase the clk rate unless max baud is 460800. Second, we can't achieve bauds higher than 460800 that require anything besides a divisor of 1, because we always call msm_find_best_baud() with a fixed port->uartclk rate that will eventually be changed after we calculate the divisor. So if we need to get a baud of 500000, we'll just multiply that by 16 and hope that the clk can give us 500000 * 16 == 8000000 Hz, which it typically can't do. To really achieve 500000 baud, we need to get an input clk rate of 24000000 Hz and then divide that by 3 inside the uart hardware. Finally, we return success for bauds even when we can't actually achieve them. This means that when the user asks for 500000 baud, we actually get 921600 right now, but the user doesn't know that. Fix all of this by searching through the divisor and clk rate space with a combination of clk_round_rate() and baud calculations, keeping track of the best clk rate and divisor we find if we can't get an exact match. Typically we can get an exact match with a divisor of 1, but sometimes we need to keep track and try more frequencies. On my msm8916 device, this results in all standard bauds in baud_table being supported except for 1800, 576000, 1152000, and 4000000. Fixes: 850b37a71bde ("tty: serial: msm: Remove 115.2 Kbps maximum baud rate limitation") Cc: "Ivan T. Ivanov" <iivanov.xz@gmail.com> Cc: Matthew McClintock <mmcclint@codeaurora.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Andy Gross <andy.gross@linaro.org> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Tested-by: Cristian Prundeanu <cprundea@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-03-25 21:35:49 +00:00
msm_find_best_baud(struct uart_port *port, unsigned int baud,
unsigned long *rate)
{
struct msm_port *msm_port = to_msm_port(port);
tty: serial: msm: Support more bauds The msm_find_best_baud() function is written with the assumption that the port->uartclk rate is fixed to a particular rate at boot time, but now this driver changes that clk rate at runtime when the baud is changed. The way the hardware works is that an input clk rate comes from the clk controller into the uart hw block. That rate is typically 1843200 or 3686400 Hz. That rate can then be divided by an internal divider in the hw block to achieve a particular baud on the serial wire. msm_find_best_baud() is looking for that divider value. A few things are wrong with the way the code is written. First, it assumes that the maximum baud that the uart can support if the clk rate is fixed at boot is 460800, which would correspond to an input clk rate of 230400 * 16 == 3686400 Hz. Except some devices have a boot rate of 1843200 Hz or max baud of 115200, so achieving 230400 on those devices doesn't work at all because we don't increase the clk rate unless max baud is 460800. Second, we can't achieve bauds higher than 460800 that require anything besides a divisor of 1, because we always call msm_find_best_baud() with a fixed port->uartclk rate that will eventually be changed after we calculate the divisor. So if we need to get a baud of 500000, we'll just multiply that by 16 and hope that the clk can give us 500000 * 16 == 8000000 Hz, which it typically can't do. To really achieve 500000 baud, we need to get an input clk rate of 24000000 Hz and then divide that by 3 inside the uart hardware. Finally, we return success for bauds even when we can't actually achieve them. This means that when the user asks for 500000 baud, we actually get 921600 right now, but the user doesn't know that. Fix all of this by searching through the divisor and clk rate space with a combination of clk_round_rate() and baud calculations, keeping track of the best clk rate and divisor we find if we can't get an exact match. Typically we can get an exact match with a divisor of 1, but sometimes we need to keep track and try more frequencies. On my msm8916 device, this results in all standard bauds in baud_table being supported except for 1800, 576000, 1152000, and 4000000. Fixes: 850b37a71bde ("tty: serial: msm: Remove 115.2 Kbps maximum baud rate limitation") Cc: "Ivan T. Ivanov" <iivanov.xz@gmail.com> Cc: Matthew McClintock <mmcclint@codeaurora.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Andy Gross <andy.gross@linaro.org> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Tested-by: Cristian Prundeanu <cprundea@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-03-25 21:35:49 +00:00
unsigned int divisor, result;
unsigned long target, old, best_rate = 0, diff, best_diff = ULONG_MAX;
const struct msm_baud_map *entry, *end, *best;
static const struct msm_baud_map table[] = {
{ 1, 0xff, 31 },
tty: serial: msm: Support more bauds The msm_find_best_baud() function is written with the assumption that the port->uartclk rate is fixed to a particular rate at boot time, but now this driver changes that clk rate at runtime when the baud is changed. The way the hardware works is that an input clk rate comes from the clk controller into the uart hw block. That rate is typically 1843200 or 3686400 Hz. That rate can then be divided by an internal divider in the hw block to achieve a particular baud on the serial wire. msm_find_best_baud() is looking for that divider value. A few things are wrong with the way the code is written. First, it assumes that the maximum baud that the uart can support if the clk rate is fixed at boot is 460800, which would correspond to an input clk rate of 230400 * 16 == 3686400 Hz. Except some devices have a boot rate of 1843200 Hz or max baud of 115200, so achieving 230400 on those devices doesn't work at all because we don't increase the clk rate unless max baud is 460800. Second, we can't achieve bauds higher than 460800 that require anything besides a divisor of 1, because we always call msm_find_best_baud() with a fixed port->uartclk rate that will eventually be changed after we calculate the divisor. So if we need to get a baud of 500000, we'll just multiply that by 16 and hope that the clk can give us 500000 * 16 == 8000000 Hz, which it typically can't do. To really achieve 500000 baud, we need to get an input clk rate of 24000000 Hz and then divide that by 3 inside the uart hardware. Finally, we return success for bauds even when we can't actually achieve them. This means that when the user asks for 500000 baud, we actually get 921600 right now, but the user doesn't know that. Fix all of this by searching through the divisor and clk rate space with a combination of clk_round_rate() and baud calculations, keeping track of the best clk rate and divisor we find if we can't get an exact match. Typically we can get an exact match with a divisor of 1, but sometimes we need to keep track and try more frequencies. On my msm8916 device, this results in all standard bauds in baud_table being supported except for 1800, 576000, 1152000, and 4000000. Fixes: 850b37a71bde ("tty: serial: msm: Remove 115.2 Kbps maximum baud rate limitation") Cc: "Ivan T. Ivanov" <iivanov.xz@gmail.com> Cc: Matthew McClintock <mmcclint@codeaurora.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Andy Gross <andy.gross@linaro.org> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Tested-by: Cristian Prundeanu <cprundea@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-03-25 21:35:49 +00:00
{ 2, 0xee, 16 },
{ 3, 0xdd, 8 },
{ 4, 0xcc, 6 },
{ 6, 0xbb, 6 },
{ 8, 0xaa, 6 },
{ 12, 0x99, 6 },
{ 16, 0x88, 1 },
{ 24, 0x77, 1 },
{ 32, 0x66, 1 },
{ 48, 0x55, 1 },
{ 96, 0x44, 1 },
{ 192, 0x33, 1 },
{ 384, 0x22, 1 },
{ 768, 0x11, 1 },
{ 1536, 0x00, 1 },
};
tty: serial: msm: Support more bauds The msm_find_best_baud() function is written with the assumption that the port->uartclk rate is fixed to a particular rate at boot time, but now this driver changes that clk rate at runtime when the baud is changed. The way the hardware works is that an input clk rate comes from the clk controller into the uart hw block. That rate is typically 1843200 or 3686400 Hz. That rate can then be divided by an internal divider in the hw block to achieve a particular baud on the serial wire. msm_find_best_baud() is looking for that divider value. A few things are wrong with the way the code is written. First, it assumes that the maximum baud that the uart can support if the clk rate is fixed at boot is 460800, which would correspond to an input clk rate of 230400 * 16 == 3686400 Hz. Except some devices have a boot rate of 1843200 Hz or max baud of 115200, so achieving 230400 on those devices doesn't work at all because we don't increase the clk rate unless max baud is 460800. Second, we can't achieve bauds higher than 460800 that require anything besides a divisor of 1, because we always call msm_find_best_baud() with a fixed port->uartclk rate that will eventually be changed after we calculate the divisor. So if we need to get a baud of 500000, we'll just multiply that by 16 and hope that the clk can give us 500000 * 16 == 8000000 Hz, which it typically can't do. To really achieve 500000 baud, we need to get an input clk rate of 24000000 Hz and then divide that by 3 inside the uart hardware. Finally, we return success for bauds even when we can't actually achieve them. This means that when the user asks for 500000 baud, we actually get 921600 right now, but the user doesn't know that. Fix all of this by searching through the divisor and clk rate space with a combination of clk_round_rate() and baud calculations, keeping track of the best clk rate and divisor we find if we can't get an exact match. Typically we can get an exact match with a divisor of 1, but sometimes we need to keep track and try more frequencies. On my msm8916 device, this results in all standard bauds in baud_table being supported except for 1800, 576000, 1152000, and 4000000. Fixes: 850b37a71bde ("tty: serial: msm: Remove 115.2 Kbps maximum baud rate limitation") Cc: "Ivan T. Ivanov" <iivanov.xz@gmail.com> Cc: Matthew McClintock <mmcclint@codeaurora.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Andy Gross <andy.gross@linaro.org> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Tested-by: Cristian Prundeanu <cprundea@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-03-25 21:35:49 +00:00
best = table; /* Default to smallest divider */
target = clk_round_rate(msm_port->clk, 16 * baud);
divisor = DIV_ROUND_CLOSEST(target, 16 * baud);
end = table + ARRAY_SIZE(table);
entry = table;
while (entry < end) {
if (entry->divisor <= divisor) {
result = target / entry->divisor / 16;
diff = abs(result - baud);
/* Keep track of best entry */
if (diff < best_diff) {
best_diff = diff;
best = entry;
best_rate = target;
}
tty: serial: msm: Support more bauds The msm_find_best_baud() function is written with the assumption that the port->uartclk rate is fixed to a particular rate at boot time, but now this driver changes that clk rate at runtime when the baud is changed. The way the hardware works is that an input clk rate comes from the clk controller into the uart hw block. That rate is typically 1843200 or 3686400 Hz. That rate can then be divided by an internal divider in the hw block to achieve a particular baud on the serial wire. msm_find_best_baud() is looking for that divider value. A few things are wrong with the way the code is written. First, it assumes that the maximum baud that the uart can support if the clk rate is fixed at boot is 460800, which would correspond to an input clk rate of 230400 * 16 == 3686400 Hz. Except some devices have a boot rate of 1843200 Hz or max baud of 115200, so achieving 230400 on those devices doesn't work at all because we don't increase the clk rate unless max baud is 460800. Second, we can't achieve bauds higher than 460800 that require anything besides a divisor of 1, because we always call msm_find_best_baud() with a fixed port->uartclk rate that will eventually be changed after we calculate the divisor. So if we need to get a baud of 500000, we'll just multiply that by 16 and hope that the clk can give us 500000 * 16 == 8000000 Hz, which it typically can't do. To really achieve 500000 baud, we need to get an input clk rate of 24000000 Hz and then divide that by 3 inside the uart hardware. Finally, we return success for bauds even when we can't actually achieve them. This means that when the user asks for 500000 baud, we actually get 921600 right now, but the user doesn't know that. Fix all of this by searching through the divisor and clk rate space with a combination of clk_round_rate() and baud calculations, keeping track of the best clk rate and divisor we find if we can't get an exact match. Typically we can get an exact match with a divisor of 1, but sometimes we need to keep track and try more frequencies. On my msm8916 device, this results in all standard bauds in baud_table being supported except for 1800, 576000, 1152000, and 4000000. Fixes: 850b37a71bde ("tty: serial: msm: Remove 115.2 Kbps maximum baud rate limitation") Cc: "Ivan T. Ivanov" <iivanov.xz@gmail.com> Cc: Matthew McClintock <mmcclint@codeaurora.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Andy Gross <andy.gross@linaro.org> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Tested-by: Cristian Prundeanu <cprundea@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-03-25 21:35:49 +00:00
if (result == baud)
break;
} else if (entry->divisor > divisor) {
old = target;
target = clk_round_rate(msm_port->clk, old + 1);
/*
* The rate didn't get any faster so we can't do
* better at dividing it down
*/
if (target == old)
break;
/* Start the divisor search over at this new rate */
entry = table;
divisor = DIV_ROUND_CLOSEST(target, 16 * baud);
continue;
}
entry++;
}
tty: serial: msm: Support more bauds The msm_find_best_baud() function is written with the assumption that the port->uartclk rate is fixed to a particular rate at boot time, but now this driver changes that clk rate at runtime when the baud is changed. The way the hardware works is that an input clk rate comes from the clk controller into the uart hw block. That rate is typically 1843200 or 3686400 Hz. That rate can then be divided by an internal divider in the hw block to achieve a particular baud on the serial wire. msm_find_best_baud() is looking for that divider value. A few things are wrong with the way the code is written. First, it assumes that the maximum baud that the uart can support if the clk rate is fixed at boot is 460800, which would correspond to an input clk rate of 230400 * 16 == 3686400 Hz. Except some devices have a boot rate of 1843200 Hz or max baud of 115200, so achieving 230400 on those devices doesn't work at all because we don't increase the clk rate unless max baud is 460800. Second, we can't achieve bauds higher than 460800 that require anything besides a divisor of 1, because we always call msm_find_best_baud() with a fixed port->uartclk rate that will eventually be changed after we calculate the divisor. So if we need to get a baud of 500000, we'll just multiply that by 16 and hope that the clk can give us 500000 * 16 == 8000000 Hz, which it typically can't do. To really achieve 500000 baud, we need to get an input clk rate of 24000000 Hz and then divide that by 3 inside the uart hardware. Finally, we return success for bauds even when we can't actually achieve them. This means that when the user asks for 500000 baud, we actually get 921600 right now, but the user doesn't know that. Fix all of this by searching through the divisor and clk rate space with a combination of clk_round_rate() and baud calculations, keeping track of the best clk rate and divisor we find if we can't get an exact match. Typically we can get an exact match with a divisor of 1, but sometimes we need to keep track and try more frequencies. On my msm8916 device, this results in all standard bauds in baud_table being supported except for 1800, 576000, 1152000, and 4000000. Fixes: 850b37a71bde ("tty: serial: msm: Remove 115.2 Kbps maximum baud rate limitation") Cc: "Ivan T. Ivanov" <iivanov.xz@gmail.com> Cc: Matthew McClintock <mmcclint@codeaurora.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Andy Gross <andy.gross@linaro.org> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Tested-by: Cristian Prundeanu <cprundea@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-03-25 21:35:49 +00:00
*rate = best_rate;
return best;
}
static int msm_set_baud_rate(struct uart_port *port, unsigned int baud,
unsigned long *saved_flags)
__must_hold(&port->lock)
{
unsigned int rxstale, watermark, mask;
struct msm_port *msm_port = to_msm_port(port);
const struct msm_baud_map *entry;
tty: serial: msm: Support more bauds The msm_find_best_baud() function is written with the assumption that the port->uartclk rate is fixed to a particular rate at boot time, but now this driver changes that clk rate at runtime when the baud is changed. The way the hardware works is that an input clk rate comes from the clk controller into the uart hw block. That rate is typically 1843200 or 3686400 Hz. That rate can then be divided by an internal divider in the hw block to achieve a particular baud on the serial wire. msm_find_best_baud() is looking for that divider value. A few things are wrong with the way the code is written. First, it assumes that the maximum baud that the uart can support if the clk rate is fixed at boot is 460800, which would correspond to an input clk rate of 230400 * 16 == 3686400 Hz. Except some devices have a boot rate of 1843200 Hz or max baud of 115200, so achieving 230400 on those devices doesn't work at all because we don't increase the clk rate unless max baud is 460800. Second, we can't achieve bauds higher than 460800 that require anything besides a divisor of 1, because we always call msm_find_best_baud() with a fixed port->uartclk rate that will eventually be changed after we calculate the divisor. So if we need to get a baud of 500000, we'll just multiply that by 16 and hope that the clk can give us 500000 * 16 == 8000000 Hz, which it typically can't do. To really achieve 500000 baud, we need to get an input clk rate of 24000000 Hz and then divide that by 3 inside the uart hardware. Finally, we return success for bauds even when we can't actually achieve them. This means that when the user asks for 500000 baud, we actually get 921600 right now, but the user doesn't know that. Fix all of this by searching through the divisor and clk rate space with a combination of clk_round_rate() and baud calculations, keeping track of the best clk rate and divisor we find if we can't get an exact match. Typically we can get an exact match with a divisor of 1, but sometimes we need to keep track and try more frequencies. On my msm8916 device, this results in all standard bauds in baud_table being supported except for 1800, 576000, 1152000, and 4000000. Fixes: 850b37a71bde ("tty: serial: msm: Remove 115.2 Kbps maximum baud rate limitation") Cc: "Ivan T. Ivanov" <iivanov.xz@gmail.com> Cc: Matthew McClintock <mmcclint@codeaurora.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Andy Gross <andy.gross@linaro.org> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Tested-by: Cristian Prundeanu <cprundea@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-03-25 21:35:49 +00:00
unsigned long flags, rate;
flags = *saved_flags;
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_unlock_irqrestore(port, flags);
tty: serial: msm: Support more bauds The msm_find_best_baud() function is written with the assumption that the port->uartclk rate is fixed to a particular rate at boot time, but now this driver changes that clk rate at runtime when the baud is changed. The way the hardware works is that an input clk rate comes from the clk controller into the uart hw block. That rate is typically 1843200 or 3686400 Hz. That rate can then be divided by an internal divider in the hw block to achieve a particular baud on the serial wire. msm_find_best_baud() is looking for that divider value. A few things are wrong with the way the code is written. First, it assumes that the maximum baud that the uart can support if the clk rate is fixed at boot is 460800, which would correspond to an input clk rate of 230400 * 16 == 3686400 Hz. Except some devices have a boot rate of 1843200 Hz or max baud of 115200, so achieving 230400 on those devices doesn't work at all because we don't increase the clk rate unless max baud is 460800. Second, we can't achieve bauds higher than 460800 that require anything besides a divisor of 1, because we always call msm_find_best_baud() with a fixed port->uartclk rate that will eventually be changed after we calculate the divisor. So if we need to get a baud of 500000, we'll just multiply that by 16 and hope that the clk can give us 500000 * 16 == 8000000 Hz, which it typically can't do. To really achieve 500000 baud, we need to get an input clk rate of 24000000 Hz and then divide that by 3 inside the uart hardware. Finally, we return success for bauds even when we can't actually achieve them. This means that when the user asks for 500000 baud, we actually get 921600 right now, but the user doesn't know that. Fix all of this by searching through the divisor and clk rate space with a combination of clk_round_rate() and baud calculations, keeping track of the best clk rate and divisor we find if we can't get an exact match. Typically we can get an exact match with a divisor of 1, but sometimes we need to keep track and try more frequencies. On my msm8916 device, this results in all standard bauds in baud_table being supported except for 1800, 576000, 1152000, and 4000000. Fixes: 850b37a71bde ("tty: serial: msm: Remove 115.2 Kbps maximum baud rate limitation") Cc: "Ivan T. Ivanov" <iivanov.xz@gmail.com> Cc: Matthew McClintock <mmcclint@codeaurora.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Andy Gross <andy.gross@linaro.org> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Tested-by: Cristian Prundeanu <cprundea@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-03-25 21:35:49 +00:00
entry = msm_find_best_baud(port, baud, &rate);
dev_pm_opp_set_rate(port->dev, rate);
tty: serial: msm: Support more bauds The msm_find_best_baud() function is written with the assumption that the port->uartclk rate is fixed to a particular rate at boot time, but now this driver changes that clk rate at runtime when the baud is changed. The way the hardware works is that an input clk rate comes from the clk controller into the uart hw block. That rate is typically 1843200 or 3686400 Hz. That rate can then be divided by an internal divider in the hw block to achieve a particular baud on the serial wire. msm_find_best_baud() is looking for that divider value. A few things are wrong with the way the code is written. First, it assumes that the maximum baud that the uart can support if the clk rate is fixed at boot is 460800, which would correspond to an input clk rate of 230400 * 16 == 3686400 Hz. Except some devices have a boot rate of 1843200 Hz or max baud of 115200, so achieving 230400 on those devices doesn't work at all because we don't increase the clk rate unless max baud is 460800. Second, we can't achieve bauds higher than 460800 that require anything besides a divisor of 1, because we always call msm_find_best_baud() with a fixed port->uartclk rate that will eventually be changed after we calculate the divisor. So if we need to get a baud of 500000, we'll just multiply that by 16 and hope that the clk can give us 500000 * 16 == 8000000 Hz, which it typically can't do. To really achieve 500000 baud, we need to get an input clk rate of 24000000 Hz and then divide that by 3 inside the uart hardware. Finally, we return success for bauds even when we can't actually achieve them. This means that when the user asks for 500000 baud, we actually get 921600 right now, but the user doesn't know that. Fix all of this by searching through the divisor and clk rate space with a combination of clk_round_rate() and baud calculations, keeping track of the best clk rate and divisor we find if we can't get an exact match. Typically we can get an exact match with a divisor of 1, but sometimes we need to keep track and try more frequencies. On my msm8916 device, this results in all standard bauds in baud_table being supported except for 1800, 576000, 1152000, and 4000000. Fixes: 850b37a71bde ("tty: serial: msm: Remove 115.2 Kbps maximum baud rate limitation") Cc: "Ivan T. Ivanov" <iivanov.xz@gmail.com> Cc: Matthew McClintock <mmcclint@codeaurora.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Andy Gross <andy.gross@linaro.org> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Tested-by: Cristian Prundeanu <cprundea@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-03-25 21:35:49 +00:00
baud = rate / 16 / entry->divisor;
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_lock_irqsave(port, &flags);
*saved_flags = flags;
tty: serial: msm: Support more bauds The msm_find_best_baud() function is written with the assumption that the port->uartclk rate is fixed to a particular rate at boot time, but now this driver changes that clk rate at runtime when the baud is changed. The way the hardware works is that an input clk rate comes from the clk controller into the uart hw block. That rate is typically 1843200 or 3686400 Hz. That rate can then be divided by an internal divider in the hw block to achieve a particular baud on the serial wire. msm_find_best_baud() is looking for that divider value. A few things are wrong with the way the code is written. First, it assumes that the maximum baud that the uart can support if the clk rate is fixed at boot is 460800, which would correspond to an input clk rate of 230400 * 16 == 3686400 Hz. Except some devices have a boot rate of 1843200 Hz or max baud of 115200, so achieving 230400 on those devices doesn't work at all because we don't increase the clk rate unless max baud is 460800. Second, we can't achieve bauds higher than 460800 that require anything besides a divisor of 1, because we always call msm_find_best_baud() with a fixed port->uartclk rate that will eventually be changed after we calculate the divisor. So if we need to get a baud of 500000, we'll just multiply that by 16 and hope that the clk can give us 500000 * 16 == 8000000 Hz, which it typically can't do. To really achieve 500000 baud, we need to get an input clk rate of 24000000 Hz and then divide that by 3 inside the uart hardware. Finally, we return success for bauds even when we can't actually achieve them. This means that when the user asks for 500000 baud, we actually get 921600 right now, but the user doesn't know that. Fix all of this by searching through the divisor and clk rate space with a combination of clk_round_rate() and baud calculations, keeping track of the best clk rate and divisor we find if we can't get an exact match. Typically we can get an exact match with a divisor of 1, but sometimes we need to keep track and try more frequencies. On my msm8916 device, this results in all standard bauds in baud_table being supported except for 1800, 576000, 1152000, and 4000000. Fixes: 850b37a71bde ("tty: serial: msm: Remove 115.2 Kbps maximum baud rate limitation") Cc: "Ivan T. Ivanov" <iivanov.xz@gmail.com> Cc: Matthew McClintock <mmcclint@codeaurora.org> Signed-off-by: Stephen Boyd <stephen.boyd@linaro.org> Tested-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Andy Gross <andy.gross@linaro.org> Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org> Tested-by: Cristian Prundeanu <cprundea@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-03-25 21:35:49 +00:00
port->uartclk = rate;
msm_write(port, entry->code, MSM_UART_CSR);
/* RX stale watermark */
rxstale = entry->rxstale;
watermark = MSM_UART_IPR_STALE_LSB & rxstale;
if (msm_port->is_uartdm) {
mask = MSM_UART_DM_IPR_STALE_TIMEOUT_MSB;
} else {
watermark |= MSM_UART_IPR_RXSTALE_LAST;
mask = MSM_UART_IPR_STALE_TIMEOUT_MSB;
}
watermark |= mask & (rxstale << 2);
msm_write(port, watermark, MSM_UART_IPR);
/* set RX watermark */
watermark = (port->fifosize * 3) / 4;
msm_write(port, watermark, MSM_UART_RFWR);
/* set TX watermark */
msm_write(port, 10, MSM_UART_TFWR);
msm_write(port, MSM_UART_CR_CMD_PROTECTION_EN, MSM_UART_CR);
msm_reset(port);
/* Enable RX and TX */
msm_write(port, MSM_UART_CR_TX_ENABLE | MSM_UART_CR_RX_ENABLE, MSM_UART_CR);
/* turn on RX and CTS interrupts */
msm_port->imr = MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE |
MSM_UART_IMR_CURRENT_CTS | MSM_UART_IMR_RXBREAK_START;
msm_write(port, msm_port->imr, MSM_UART_IMR);
if (msm_port->is_uartdm) {
msm_write(port, MSM_UART_CR_CMD_RESET_STALE_INT, MSM_UART_CR);
msm_write(port, 0xFFFFFF, UARTDM_DMRX);
msm_write(port, MSM_UART_CR_CMD_STALE_EVENT_ENABLE, MSM_UART_CR);
}
return baud;
}
static void msm_init_clock(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
dev_pm_opp_set_rate(port->dev, port->uartclk);
clk_prepare_enable(msm_port->clk);
clk_prepare_enable(msm_port->pclk);
msm_serial_set_mnd_regs(port);
}
static int msm_startup(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
unsigned int data, rfr_level, mask;
int ret;
snprintf(msm_port->name, sizeof(msm_port->name),
"msm_serial%d", port->line);
msm_init_clock(port);
if (likely(port->fifosize > 12))
rfr_level = port->fifosize - 12;
else
rfr_level = port->fifosize;
/* set automatic RFR level */
data = msm_read(port, MSM_UART_MR1);
if (msm_port->is_uartdm)
mask = MSM_UART_DM_MR1_AUTO_RFR_LEVEL1;
else
mask = MSM_UART_MR1_AUTO_RFR_LEVEL1;
data &= ~mask;
data &= ~MSM_UART_MR1_AUTO_RFR_LEVEL0;
data |= mask & (rfr_level << 2);
data |= MSM_UART_MR1_AUTO_RFR_LEVEL0 & rfr_level;
msm_write(port, data, MSM_UART_MR1);
if (msm_port->is_uartdm) {
msm_request_tx_dma(msm_port, msm_port->uart.mapbase);
msm_request_rx_dma(msm_port, msm_port->uart.mapbase);
}
ret = request_irq(port->irq, msm_uart_irq, IRQF_TRIGGER_HIGH,
msm_port->name, port);
if (unlikely(ret))
goto err_irq;
return 0;
err_irq:
if (msm_port->is_uartdm)
msm_release_dma(msm_port);
clk_disable_unprepare(msm_port->pclk);
clk_disable_unprepare(msm_port->clk);
dev_pm_opp_set_rate(port->dev, 0);
return ret;
}
static void msm_shutdown(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
msm_port->imr = 0;
msm_write(port, 0, MSM_UART_IMR); /* disable interrupts */
if (msm_port->is_uartdm)
msm_release_dma(msm_port);
clk_disable_unprepare(msm_port->clk);
dev_pm_opp_set_rate(port->dev, 0);
free_irq(port->irq, port);
}
static void msm_set_termios(struct uart_port *port, struct ktermios *termios,
const struct ktermios *old)
{
struct msm_port *msm_port = to_msm_port(port);
struct msm_dma *dma = &msm_port->rx_dma;
unsigned long flags;
unsigned int baud, mr;
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_lock_irqsave(port, &flags);
if (dma->chan) /* Terminate if any */
msm_stop_dma(port, dma);
/* calculate and set baud rate */
baud = uart_get_baud_rate(port, termios, old, 300, 4000000);
baud = msm_set_baud_rate(port, baud, &flags);
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
/* calculate parity */
mr = msm_read(port, MSM_UART_MR2);
mr &= ~MSM_UART_MR2_PARITY_MODE;
if (termios->c_cflag & PARENB) {
if (termios->c_cflag & PARODD)
mr |= MSM_UART_MR2_PARITY_MODE_ODD;
else if (termios->c_cflag & CMSPAR)
mr |= MSM_UART_MR2_PARITY_MODE_SPACE;
else
mr |= MSM_UART_MR2_PARITY_MODE_EVEN;
}
/* calculate bits per char */
mr &= ~MSM_UART_MR2_BITS_PER_CHAR;
switch (termios->c_cflag & CSIZE) {
case CS5:
mr |= MSM_UART_MR2_BITS_PER_CHAR_5;
break;
case CS6:
mr |= MSM_UART_MR2_BITS_PER_CHAR_6;
break;
case CS7:
mr |= MSM_UART_MR2_BITS_PER_CHAR_7;
break;
case CS8:
default:
mr |= MSM_UART_MR2_BITS_PER_CHAR_8;
break;
}
/* calculate stop bits */
mr &= ~(MSM_UART_MR2_STOP_BIT_LEN_ONE | MSM_UART_MR2_STOP_BIT_LEN_TWO);
if (termios->c_cflag & CSTOPB)
mr |= MSM_UART_MR2_STOP_BIT_LEN_TWO;
else
mr |= MSM_UART_MR2_STOP_BIT_LEN_ONE;
/* set parity, bits per char, and stop bit */
msm_write(port, mr, MSM_UART_MR2);
/* calculate and set hardware flow control */
mr = msm_read(port, MSM_UART_MR1);
mr &= ~(MSM_UART_MR1_CTS_CTL | MSM_UART_MR1_RX_RDY_CTL);
if (termios->c_cflag & CRTSCTS) {
mr |= MSM_UART_MR1_CTS_CTL;
mr |= MSM_UART_MR1_RX_RDY_CTL;
}
msm_write(port, mr, MSM_UART_MR1);
/* Configure status bits to ignore based on termio flags. */
port->read_status_mask = 0;
if (termios->c_iflag & INPCK)
port->read_status_mask |= MSM_UART_SR_PAR_FRAME_ERR;
serial: Fix IGNBRK handling If IGNBRK is set without either BRKINT or PARMRK set, some uart drivers send a 0x00 byte for BREAK without the TTYBREAK flag to the line discipline, when it should send either nothing or the TTYBREAK flag set. This happens because the read_status_mask masks out the BI condition, which uart_insert_char() then interprets as a normal 0x00 byte. SUS v3 is clear regarding the meaning of IGNBRK; Section 11.2.2, General Terminal Interface - Input Modes, states: "If IGNBRK is set, a break condition detected on input shall be ignored; that is, not put on the input queue and therefore not read by any process." Fix read_status_mask to include the BI bit if IGNBRK is set; the lsr status retains the BI bit if a BREAK is recv'd, which is subsequently ignored in uart_insert_char() when masked with the ignore_status_mask. Affected drivers: 8250 - all serial_txx9 mfd amba-pl010 amba-pl011 atmel_serial bfin_uart dz ip22zilog max310x mxs-auart netx-serial pnx8xxx_uart pxa sb1250-duart sccnxp serial_ks8695 sirfsoc_uart st-asc vr41xx_siu zs sunzilog fsl_lpuart sunsab ucc_uart bcm63xx_uart sunsu efm32-uart pmac_zilog mpsc msm_serial m32r_sio Unaffected drivers: omap-serial rp2 sa1100 imx icom Annotated for fixes: altera_uart mcf Drivers without break detection: 21285 xilinx-uartps altera_jtaguart apbuart arc-uart clps711x max3100 uartlite msm_serial_hs nwpserial lantiq vt8500_serial Unknown: samsung mpc52xx_uart bfin_sport_uart cpm_uart/core Fixes: Bugzilla #71651, '8250_core.c incorrectly handles IGNBRK flag' Reported-by: Ivan <athlon_@mail.ru> Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-06-16 12:10:41 +00:00
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= MSM_UART_SR_RX_BREAK;
uart_update_timeout(port, termios->c_cflag, baud);
/* Try to use DMA */
msm_start_rx_dma(msm_port);
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_unlock_irqrestore(port, flags);
}
static const char *msm_type(struct uart_port *port)
{
return "MSM";
}
static void msm_release_port(struct uart_port *port)
{
struct platform_device *pdev = to_platform_device(port->dev);
struct resource *uart_resource;
resource_size_t size;
uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!uart_resource))
return;
size = resource_size(uart_resource);
release_mem_region(port->mapbase, size);
iounmap(port->membase);
port->membase = NULL;
}
static int msm_request_port(struct uart_port *port)
{
struct platform_device *pdev = to_platform_device(port->dev);
struct resource *uart_resource;
resource_size_t size;
int ret;
uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!uart_resource))
return -ENXIO;
size = resource_size(uart_resource);
if (!request_mem_region(port->mapbase, size, "msm_serial"))
return -EBUSY;
port->membase = ioremap(port->mapbase, size);
if (!port->membase) {
ret = -EBUSY;
goto fail_release_port;
}
return 0;
fail_release_port:
release_mem_region(port->mapbase, size);
return ret;
}
static void msm_config_port(struct uart_port *port, int flags)
{
int ret;
if (flags & UART_CONFIG_TYPE) {
port->type = PORT_MSM;
ret = msm_request_port(port);
if (ret)
return;
}
}
static int msm_verify_port(struct uart_port *port, struct serial_struct *ser)
{
if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_MSM))
return -EINVAL;
if (unlikely(port->irq != ser->irq))
return -EINVAL;
return 0;
}
static void msm_power(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
struct msm_port *msm_port = to_msm_port(port);
switch (state) {
case 0:
dev_pm_opp_set_rate(port->dev, port->uartclk);
clk_prepare_enable(msm_port->clk);
clk_prepare_enable(msm_port->pclk);
break;
case 3:
clk_disable_unprepare(msm_port->clk);
dev_pm_opp_set_rate(port->dev, 0);
clk_disable_unprepare(msm_port->pclk);
break;
default:
pr_err("msm_serial: Unknown PM state %d\n", state);
}
}
#ifdef CONFIG_CONSOLE_POLL
static int msm_poll_get_char_single(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
unsigned int rf_reg = msm_port->is_uartdm ? UARTDM_RF : MSM_UART_RF;
if (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_RX_READY))
return NO_POLL_CHAR;
return msm_read(port, rf_reg) & 0xff;
}
serial: msm_serial: Fix kgdb continue Frank reports that after continuing in kgdb the RX stale event doesn't occur until after the RX fifo is filled up with exactly the amount of characters programmed for the RX watermark (in this case it's 48). To read a single character from the uartdm hardware we force a stale event so that any characters in the RX packing buffer are flushed into the RX fifo immediately instead of waiting for a stale timeout or for the fifo to fill. Forcing that stale event asserts the stale interrupt but we never clear that interrupt via UART_CR_CMD_RESET_STALE_INT in the polling functions. So when kgdb continues the stale interrupt is left pending in the hardware and we don't timeout with a stale event, like we usually would if a user typed one character on the console, until the reset stale interrupt and stale event commands are sent. Frank could get things working again by running handle_rx_dm(). By putting enough characters into the fifo he could trigger a watermark interrupt, and thus cause handle_rx_dm() to run finally resetting the stale interrupt and enabling the stale event so that single characters would cause timeouts again. The fix is to just do what the interrupt routine was doing all along and clear the stale interrupt and enable the event again. Doing this also smooths over any differences in the fifo behavior between v1.3 and v1.4 hardware allowing us to skip forcing the uart into single character mode. Reviewed-by: Frank Rowand <frank.rowand@sonymobile.com> Tested-by: Frank Rowand <frank.rowand@sonymobile.com> Fixes: f7e54d7ad743 "msm_serial: Add support for poll_{get,put}_char()" Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-08-06 01:37:24 +00:00
static int msm_poll_get_char_dm(struct uart_port *port)
{
int c;
static u32 slop;
static int count;
unsigned char *sp = (unsigned char *)&slop;
/* Check if a previous read had more than one char */
if (count) {
c = sp[sizeof(slop) - count];
count--;
/* Or if FIFO is empty */
} else if (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_RX_READY)) {
/*
* If RX packing buffer has less than a word, force stale to
* push contents into RX FIFO
*/
count = msm_read(port, UARTDM_RXFS);
count = (count >> UARTDM_RXFS_BUF_SHIFT) & UARTDM_RXFS_BUF_MASK;
if (count) {
msm_write(port, MSM_UART_CR_CMD_FORCE_STALE, MSM_UART_CR);
slop = msm_read(port, UARTDM_RF);
c = sp[0];
count--;
msm_write(port, MSM_UART_CR_CMD_RESET_STALE_INT, MSM_UART_CR);
serial: msm_serial: Fix kgdb continue Frank reports that after continuing in kgdb the RX stale event doesn't occur until after the RX fifo is filled up with exactly the amount of characters programmed for the RX watermark (in this case it's 48). To read a single character from the uartdm hardware we force a stale event so that any characters in the RX packing buffer are flushed into the RX fifo immediately instead of waiting for a stale timeout or for the fifo to fill. Forcing that stale event asserts the stale interrupt but we never clear that interrupt via UART_CR_CMD_RESET_STALE_INT in the polling functions. So when kgdb continues the stale interrupt is left pending in the hardware and we don't timeout with a stale event, like we usually would if a user typed one character on the console, until the reset stale interrupt and stale event commands are sent. Frank could get things working again by running handle_rx_dm(). By putting enough characters into the fifo he could trigger a watermark interrupt, and thus cause handle_rx_dm() to run finally resetting the stale interrupt and enabling the stale event so that single characters would cause timeouts again. The fix is to just do what the interrupt routine was doing all along and clear the stale interrupt and enable the event again. Doing this also smooths over any differences in the fifo behavior between v1.3 and v1.4 hardware allowing us to skip forcing the uart into single character mode. Reviewed-by: Frank Rowand <frank.rowand@sonymobile.com> Tested-by: Frank Rowand <frank.rowand@sonymobile.com> Fixes: f7e54d7ad743 "msm_serial: Add support for poll_{get,put}_char()" Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-08-06 01:37:24 +00:00
msm_write(port, 0xFFFFFF, UARTDM_DMRX);
msm_write(port, MSM_UART_CR_CMD_STALE_EVENT_ENABLE, MSM_UART_CR);
} else {
c = NO_POLL_CHAR;
}
/* FIFO has a word */
} else {
slop = msm_read(port, UARTDM_RF);
c = sp[0];
count = sizeof(slop) - 1;
}
return c;
}
static int msm_poll_get_char(struct uart_port *port)
{
u32 imr;
int c;
struct msm_port *msm_port = to_msm_port(port);
/* Disable all interrupts */
imr = msm_read(port, MSM_UART_IMR);
msm_write(port, 0, MSM_UART_IMR);
serial: msm_serial: Fix kgdb continue Frank reports that after continuing in kgdb the RX stale event doesn't occur until after the RX fifo is filled up with exactly the amount of characters programmed for the RX watermark (in this case it's 48). To read a single character from the uartdm hardware we force a stale event so that any characters in the RX packing buffer are flushed into the RX fifo immediately instead of waiting for a stale timeout or for the fifo to fill. Forcing that stale event asserts the stale interrupt but we never clear that interrupt via UART_CR_CMD_RESET_STALE_INT in the polling functions. So when kgdb continues the stale interrupt is left pending in the hardware and we don't timeout with a stale event, like we usually would if a user typed one character on the console, until the reset stale interrupt and stale event commands are sent. Frank could get things working again by running handle_rx_dm(). By putting enough characters into the fifo he could trigger a watermark interrupt, and thus cause handle_rx_dm() to run finally resetting the stale interrupt and enabling the stale event so that single characters would cause timeouts again. The fix is to just do what the interrupt routine was doing all along and clear the stale interrupt and enable the event again. Doing this also smooths over any differences in the fifo behavior between v1.3 and v1.4 hardware allowing us to skip forcing the uart into single character mode. Reviewed-by: Frank Rowand <frank.rowand@sonymobile.com> Tested-by: Frank Rowand <frank.rowand@sonymobile.com> Fixes: f7e54d7ad743 "msm_serial: Add support for poll_{get,put}_char()" Signed-off-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-08-06 01:37:24 +00:00
if (msm_port->is_uartdm)
c = msm_poll_get_char_dm(port);
else
c = msm_poll_get_char_single(port);
/* Enable interrupts */
msm_write(port, imr, MSM_UART_IMR);
return c;
}
static void msm_poll_put_char(struct uart_port *port, unsigned char c)
{
u32 imr;
struct msm_port *msm_port = to_msm_port(port);
/* Disable all interrupts */
imr = msm_read(port, MSM_UART_IMR);
msm_write(port, 0, MSM_UART_IMR);
if (msm_port->is_uartdm)
msm_reset_dm_count(port, 1);
/* Wait until FIFO is empty */
while (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_READY))
cpu_relax();
/* Write a character */
msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : MSM_UART_TF);
/* Wait until FIFO is empty */
while (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_READY))
cpu_relax();
/* Enable interrupts */
msm_write(port, imr, MSM_UART_IMR);
}
#endif
static const struct uart_ops msm_uart_pops = {
.tx_empty = msm_tx_empty,
.set_mctrl = msm_set_mctrl,
.get_mctrl = msm_get_mctrl,
.stop_tx = msm_stop_tx,
.start_tx = msm_start_tx,
.stop_rx = msm_stop_rx,
.enable_ms = msm_enable_ms,
.break_ctl = msm_break_ctl,
.startup = msm_startup,
.shutdown = msm_shutdown,
.set_termios = msm_set_termios,
.type = msm_type,
.release_port = msm_release_port,
.request_port = msm_request_port,
.config_port = msm_config_port,
.verify_port = msm_verify_port,
.pm = msm_power,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = msm_poll_get_char,
.poll_put_char = msm_poll_put_char,
#endif
};
static struct msm_port msm_uart_ports[] = {
{
.uart = {
.iotype = UPIO_MEM,
.ops = &msm_uart_pops,
.flags = UPF_BOOT_AUTOCONF,
.fifosize = 64,
.line = 0,
},
},
{
.uart = {
.iotype = UPIO_MEM,
.ops = &msm_uart_pops,
.flags = UPF_BOOT_AUTOCONF,
.fifosize = 64,
.line = 1,
},
},
{
.uart = {
.iotype = UPIO_MEM,
.ops = &msm_uart_pops,
.flags = UPF_BOOT_AUTOCONF,
.fifosize = 64,
.line = 2,
},
},
};
#define MSM_UART_NR ARRAY_SIZE(msm_uart_ports)
static inline struct uart_port *msm_get_port_from_line(unsigned int line)
{
return &msm_uart_ports[line].uart;
}
#ifdef CONFIG_SERIAL_MSM_CONSOLE
static void __msm_console_write(struct uart_port *port, const char *s,
unsigned int count, bool is_uartdm)
{
unsigned long flags;
int i;
int num_newlines = 0;
bool replaced = false;
void __iomem *tf;
int locked = 1;
if (is_uartdm)
tf = port->membase + UARTDM_TF;
else
tf = port->membase + MSM_UART_TF;
/* Account for newlines that will get a carriage return added */
for (i = 0; i < count; i++)
if (s[i] == '\n')
num_newlines++;
count += num_newlines;
local_irq_save(flags);
if (port->sysrq)
locked = 0;
else if (oops_in_progress)
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
locked = uart_port_trylock(port);
else
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_lock(port);
if (is_uartdm)
msm_reset_dm_count(port, count);
i = 0;
while (i < count) {
int j;
unsigned int num_chars;
char buf[4] = { 0 };
if (is_uartdm)
num_chars = min(count - i, (unsigned int)sizeof(buf));
else
num_chars = 1;
for (j = 0; j < num_chars; j++) {
char c = *s;
if (c == '\n' && !replaced) {
buf[j] = '\r';
j++;
replaced = true;
}
if (j < num_chars) {
buf[j] = c;
s++;
replaced = false;
}
}
while (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_READY))
cpu_relax();
iowrite32_rep(tf, buf, 1);
i += num_chars;
}
if (locked)
serial: msm: 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> Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com> Signed-off-by: John Ogness <john.ogness@linutronix.de> Link: https://lore.kernel.org/r/20230914183831.587273-42-john.ogness@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-09-14 18:37:58 +00:00
uart_port_unlock(port);
local_irq_restore(flags);
}
static void msm_console_write(struct console *co, const char *s,
unsigned int count)
{
struct uart_port *port;
struct msm_port *msm_port;
BUG_ON(co->index < 0 || co->index >= MSM_UART_NR);
port = msm_get_port_from_line(co->index);
msm_port = to_msm_port(port);
__msm_console_write(port, s, count, msm_port->is_uartdm);
}
static int msm_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = 115200;
int bits = 8;
int parity = 'n';
int flow = 'n';
if (unlikely(co->index >= MSM_UART_NR || co->index < 0))
return -ENXIO;
port = msm_get_port_from_line(co->index);
if (unlikely(!port->membase))
return -ENXIO;
msm_init_clock(port);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
pr_info("msm_serial: console setup on port #%d\n", port->line);
return uart_set_options(port, co, baud, parity, bits, flow);
}
static void
msm_serial_early_write(struct console *con, const char *s, unsigned n)
{
struct earlycon_device *dev = con->data;
__msm_console_write(&dev->port, s, n, false);
}
static int __init
msm_serial_early_console_setup(struct earlycon_device *device, const char *opt)
{
if (!device->port.membase)
return -ENODEV;
device->con->write = msm_serial_early_write;
return 0;
}
OF_EARLYCON_DECLARE(msm_serial, "qcom,msm-uart",
msm_serial_early_console_setup);
static void
msm_serial_early_write_dm(struct console *con, const char *s, unsigned n)
{
struct earlycon_device *dev = con->data;
__msm_console_write(&dev->port, s, n, true);
}
static int __init
msm_serial_early_console_setup_dm(struct earlycon_device *device,
const char *opt)
{
if (!device->port.membase)
return -ENODEV;
device->con->write = msm_serial_early_write_dm;
return 0;
}
OF_EARLYCON_DECLARE(msm_serial_dm, "qcom,msm-uartdm",
msm_serial_early_console_setup_dm);
static struct uart_driver msm_uart_driver;
static struct console msm_console = {
.name = "ttyMSM",
.write = msm_console_write,
.device = uart_console_device,
.setup = msm_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &msm_uart_driver,
};
#define MSM_CONSOLE (&msm_console)
#else
#define MSM_CONSOLE NULL
#endif
static struct uart_driver msm_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "msm_serial",
.dev_name = "ttyMSM",
.nr = MSM_UART_NR,
.cons = MSM_CONSOLE,
};
static atomic_t msm_uart_next_id = ATOMIC_INIT(0);
static const struct of_device_id msm_uartdm_table[] = {
{ .compatible = "qcom,msm-uartdm-v1.1", .data = (void *)UARTDM_1P1 },
{ .compatible = "qcom,msm-uartdm-v1.2", .data = (void *)UARTDM_1P2 },
{ .compatible = "qcom,msm-uartdm-v1.3", .data = (void *)UARTDM_1P3 },
{ .compatible = "qcom,msm-uartdm-v1.4", .data = (void *)UARTDM_1P4 },
{ }
};
static int msm_serial_probe(struct platform_device *pdev)
{
struct msm_port *msm_port;
struct resource *resource;
struct uart_port *port;
const struct of_device_id *id;
int irq, line, ret;
if (pdev->dev.of_node)
line = of_alias_get_id(pdev->dev.of_node, "serial");
else
line = pdev->id;
if (line < 0)
line = atomic_inc_return(&msm_uart_next_id) - 1;
if (unlikely(line < 0 || line >= MSM_UART_NR))
return -ENXIO;
dev_info(&pdev->dev, "msm_serial: detected port #%d\n", line);
port = msm_get_port_from_line(line);
port->dev = &pdev->dev;
msm_port = to_msm_port(port);
id = of_match_device(msm_uartdm_table, &pdev->dev);
if (id)
msm_port->is_uartdm = (unsigned long)id->data;
else
msm_port->is_uartdm = 0;
msm_port->clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(msm_port->clk))
return PTR_ERR(msm_port->clk);
if (msm_port->is_uartdm) {
msm_port->pclk = devm_clk_get(&pdev->dev, "iface");
if (IS_ERR(msm_port->pclk))
return PTR_ERR(msm_port->pclk);
}
ret = devm_pm_opp_set_clkname(&pdev->dev, "core");
if (ret)
return ret;
/* OPP table is optional */
ret = devm_pm_opp_of_add_table(&pdev->dev);
if (ret && ret != -ENODEV)
return dev_err_probe(&pdev->dev, ret, "invalid OPP table\n");
port->uartclk = clk_get_rate(msm_port->clk);
dev_info(&pdev->dev, "uartclk = %d\n", port->uartclk);
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!resource))
return -ENXIO;
port->mapbase = resource->start;
irq = platform_get_irq(pdev, 0);
if (unlikely(irq < 0))
return -ENXIO;
port->irq = irq;
port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_MSM_CONSOLE);
platform_set_drvdata(pdev, port);
return uart_add_one_port(&msm_uart_driver, port);
}
static void msm_serial_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
uart_remove_one_port(&msm_uart_driver, port);
}
static const struct of_device_id msm_match_table[] = {
{ .compatible = "qcom,msm-uart" },
{ .compatible = "qcom,msm-uartdm" },
{}
};
MODULE_DEVICE_TABLE(of, msm_match_table);
static int __maybe_unused msm_serial_suspend(struct device *dev)
{
struct msm_port *port = dev_get_drvdata(dev);
uart_suspend_port(&msm_uart_driver, &port->uart);
return 0;
}
static int __maybe_unused msm_serial_resume(struct device *dev)
{
struct msm_port *port = dev_get_drvdata(dev);
uart_resume_port(&msm_uart_driver, &port->uart);
return 0;
}
static const struct dev_pm_ops msm_serial_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(msm_serial_suspend, msm_serial_resume)
};
static struct platform_driver msm_platform_driver = {
.remove_new = msm_serial_remove,
.probe = msm_serial_probe,
.driver = {
.name = "msm_serial",
.pm = &msm_serial_dev_pm_ops,
.of_match_table = msm_match_table,
},
};
static int __init msm_serial_init(void)
{
int ret;
ret = uart_register_driver(&msm_uart_driver);
if (unlikely(ret))
return ret;
ret = platform_driver_register(&msm_platform_driver);
if (unlikely(ret))
uart_unregister_driver(&msm_uart_driver);
pr_info("msm_serial: driver initialized\n");
return ret;
}
static void __exit msm_serial_exit(void)
{
platform_driver_unregister(&msm_platform_driver);
uart_unregister_driver(&msm_uart_driver);
}
module_init(msm_serial_init);
module_exit(msm_serial_exit);
MODULE_AUTHOR("Robert Love <rlove@google.com>");
MODULE_DESCRIPTION("Driver for msm7x serial device");
MODULE_LICENSE("GPL");