linux/drivers/tty/serial/sc16is7xx.c

1835 lines
52 KiB
C
Raw Permalink 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 18:11:51 +01:00
// SPDX-License-Identifier: GPL-2.0+
/*
* SC16IS7xx tty serial driver - common code
*
* Copyright (C) 2014 GridPoint
* Author: Jon Ringle <jringle@gridpoint.com>
* Based on max310x.c, by Alexander Shiyan <shc_work@mail.ru>
*/
#undef DEFAULT_SYMBOL_NAMESPACE
#define DEFAULT_SYMBOL_NAMESPACE "SERIAL_NXP_SC16IS7XX"
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/idr.h>
#include <linux/kthread.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/sched.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/string.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/uaccess.h>
#include <linux/units.h>
#include "sc16is7xx.h"
#define SC16IS7XX_MAX_DEVS 8
/* SC16IS7XX register definitions */
#define SC16IS7XX_RHR_REG (0x00) /* RX FIFO */
#define SC16IS7XX_THR_REG (0x00) /* TX FIFO */
#define SC16IS7XX_IER_REG (0x01) /* Interrupt enable */
#define SC16IS7XX_IIR_REG (0x02) /* Interrupt Identification */
#define SC16IS7XX_FCR_REG (0x02) /* FIFO control */
#define SC16IS7XX_LCR_REG (0x03) /* Line Control */
#define SC16IS7XX_MCR_REG (0x04) /* Modem Control */
#define SC16IS7XX_LSR_REG (0x05) /* Line Status */
#define SC16IS7XX_MSR_REG (0x06) /* Modem Status */
#define SC16IS7XX_SPR_REG (0x07) /* Scratch Pad */
#define SC16IS7XX_TXLVL_REG (0x08) /* TX FIFO level */
#define SC16IS7XX_RXLVL_REG (0x09) /* RX FIFO level */
#define SC16IS7XX_IODIR_REG (0x0a) /* I/O Direction
* - only on 75x/76x
*/
#define SC16IS7XX_IOSTATE_REG (0x0b) /* I/O State
* - only on 75x/76x
*/
#define SC16IS7XX_IOINTENA_REG (0x0c) /* I/O Interrupt Enable
* - only on 75x/76x
*/
#define SC16IS7XX_IOCONTROL_REG (0x0e) /* I/O Control
* - only on 75x/76x
*/
#define SC16IS7XX_EFCR_REG (0x0f) /* Extra Features Control */
/* TCR/TLR Register set: Only if ((MCR[2] == 1) && (EFR[4] == 1)) */
#define SC16IS7XX_TCR_REG (0x06) /* Transmit control */
#define SC16IS7XX_TLR_REG (0x07) /* Trigger level */
/* Special Register set: Only if ((LCR[7] == 1) && (LCR != 0xBF)) */
#define SC16IS7XX_DLL_REG (0x00) /* Divisor Latch Low */
#define SC16IS7XX_DLH_REG (0x01) /* Divisor Latch High */
/* Enhanced Register set: Only if (LCR == 0xBF) */
#define SC16IS7XX_EFR_REG (0x02) /* Enhanced Features */
#define SC16IS7XX_XON1_REG (0x04) /* Xon1 word */
#define SC16IS7XX_XON2_REG (0x05) /* Xon2 word */
#define SC16IS7XX_XOFF1_REG (0x06) /* Xoff1 word */
#define SC16IS7XX_XOFF2_REG (0x07) /* Xoff2 word */
/* IER register bits */
#define SC16IS7XX_IER_RDI_BIT BIT(0) /* Enable RX data interrupt */
#define SC16IS7XX_IER_THRI_BIT BIT(1) /* Enable TX holding register
* interrupt */
#define SC16IS7XX_IER_RLSI_BIT BIT(2) /* Enable RX line status
* interrupt */
#define SC16IS7XX_IER_MSI_BIT BIT(3) /* Enable Modem status
* interrupt */
/* IER register bits - write only if (EFR[4] == 1) */
#define SC16IS7XX_IER_SLEEP_BIT BIT(4) /* Enable Sleep mode */
#define SC16IS7XX_IER_XOFFI_BIT BIT(5) /* Enable Xoff interrupt */
#define SC16IS7XX_IER_RTSI_BIT BIT(6) /* Enable nRTS interrupt */
#define SC16IS7XX_IER_CTSI_BIT BIT(7) /* Enable nCTS interrupt */
/* FCR register bits */
#define SC16IS7XX_FCR_FIFO_BIT BIT(0) /* Enable FIFO */
#define SC16IS7XX_FCR_RXRESET_BIT BIT(1) /* Reset RX FIFO */
#define SC16IS7XX_FCR_TXRESET_BIT BIT(2) /* Reset TX FIFO */
#define SC16IS7XX_FCR_RXLVLL_BIT BIT(6) /* RX Trigger level LSB */
#define SC16IS7XX_FCR_RXLVLH_BIT BIT(7) /* RX Trigger level MSB */
/* FCR register bits - write only if (EFR[4] == 1) */
#define SC16IS7XX_FCR_TXLVLL_BIT BIT(4) /* TX Trigger level LSB */
#define SC16IS7XX_FCR_TXLVLH_BIT BIT(5) /* TX Trigger level MSB */
/* IIR register bits */
#define SC16IS7XX_IIR_NO_INT_BIT 0x01 /* No interrupts pending */
#define SC16IS7XX_IIR_ID_MASK GENMASK(5, 1) /* Mask for the interrupt ID */
#define SC16IS7XX_IIR_THRI_SRC 0x02 /* TX holding register empty */
#define SC16IS7XX_IIR_RDI_SRC 0x04 /* RX data interrupt */
#define SC16IS7XX_IIR_RLSE_SRC 0x06 /* RX line status error */
#define SC16IS7XX_IIR_RTOI_SRC 0x0c /* RX time-out interrupt */
#define SC16IS7XX_IIR_MSI_SRC 0x00 /* Modem status interrupt
* - only on 75x/76x
*/
#define SC16IS7XX_IIR_INPIN_SRC 0x30 /* Input pin change of state
* - only on 75x/76x
*/
#define SC16IS7XX_IIR_XOFFI_SRC 0x10 /* Received Xoff */
#define SC16IS7XX_IIR_CTSRTS_SRC 0x20 /* nCTS,nRTS change of state
* from active (LOW)
* to inactive (HIGH)
*/
/* LCR register bits */
#define SC16IS7XX_LCR_LENGTH0_BIT BIT(0) /* Word length bit 0 */
#define SC16IS7XX_LCR_LENGTH1_BIT BIT(1) /* Word length bit 1
*
* Word length bits table:
* 00 -> 5 bit words
* 01 -> 6 bit words
* 10 -> 7 bit words
* 11 -> 8 bit words
*/
#define SC16IS7XX_LCR_STOPLEN_BIT BIT(2) /* STOP length bit
*
* STOP length bit table:
* 0 -> 1 stop bit
* 1 -> 1-1.5 stop bits if
* word length is 5,
* 2 stop bits otherwise
*/
#define SC16IS7XX_LCR_PARITY_BIT BIT(3) /* Parity bit enable */
#define SC16IS7XX_LCR_EVENPARITY_BIT BIT(4) /* Even parity bit enable */
#define SC16IS7XX_LCR_FORCEPARITY_BIT BIT(5) /* 9-bit multidrop parity */
#define SC16IS7XX_LCR_TXBREAK_BIT BIT(6) /* TX break enable */
#define SC16IS7XX_LCR_DLAB_BIT BIT(7) /* Divisor Latch enable */
#define SC16IS7XX_LCR_WORD_LEN_5 (0x00)
#define SC16IS7XX_LCR_WORD_LEN_6 (0x01)
#define SC16IS7XX_LCR_WORD_LEN_7 (0x02)
#define SC16IS7XX_LCR_WORD_LEN_8 (0x03)
#define SC16IS7XX_LCR_CONF_MODE_A SC16IS7XX_LCR_DLAB_BIT /* Special
* reg set */
#define SC16IS7XX_LCR_CONF_MODE_B 0xBF /* Enhanced
* reg set */
/* MCR register bits */
#define SC16IS7XX_MCR_DTR_BIT BIT(0) /* DTR complement
* - only on 75x/76x
*/
#define SC16IS7XX_MCR_RTS_BIT BIT(1) /* RTS complement */
#define SC16IS7XX_MCR_TCRTLR_BIT BIT(2) /* TCR/TLR register enable */
#define SC16IS7XX_MCR_LOOP_BIT BIT(4) /* Enable loopback test mode */
#define SC16IS7XX_MCR_XONANY_BIT BIT(5) /* Enable Xon Any
* - write enabled
* if (EFR[4] == 1)
*/
#define SC16IS7XX_MCR_IRDA_BIT BIT(6) /* Enable IrDA mode
* - write enabled
* if (EFR[4] == 1)
*/
#define SC16IS7XX_MCR_CLKSEL_BIT BIT(7) /* Divide clock by 4
* - write enabled
* if (EFR[4] == 1)
*/
/* LSR register bits */
#define SC16IS7XX_LSR_DR_BIT BIT(0) /* Receiver data ready */
#define SC16IS7XX_LSR_OE_BIT BIT(1) /* Overrun Error */
#define SC16IS7XX_LSR_PE_BIT BIT(2) /* Parity Error */
#define SC16IS7XX_LSR_FE_BIT BIT(3) /* Frame Error */
#define SC16IS7XX_LSR_BI_BIT BIT(4) /* Break Interrupt */
#define SC16IS7XX_LSR_BRK_ERROR_MASK \
(SC16IS7XX_LSR_OE_BIT | \
SC16IS7XX_LSR_PE_BIT | \
SC16IS7XX_LSR_FE_BIT | \
SC16IS7XX_LSR_BI_BIT)
#define SC16IS7XX_LSR_THRE_BIT BIT(5) /* TX holding register empty */
#define SC16IS7XX_LSR_TEMT_BIT BIT(6) /* Transmitter empty */
#define SC16IS7XX_LSR_FIFOE_BIT BIT(7) /* Fifo Error */
/* MSR register bits */
#define SC16IS7XX_MSR_DCTS_BIT BIT(0) /* Delta CTS Clear To Send */
#define SC16IS7XX_MSR_DDSR_BIT BIT(1) /* Delta DSR Data Set Ready
* or (IO4)
* - only on 75x/76x
*/
#define SC16IS7XX_MSR_DRI_BIT BIT(2) /* Delta RI Ring Indicator
* or (IO7)
* - only on 75x/76x
*/
#define SC16IS7XX_MSR_DCD_BIT BIT(3) /* Delta CD Carrier Detect
* or (IO6)
* - only on 75x/76x
*/
#define SC16IS7XX_MSR_CTS_BIT BIT(4) /* CTS */
#define SC16IS7XX_MSR_DSR_BIT BIT(5) /* DSR (IO4)
* - only on 75x/76x
*/
#define SC16IS7XX_MSR_RI_BIT BIT(6) /* RI (IO7)
* - only on 75x/76x
*/
#define SC16IS7XX_MSR_CD_BIT BIT(7) /* CD (IO6)
* - only on 75x/76x
*/
/*
* TCR register bits
* TCR trigger levels are available from 0 to 60 characters with a granularity
* of four.
* The programmer must program the TCR such that TCR[3:0] > TCR[7:4]. There is
* no built-in hardware check to make sure this condition is met. Also, the TCR
* must be programmed with this condition before auto RTS or software flow
* control is enabled to avoid spurious operation of the device.
*/
#define SC16IS7XX_TCR_RX_HALT(words) ((((words) / 4) & 0x0f) << 0)
#define SC16IS7XX_TCR_RX_RESUME(words) ((((words) / 4) & 0x0f) << 4)
/*
* TLR register bits
* If TLR[3:0] or TLR[7:4] are logical 0, the selectable trigger levels via the
* FIFO Control Register (FCR) are used for the transmit and receive FIFO
* trigger levels. Trigger levels from 4 characters to 60 characters are
* available with a granularity of four.
*
* When the trigger level setting in TLR is zero, the SC16IS74x/75x/76x uses the
* trigger level setting defined in FCR. If TLR has non-zero trigger level value
* the trigger level defined in FCR is discarded. This applies to both transmit
* FIFO and receive FIFO trigger level setting.
*
* When TLR is used for RX trigger level control, FCR[7:6] should be left at the
* default state, that is, '00'.
*/
#define SC16IS7XX_TLR_TX_TRIGGER(words) ((((words) / 4) & 0x0f) << 0)
#define SC16IS7XX_TLR_RX_TRIGGER(words) ((((words) / 4) & 0x0f) << 4)
/* IOControl register bits (Only 75x/76x) */
#define SC16IS7XX_IOCONTROL_LATCH_BIT BIT(0) /* Enable input latching */
#define SC16IS7XX_IOCONTROL_MODEM_A_BIT BIT(1) /* Enable GPIO[7:4] as modem A pins */
#define SC16IS7XX_IOCONTROL_MODEM_B_BIT BIT(2) /* Enable GPIO[3:0] as modem B pins */
#define SC16IS7XX_IOCONTROL_SRESET_BIT BIT(3) /* Software Reset */
/* EFCR register bits */
#define SC16IS7XX_EFCR_9BIT_MODE_BIT BIT(0) /* Enable 9-bit or Multidrop
* mode (RS485) */
#define SC16IS7XX_EFCR_RXDISABLE_BIT BIT(1) /* Disable receiver */
#define SC16IS7XX_EFCR_TXDISABLE_BIT BIT(2) /* Disable transmitter */
#define SC16IS7XX_EFCR_AUTO_RS485_BIT BIT(4) /* Auto RS485 RTS direction */
#define SC16IS7XX_EFCR_RTS_INVERT_BIT BIT(5) /* RTS output inversion */
#define SC16IS7XX_EFCR_IRDA_MODE_BIT BIT(7) /* IrDA mode
* 0 = rate upto 115.2 kbit/s
* - Only 75x/76x
* 1 = rate upto 1.152 Mbit/s
* - Only 76x
*/
/* EFR register bits */
#define SC16IS7XX_EFR_AUTORTS_BIT BIT(6) /* Auto RTS flow ctrl enable */
#define SC16IS7XX_EFR_AUTOCTS_BIT BIT(7) /* Auto CTS flow ctrl enable */
#define SC16IS7XX_EFR_XOFF2_DETECT_BIT BIT(5) /* Enable Xoff2 detection */
#define SC16IS7XX_EFR_ENABLE_BIT BIT(4) /* Enable enhanced functions
* and writing to IER[7:4],
* FCR[5:4], MCR[7:5]
*/
#define SC16IS7XX_EFR_SWFLOW3_BIT BIT(3)
#define SC16IS7XX_EFR_SWFLOW2_BIT BIT(2)
/*
* SWFLOW bits 3 & 2 table:
* 00 -> no transmitter flow
* control
* 01 -> transmitter generates
* XON2 and XOFF2
* 10 -> transmitter generates
* XON1 and XOFF1
* 11 -> transmitter generates
* XON1, XON2, XOFF1 and
* XOFF2
*/
#define SC16IS7XX_EFR_SWFLOW1_BIT BIT(1)
#define SC16IS7XX_EFR_SWFLOW0_BIT BIT(0)
/*
* SWFLOW bits 1 & 0 table:
* 00 -> no received flow
* control
* 01 -> receiver compares
* XON2 and XOFF2
* 10 -> receiver compares
* XON1 and XOFF1
* 11 -> receiver compares
* XON1, XON2, XOFF1 and
* XOFF2
*/
#define SC16IS7XX_EFR_FLOWCTRL_BITS (SC16IS7XX_EFR_AUTORTS_BIT | \
SC16IS7XX_EFR_AUTOCTS_BIT | \
SC16IS7XX_EFR_XOFF2_DETECT_BIT | \
SC16IS7XX_EFR_SWFLOW3_BIT | \
SC16IS7XX_EFR_SWFLOW2_BIT | \
SC16IS7XX_EFR_SWFLOW1_BIT | \
SC16IS7XX_EFR_SWFLOW0_BIT)
/* Misc definitions */
#define SC16IS7XX_FIFO_SIZE (64)
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
#define SC16IS7XX_GPIOS_PER_BANK 4
#define SC16IS7XX_POLL_PERIOD_MS 10
#define SC16IS7XX_RECONF_MD BIT(0)
#define SC16IS7XX_RECONF_IER BIT(1)
#define SC16IS7XX_RECONF_RS485 BIT(2)
struct sc16is7xx_one_config {
unsigned int flags;
u8 ier_mask;
u8 ier_val;
};
struct sc16is7xx_one {
struct uart_port port;
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
struct regmap *regmap;
struct mutex efr_lock; /* EFR registers access */
struct kthread_work tx_work;
struct kthread_work reg_work;
struct kthread_delayed_work ms_work;
struct sc16is7xx_one_config config;
unsigned char buf[SC16IS7XX_FIFO_SIZE]; /* Rx buffer. */
unsigned int old_mctrl;
u8 old_lcr; /* Value before EFR access. */
bool irda_mode;
};
struct sc16is7xx_port {
const struct sc16is7xx_devtype *devtype;
struct clk *clk;
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio;
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
unsigned long gpio_valid_mask;
#endif
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
u8 mctrl_mask;
struct kthread_worker kworker;
struct task_struct *kworker_task;
struct kthread_delayed_work poll_work;
bool polling;
struct sc16is7xx_one p[];
};
static DEFINE_IDA(sc16is7xx_lines);
static struct uart_driver sc16is7xx_uart = {
.owner = THIS_MODULE,
.driver_name = SC16IS7XX_NAME,
.dev_name = "ttySC",
.nr = SC16IS7XX_MAX_DEVS,
};
#define to_sc16is7xx_one(p,e) ((container_of((p), struct sc16is7xx_one, e)))
static u8 sc16is7xx_port_read(struct uart_port *port, u8 reg)
{
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
unsigned int val = 0;
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
regmap_read(one->regmap, reg, &val);
return val;
}
static void sc16is7xx_port_write(struct uart_port *port, u8 reg, u8 val)
{
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
regmap_write(one->regmap, reg, val);
}
static void sc16is7xx_fifo_read(struct uart_port *port, u8 *rxbuf, unsigned int rxlen)
{
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
regmap_noinc_read(one->regmap, SC16IS7XX_RHR_REG, rxbuf, rxlen);
}
static void sc16is7xx_fifo_write(struct uart_port *port, u8 *txbuf, u8 to_send)
{
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
/*
* Don't send zero-length data, at least on SPI it confuses the chip
* delivering wrong TXLVL data.
*/
if (unlikely(!to_send))
return;
regmap_noinc_write(one->regmap, SC16IS7XX_THR_REG, txbuf, to_send);
}
static void sc16is7xx_port_update(struct uart_port *port, u8 reg,
u8 mask, u8 val)
{
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
regmap_update_bits(one->regmap, reg, mask, val);
}
static void sc16is7xx_power(struct uart_port *port, int on)
{
sc16is7xx_port_update(port, SC16IS7XX_IER_REG,
SC16IS7XX_IER_SLEEP_BIT,
on ? 0 : SC16IS7XX_IER_SLEEP_BIT);
}
/*
* In an amazing feat of design, the Enhanced Features Register (EFR)
* shares the address of the Interrupt Identification Register (IIR).
* Access to EFR is switched on by writing a magic value (0xbf) to the
* Line Control Register (LCR). Any interrupt firing during this time will
* see the EFR where it expects the IIR to be, leading to
* "Unexpected interrupt" messages.
*
* Prevent this possibility by claiming a mutex while accessing the EFR,
* and claiming the same mutex from within the interrupt handler. This is
* similar to disabling the interrupt, but that doesn't work because the
* bulk of the interrupt processing is run as a workqueue job in thread
* context.
*/
static void sc16is7xx_efr_lock(struct uart_port *port)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
mutex_lock(&one->efr_lock);
/* Backup content of LCR. */
one->old_lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
/* Enable access to Enhanced register set */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_CONF_MODE_B);
/* Disable cache updates when writing to EFR registers */
regcache_cache_bypass(one->regmap, true);
}
static void sc16is7xx_efr_unlock(struct uart_port *port)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
/* Re-enable cache updates when writing to normal registers */
regcache_cache_bypass(one->regmap, false);
/* Restore original content of LCR */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, one->old_lcr);
mutex_unlock(&one->efr_lock);
}
static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
lockdep_assert_held_once(&port->lock);
one->config.flags |= SC16IS7XX_RECONF_IER;
one->config.ier_mask |= bit;
one->config.ier_val &= ~bit;
kthread_queue_work(&s->kworker, &one->reg_work);
}
static void sc16is7xx_ier_set(struct uart_port *port, u8 bit)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
lockdep_assert_held_once(&port->lock);
one->config.flags |= SC16IS7XX_RECONF_IER;
one->config.ier_mask |= bit;
one->config.ier_val |= bit;
kthread_queue_work(&s->kworker, &one->reg_work);
}
static void sc16is7xx_stop_tx(struct uart_port *port)
{
sc16is7xx_ier_clear(port, SC16IS7XX_IER_THRI_BIT);
}
static void sc16is7xx_stop_rx(struct uart_port *port)
{
sc16is7xx_ier_clear(port, SC16IS7XX_IER_RDI_BIT);
}
const struct sc16is7xx_devtype sc16is74x_devtype = {
.name = "SC16IS74X",
.nr_gpio = 0,
.nr_uart = 1,
};
EXPORT_SYMBOL_GPL(sc16is74x_devtype);
const struct sc16is7xx_devtype sc16is750_devtype = {
.name = "SC16IS750",
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
.nr_gpio = 8,
.nr_uart = 1,
};
EXPORT_SYMBOL_GPL(sc16is750_devtype);
const struct sc16is7xx_devtype sc16is752_devtype = {
.name = "SC16IS752",
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
.nr_gpio = 8,
.nr_uart = 2,
};
EXPORT_SYMBOL_GPL(sc16is752_devtype);
const struct sc16is7xx_devtype sc16is760_devtype = {
.name = "SC16IS760",
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
.nr_gpio = 8,
.nr_uart = 1,
};
EXPORT_SYMBOL_GPL(sc16is760_devtype);
const struct sc16is7xx_devtype sc16is762_devtype = {
.name = "SC16IS762",
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
.nr_gpio = 8,
.nr_uart = 2,
};
EXPORT_SYMBOL_GPL(sc16is762_devtype);
static bool sc16is7xx_regmap_volatile(struct device *dev, unsigned int reg)
{
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
switch (reg) {
case SC16IS7XX_RHR_REG:
case SC16IS7XX_IIR_REG:
case SC16IS7XX_LSR_REG:
case SC16IS7XX_MSR_REG:
case SC16IS7XX_TXLVL_REG:
case SC16IS7XX_RXLVL_REG:
case SC16IS7XX_IOSTATE_REG:
case SC16IS7XX_IOCONTROL_REG:
return true;
default:
return false;
}
}
static bool sc16is7xx_regmap_precious(struct device *dev, unsigned int reg)
{
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
switch (reg) {
case SC16IS7XX_RHR_REG:
return true;
default:
return false;
}
}
static bool sc16is7xx_regmap_noinc(struct device *dev, unsigned int reg)
{
return reg == SC16IS7XX_RHR_REG;
}
/*
* Configure programmable baud rate generator (divisor) according to the
* desired baud rate.
*
* From the datasheet, the divisor is computed according to:
*
* XTAL1 input frequency
* -----------------------
* prescaler
* divisor = ---------------------------
* baud-rate x sampling-rate
*/
static int sc16is7xx_set_baud(struct uart_port *port, int baud)
{
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
u8 lcr;
unsigned int prescaler = 1;
unsigned long clk = port->uartclk, div = clk / 16 / baud;
if (div >= BIT(16)) {
prescaler = 4;
div /= prescaler;
}
/* Enable enhanced features */
sc16is7xx_efr_lock(port);
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
SC16IS7XX_EFR_ENABLE_BIT,
SC16IS7XX_EFR_ENABLE_BIT);
sc16is7xx_efr_unlock(port);
/* If bit MCR_CLKSEL is set, the divide by 4 prescaler is activated. */
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
SC16IS7XX_MCR_CLKSEL_BIT,
prescaler == 1 ? 0 : SC16IS7XX_MCR_CLKSEL_BIT);
mutex_lock(&one->efr_lock);
/* Backup LCR and access special register set (DLL/DLH) */
lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
SC16IS7XX_LCR_CONF_MODE_A);
/* Write the new divisor */
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
regcache_cache_bypass(one->regmap, true);
sc16is7xx_port_write(port, SC16IS7XX_DLH_REG, div / 256);
sc16is7xx_port_write(port, SC16IS7XX_DLL_REG, div % 256);
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
regcache_cache_bypass(one->regmap, false);
/* Restore LCR and access to general register set */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
mutex_unlock(&one->efr_lock);
return DIV_ROUND_CLOSEST((clk / prescaler) / 16, div);
}
static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
unsigned int iir)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
serial: drivers: switch ch and flag to u8 Now that the serial layer explicitly expects 'u8' for flags and characters, propagate this type to drivers' (RX) routines. Note that amba-pl011's, clps711x's and st-asc's 'ch' are left unchanged because 'ch' contains not only a character, but whole status. Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Claudiu Beznea <claudiu.beznea@microchip.com> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Kevin Cernekee <cernekee@gmail.com> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Alim Akhtar <alim.akhtar@samsung.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Paul Walmsley <paul.walmsley@sifive.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: Hammer Hsieh <hammerh0314@gmail.com> Acked-by: Richard GENOUD <richard.genoud@gmail.com> Acked-by: Tobias Klauser <tklauser@distanz.ch> Acked-by: Thierry Reding <treding@nvidia.com> Acked-by: Maciej W. Rozycki <macro@orcam.me.uk> Link: https://lore.kernel.org/r/20230712081811.29004-11-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-07-12 10:18:11 +02:00
unsigned int lsr = 0, bytes_read, i;
bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC) ? true : false;
serial: drivers: switch ch and flag to u8 Now that the serial layer explicitly expects 'u8' for flags and characters, propagate this type to drivers' (RX) routines. Note that amba-pl011's, clps711x's and st-asc's 'ch' are left unchanged because 'ch' contains not only a character, but whole status. Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Tobias Klauser <tklauser@distanz.ch> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Claudiu Beznea <claudiu.beznea@microchip.com> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: "Maciej W. Rozycki" <macro@orcam.me.uk> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Pengutronix Kernel Team <kernel@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: NXP Linux Team <linux-imx@nxp.com> Cc: Kevin Cernekee <cernekee@gmail.com> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Alim Akhtar <alim.akhtar@samsung.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Palmer Dabbelt <palmer@dabbelt.com> Cc: Paul Walmsley <paul.walmsley@sifive.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: Hammer Hsieh <hammerh0314@gmail.com> Acked-by: Richard GENOUD <richard.genoud@gmail.com> Acked-by: Tobias Klauser <tklauser@distanz.ch> Acked-by: Thierry Reding <treding@nvidia.com> Acked-by: Maciej W. Rozycki <macro@orcam.me.uk> Link: https://lore.kernel.org/r/20230712081811.29004-11-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-07-12 10:18:11 +02:00
u8 ch, flag;
if (unlikely(rxlen >= sizeof(one->buf))) {
dev_warn_ratelimited(port->dev,
"ttySC%i: Possible RX FIFO overrun: %d\n",
port->line, rxlen);
port->icount.buf_overrun++;
/* Ensure sanity of RX level */
rxlen = sizeof(one->buf);
}
while (rxlen) {
/* Only read lsr if there are possible errors in FIFO */
if (read_lsr) {
lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG);
if (!(lsr & SC16IS7XX_LSR_FIFOE_BIT))
read_lsr = false; /* No errors left in FIFO */
} else
lsr = 0;
if (read_lsr) {
one->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG);
bytes_read = 1;
} else {
sc16is7xx_fifo_read(port, one->buf, rxlen);
bytes_read = rxlen;
}
lsr &= SC16IS7XX_LSR_BRK_ERROR_MASK;
port->icount.rx++;
flag = TTY_NORMAL;
if (unlikely(lsr)) {
if (lsr & SC16IS7XX_LSR_BI_BIT) {
port->icount.brk++;
if (uart_handle_break(port))
continue;
} else if (lsr & SC16IS7XX_LSR_PE_BIT)
port->icount.parity++;
else if (lsr & SC16IS7XX_LSR_FE_BIT)
port->icount.frame++;
else if (lsr & SC16IS7XX_LSR_OE_BIT)
port->icount.overrun++;
lsr &= port->read_status_mask;
if (lsr & SC16IS7XX_LSR_BI_BIT)
flag = TTY_BREAK;
else if (lsr & SC16IS7XX_LSR_PE_BIT)
flag = TTY_PARITY;
else if (lsr & SC16IS7XX_LSR_FE_BIT)
flag = TTY_FRAME;
else if (lsr & SC16IS7XX_LSR_OE_BIT)
flag = TTY_OVERRUN;
}
for (i = 0; i < bytes_read; ++i) {
ch = one->buf[i];
if (uart_handle_sysrq_char(port, ch))
continue;
if (lsr & port->ignore_status_mask)
continue;
uart_insert_char(port, lsr, SC16IS7XX_LSR_OE_BIT, ch,
flag);
}
rxlen -= bytes_read;
}
tty_flip_buffer_push(&port->state->port);
}
static void sc16is7xx_handle_tx(struct uart_port *port)
{
tty: serial: switch from circ_buf to kfifo Switch from struct circ_buf to proper kfifo. kfifo provides much better API, esp. when wrap-around of the buffer needs to be taken into account. Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example. Kfifo API can also fill in scatter-gather DMA structures, so it easier for that use case too. Look at lpuart_dma_tx() for example. Note that not all drivers can be converted to that (like atmel_serial), they handle DMA specially. Note that usb-serial uses kfifo for TX for ages. omap needed a bit more care as it needs to put a char into FIFO to start the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to do kfifo_dma_out_prepare twice: once to find out the tx_size (to find out if it is worths to do DMA at all -- size >= 4), the second time for the actual transfer. All traces of circ_buf are removed from serial_core.h (and its struct uart_state). Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Al Cooper <alcooperx@gmail.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Cc: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com> Cc: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Claudiu Beznea <claudiu.beznea@tuxon.dev> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: Maciej W. Rozycki <macro@orcam.me.uk> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: Neil Armstrong <neil.armstrong@linaro.org> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Bjorn Andersson <andersson@kernel.org> Cc: Konrad Dybcio <konrad.dybcio@linaro.org> Cc: Pali Rohár <pali@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Nicholas Piggin <npiggin@gmail.com> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org> Cc: Naveen N. Rao <naveen.n.rao@linux.ibm.com> Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Alim Akhtar <alim.akhtar@samsung.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: David S. Miller <davem@davemloft.net> Cc: Hammer Hsieh <hammerh0314@gmail.com> Cc: Peter Korsgaard <jacmet@sunsite.dk> Cc: Timur Tabi <timur@kernel.org> Cc: Michal Simek <michal.simek@amd.com> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Christian König <christian.koenig@amd.com> Link: https://lore.kernel.org/r/20240405060826.2521-13-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-04-05 08:08:23 +02:00
struct tty_port *tport = &port->state->port;
unsigned long flags;
tty: serial: switch from circ_buf to kfifo Switch from struct circ_buf to proper kfifo. kfifo provides much better API, esp. when wrap-around of the buffer needs to be taken into account. Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example. Kfifo API can also fill in scatter-gather DMA structures, so it easier for that use case too. Look at lpuart_dma_tx() for example. Note that not all drivers can be converted to that (like atmel_serial), they handle DMA specially. Note that usb-serial uses kfifo for TX for ages. omap needed a bit more care as it needs to put a char into FIFO to start the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to do kfifo_dma_out_prepare twice: once to find out the tx_size (to find out if it is worths to do DMA at all -- size >= 4), the second time for the actual transfer. All traces of circ_buf are removed from serial_core.h (and its struct uart_state). Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Al Cooper <alcooperx@gmail.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Cc: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com> Cc: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Claudiu Beznea <claudiu.beznea@tuxon.dev> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: Maciej W. Rozycki <macro@orcam.me.uk> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: Neil Armstrong <neil.armstrong@linaro.org> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Bjorn Andersson <andersson@kernel.org> Cc: Konrad Dybcio <konrad.dybcio@linaro.org> Cc: Pali Rohár <pali@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Nicholas Piggin <npiggin@gmail.com> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org> Cc: Naveen N. Rao <naveen.n.rao@linux.ibm.com> Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Alim Akhtar <alim.akhtar@samsung.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: David S. Miller <davem@davemloft.net> Cc: Hammer Hsieh <hammerh0314@gmail.com> Cc: Peter Korsgaard <jacmet@sunsite.dk> Cc: Timur Tabi <timur@kernel.org> Cc: Michal Simek <michal.simek@amd.com> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Christian König <christian.koenig@amd.com> Link: https://lore.kernel.org/r/20240405060826.2521-13-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-04-05 08:08:23 +02:00
unsigned int txlen;
unsigned char *tail;
if (unlikely(port->x_char)) {
sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char);
port->icount.tx++;
port->x_char = 0;
return;
}
tty: serial: switch from circ_buf to kfifo Switch from struct circ_buf to proper kfifo. kfifo provides much better API, esp. when wrap-around of the buffer needs to be taken into account. Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example. Kfifo API can also fill in scatter-gather DMA structures, so it easier for that use case too. Look at lpuart_dma_tx() for example. Note that not all drivers can be converted to that (like atmel_serial), they handle DMA specially. Note that usb-serial uses kfifo for TX for ages. omap needed a bit more care as it needs to put a char into FIFO to start the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to do kfifo_dma_out_prepare twice: once to find out the tx_size (to find out if it is worths to do DMA at all -- size >= 4), the second time for the actual transfer. All traces of circ_buf are removed from serial_core.h (and its struct uart_state). Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Al Cooper <alcooperx@gmail.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Cc: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com> Cc: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Claudiu Beznea <claudiu.beznea@tuxon.dev> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: Maciej W. Rozycki <macro@orcam.me.uk> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: Neil Armstrong <neil.armstrong@linaro.org> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Bjorn Andersson <andersson@kernel.org> Cc: Konrad Dybcio <konrad.dybcio@linaro.org> Cc: Pali Rohár <pali@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Nicholas Piggin <npiggin@gmail.com> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org> Cc: Naveen N. Rao <naveen.n.rao@linux.ibm.com> Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Alim Akhtar <alim.akhtar@samsung.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: David S. Miller <davem@davemloft.net> Cc: Hammer Hsieh <hammerh0314@gmail.com> Cc: Peter Korsgaard <jacmet@sunsite.dk> Cc: Timur Tabi <timur@kernel.org> Cc: Michal Simek <michal.simek@amd.com> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Christian König <christian.koenig@amd.com> Link: https://lore.kernel.org/r/20240405060826.2521-13-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-04-05 08:08:23 +02:00
if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
uart_port_lock_irqsave(port, &flags);
sc16is7xx_stop_tx(port);
uart_port_unlock_irqrestore(port, flags);
return;
}
tty: serial: switch from circ_buf to kfifo Switch from struct circ_buf to proper kfifo. kfifo provides much better API, esp. when wrap-around of the buffer needs to be taken into account. Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example. Kfifo API can also fill in scatter-gather DMA structures, so it easier for that use case too. Look at lpuart_dma_tx() for example. Note that not all drivers can be converted to that (like atmel_serial), they handle DMA specially. Note that usb-serial uses kfifo for TX for ages. omap needed a bit more care as it needs to put a char into FIFO to start the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to do kfifo_dma_out_prepare twice: once to find out the tx_size (to find out if it is worths to do DMA at all -- size >= 4), the second time for the actual transfer. All traces of circ_buf are removed from serial_core.h (and its struct uart_state). Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Al Cooper <alcooperx@gmail.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Cc: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com> Cc: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Claudiu Beznea <claudiu.beznea@tuxon.dev> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: Maciej W. Rozycki <macro@orcam.me.uk> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: Neil Armstrong <neil.armstrong@linaro.org> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Bjorn Andersson <andersson@kernel.org> Cc: Konrad Dybcio <konrad.dybcio@linaro.org> Cc: Pali Rohár <pali@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Nicholas Piggin <npiggin@gmail.com> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org> Cc: Naveen N. Rao <naveen.n.rao@linux.ibm.com> Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Alim Akhtar <alim.akhtar@samsung.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: David S. Miller <davem@davemloft.net> Cc: Hammer Hsieh <hammerh0314@gmail.com> Cc: Peter Korsgaard <jacmet@sunsite.dk> Cc: Timur Tabi <timur@kernel.org> Cc: Michal Simek <michal.simek@amd.com> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Christian König <christian.koenig@amd.com> Link: https://lore.kernel.org/r/20240405060826.2521-13-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-04-05 08:08:23 +02:00
/* Limit to space available in TX FIFO */
txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG);
if (txlen > SC16IS7XX_FIFO_SIZE) {
dev_err_ratelimited(port->dev,
"chip reports %d free bytes in TX fifo, but it only has %d",
txlen, SC16IS7XX_FIFO_SIZE);
txlen = 0;
}
txlen = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen);
sc16is7xx_fifo_write(port, tail, txlen);
uart_xmit_advance(port, txlen);
tty: serial: switch from circ_buf to kfifo Switch from struct circ_buf to proper kfifo. kfifo provides much better API, esp. when wrap-around of the buffer needs to be taken into account. Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example. Kfifo API can also fill in scatter-gather DMA structures, so it easier for that use case too. Look at lpuart_dma_tx() for example. Note that not all drivers can be converted to that (like atmel_serial), they handle DMA specially. Note that usb-serial uses kfifo for TX for ages. omap needed a bit more care as it needs to put a char into FIFO to start the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to do kfifo_dma_out_prepare twice: once to find out the tx_size (to find out if it is worths to do DMA at all -- size >= 4), the second time for the actual transfer. All traces of circ_buf are removed from serial_core.h (and its struct uart_state). Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Al Cooper <alcooperx@gmail.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Cc: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com> Cc: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Claudiu Beznea <claudiu.beznea@tuxon.dev> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: Maciej W. Rozycki <macro@orcam.me.uk> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: Neil Armstrong <neil.armstrong@linaro.org> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Bjorn Andersson <andersson@kernel.org> Cc: Konrad Dybcio <konrad.dybcio@linaro.org> Cc: Pali Rohár <pali@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Nicholas Piggin <npiggin@gmail.com> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org> Cc: Naveen N. Rao <naveen.n.rao@linux.ibm.com> Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Alim Akhtar <alim.akhtar@samsung.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: David S. Miller <davem@davemloft.net> Cc: Hammer Hsieh <hammerh0314@gmail.com> Cc: Peter Korsgaard <jacmet@sunsite.dk> Cc: Timur Tabi <timur@kernel.org> Cc: Michal Simek <michal.simek@amd.com> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Christian König <christian.koenig@amd.com> Link: https://lore.kernel.org/r/20240405060826.2521-13-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-04-05 08:08:23 +02:00
uart_port_lock_irqsave(port, &flags);
tty: serial: switch from circ_buf to kfifo Switch from struct circ_buf to proper kfifo. kfifo provides much better API, esp. when wrap-around of the buffer needs to be taken into account. Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example. Kfifo API can also fill in scatter-gather DMA structures, so it easier for that use case too. Look at lpuart_dma_tx() for example. Note that not all drivers can be converted to that (like atmel_serial), they handle DMA specially. Note that usb-serial uses kfifo for TX for ages. omap needed a bit more care as it needs to put a char into FIFO to start the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to do kfifo_dma_out_prepare twice: once to find out the tx_size (to find out if it is worths to do DMA at all -- size >= 4), the second time for the actual transfer. All traces of circ_buf are removed from serial_core.h (and its struct uart_state). Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Al Cooper <alcooperx@gmail.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Cc: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com> Cc: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Claudiu Beznea <claudiu.beznea@tuxon.dev> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: Maciej W. Rozycki <macro@orcam.me.uk> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: Neil Armstrong <neil.armstrong@linaro.org> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Bjorn Andersson <andersson@kernel.org> Cc: Konrad Dybcio <konrad.dybcio@linaro.org> Cc: Pali Rohár <pali@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Nicholas Piggin <npiggin@gmail.com> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org> Cc: Naveen N. Rao <naveen.n.rao@linux.ibm.com> Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Alim Akhtar <alim.akhtar@samsung.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: David S. Miller <davem@davemloft.net> Cc: Hammer Hsieh <hammerh0314@gmail.com> Cc: Peter Korsgaard <jacmet@sunsite.dk> Cc: Timur Tabi <timur@kernel.org> Cc: Michal Simek <michal.simek@amd.com> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Christian König <christian.koenig@amd.com> Link: https://lore.kernel.org/r/20240405060826.2521-13-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-04-05 08:08:23 +02:00
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
uart_write_wakeup(port);
tty: serial: switch from circ_buf to kfifo Switch from struct circ_buf to proper kfifo. kfifo provides much better API, esp. when wrap-around of the buffer needs to be taken into account. Look at pl011_dma_tx_refill() or cpm_uart_tx_pump() changes for example. Kfifo API can also fill in scatter-gather DMA structures, so it easier for that use case too. Look at lpuart_dma_tx() for example. Note that not all drivers can be converted to that (like atmel_serial), they handle DMA specially. Note that usb-serial uses kfifo for TX for ages. omap needed a bit more care as it needs to put a char into FIFO to start the DMA transfer when OMAP_DMA_TX_KICK is set. In that case, we have to do kfifo_dma_out_prepare twice: once to find out the tx_size (to find out if it is worths to do DMA at all -- size >= 4), the second time for the actual transfer. All traces of circ_buf are removed from serial_core.h (and its struct uart_state). Signed-off-by: Jiri Slaby (SUSE) <jirislaby@kernel.org> Cc: Al Cooper <alcooperx@gmail.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Cc: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com> Cc: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com> Cc: Russell King <linux@armlinux.org.uk> Cc: Vineet Gupta <vgupta@kernel.org> Cc: Richard Genoud <richard.genoud@gmail.com> Cc: Nicolas Ferre <nicolas.ferre@microchip.com> Cc: Alexandre Belloni <alexandre.belloni@bootlin.com> Cc: Claudiu Beznea <claudiu.beznea@tuxon.dev> Cc: Alexander Shiyan <shc_work@mail.ru> Cc: Baruch Siach <baruch@tkos.co.il> Cc: Maciej W. Rozycki <macro@orcam.me.uk> Cc: Shawn Guo <shawnguo@kernel.org> Cc: Sascha Hauer <s.hauer@pengutronix.de> Cc: Fabio Estevam <festevam@gmail.com> Cc: Neil Armstrong <neil.armstrong@linaro.org> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Taichi Sugaya <sugaya.taichi@socionext.com> Cc: Takao Orito <orito.takao@socionext.com> Cc: Bjorn Andersson <andersson@kernel.org> Cc: Konrad Dybcio <konrad.dybcio@linaro.org> Cc: Pali Rohár <pali@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Nicholas Piggin <npiggin@gmail.com> Cc: Christophe Leroy <christophe.leroy@csgroup.eu> Cc: Aneesh Kumar K.V <aneesh.kumar@kernel.org> Cc: Naveen N. Rao <naveen.n.rao@linux.ibm.com> Cc: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Cc: Alim Akhtar <alim.akhtar@samsung.com> Cc: Laxman Dewangan <ldewangan@nvidia.com> Cc: Thierry Reding <thierry.reding@gmail.com> Cc: Jonathan Hunter <jonathanh@nvidia.com> Cc: Orson Zhai <orsonzhai@gmail.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: Chunyan Zhang <zhang.lyra@gmail.com> Cc: Patrice Chotard <patrice.chotard@foss.st.com> Cc: Maxime Coquelin <mcoquelin.stm32@gmail.com> Cc: Alexandre Torgue <alexandre.torgue@foss.st.com> Cc: David S. Miller <davem@davemloft.net> Cc: Hammer Hsieh <hammerh0314@gmail.com> Cc: Peter Korsgaard <jacmet@sunsite.dk> Cc: Timur Tabi <timur@kernel.org> Cc: Michal Simek <michal.simek@amd.com> Cc: Sumit Semwal <sumit.semwal@linaro.org> Cc: Christian König <christian.koenig@amd.com> Link: https://lore.kernel.org/r/20240405060826.2521-13-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-04-05 08:08:23 +02:00
if (kfifo_is_empty(&tport->xmit_fifo))
sc16is7xx_stop_tx(port);
serial: sc16is7xx: fix unconditional activation of THRI interrupt Commit cc4c1d05eb10 ("sc16is7xx: Properly resume TX after stop") changed behavior to unconditionnaly set the THRI interrupt in sc16is7xx_tx_proc(). For example when sending a 65 bytes message, and assuming the Tx FIFO is initially empty, sc16is7xx_handle_tx() will write the first 64 bytes of the message to the FIFO and sc16is7xx_tx_proc() will then activate THRI. When the THRI IRQ is fired, the driver will write the remaining byte of the message to the FIFO, and disable THRI by calling sc16is7xx_stop_tx(). When sending a 2 bytes message, sc16is7xx_handle_tx() will write the 2 bytes of the message to the FIFO and call sc16is7xx_stop_tx(), disabling THRI. After sc16is7xx_handle_tx() exits, control returns to sc16is7xx_tx_proc() which will unconditionally set THRI. When the THRI IRQ is fired, the driver simply acknowledges the interrupt and does nothing more, since all the data has already been written to the FIFO. This results in 2 register writes and 4 register reads all for nothing and taking precious cycles from the I2C/SPI bus. Fix this by enabling the THRI interrupt only when we fill the Tx FIFO to its maximum capacity and there are remaining bytes to send in the message. Fixes: cc4c1d05eb10 ("sc16is7xx: Properly resume TX after stop") Cc: <stable@vger.kernel.org> Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231211171353.2901416-7-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-11 12:13:53 -05:00
else
sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT);
uart_port_unlock_irqrestore(port, flags);
}
static unsigned int sc16is7xx_get_hwmctrl(struct uart_port *port)
{
u8 msr = sc16is7xx_port_read(port, SC16IS7XX_MSR_REG);
unsigned int mctrl = 0;
mctrl |= (msr & SC16IS7XX_MSR_CTS_BIT) ? TIOCM_CTS : 0;
mctrl |= (msr & SC16IS7XX_MSR_DSR_BIT) ? TIOCM_DSR : 0;
mctrl |= (msr & SC16IS7XX_MSR_CD_BIT) ? TIOCM_CAR : 0;
mctrl |= (msr & SC16IS7XX_MSR_RI_BIT) ? TIOCM_RNG : 0;
return mctrl;
}
static void sc16is7xx_update_mlines(struct sc16is7xx_one *one)
{
struct uart_port *port = &one->port;
unsigned long flags;
unsigned int status, changed;
lockdep_assert_held_once(&one->efr_lock);
status = sc16is7xx_get_hwmctrl(port);
changed = status ^ one->old_mctrl;
if (changed == 0)
return;
one->old_mctrl = status;
uart_port_lock_irqsave(port, &flags);
if ((changed & TIOCM_RNG) && (status & TIOCM_RNG))
port->icount.rng++;
if (changed & TIOCM_DSR)
port->icount.dsr++;
if (changed & TIOCM_CAR)
uart_handle_dcd_change(port, status & TIOCM_CAR);
if (changed & TIOCM_CTS)
uart_handle_cts_change(port, status & TIOCM_CTS);
wake_up_interruptible(&port->state->port.delta_msr_wait);
uart_port_unlock_irqrestore(port, flags);
}
sc16is7xx: Fix for multi-channel stall The SC16IS752 is a dual-channel device. The two channels are largely independent, but the IRQ signals are wired together as an open-drain, active low signal which will be driven low while either of the channels requires attention, which can be for significant periods of time until operations complete and the interrupt can be acknowledged. In that respect it is should be treated as a true level-sensitive IRQ. The kernel, however, needs to be able to exit interrupt context in order to use I2C or SPI to access the device registers (which may involve sleeping). Therefore the interrupt needs to be masked out or paused in some way. The usual way to manage sleeping from within an interrupt handler is to use a threaded interrupt handler - a regular interrupt routine does the minimum amount of work needed to triage the interrupt before waking the interrupt service thread. If the threaded IRQ is marked as IRQF_ONESHOT the kernel will automatically mask out the interrupt until the thread runs to completion. The sc16is7xx driver used to use a threaded IRQ, but a patch switched to using a kthread_worker in order to set realtime priorities on the handler thread and for other optimisations. The end result is non-threaded IRQ that schedules some work then returns IRQ_HANDLED, making the kernel think that all IRQ processing has completed. The work-around to prevent a constant stream of interrupts is to mark the interrupt as edge-sensitive rather than level-sensitive, but interpreting an active-low source as a falling-edge source requires care to prevent a total cessation of interrupts. Whereas an edge-triggering source will generate a new edge for every interrupt condition a level-triggering source will keep the signal at the interrupting level until it no longer requires attention; in other words, the host won't see another edge until all interrupt conditions are cleared. It is therefore vital that the interrupt handler does not exit with an outstanding interrupt condition, otherwise the kernel will not receive another interrupt unless some other operation causes the interrupt state on the device to be cleared. The existing sc16is7xx driver has a very simple interrupt "thread" (kthread_work job) that processes interrupts on each channel in turn until there are no more. If both channels are active and the first channel starts interrupting while the handler for the second channel is running then it will not be detected and an IRQ stall ensues. This could be handled easily if there was a shared IRQ status register, or a convenient way to determine if the IRQ had been deasserted for any length of time, but both appear to be lacking. Avoid this problem (or at least make it much less likely to happen) by reducing the granularity of per-channel interrupt processing to one condition per iteration, only exiting the overall loop when both channels are no longer interrupting. Signed-off-by: Phil Elwell <phil@raspberrypi.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-09-12 15:31:55 +01:00
static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno)
{
bool rc = true;
unsigned int iir, rxlen;
struct uart_port *port = &s->p[portno].port;
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
mutex_lock(&one->efr_lock);
iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG);
if (iir & SC16IS7XX_IIR_NO_INT_BIT) {
rc = false;
goto out_port_irq;
}
iir &= SC16IS7XX_IIR_ID_MASK;
switch (iir) {
case SC16IS7XX_IIR_RDI_SRC:
case SC16IS7XX_IIR_RLSE_SRC:
case SC16IS7XX_IIR_RTOI_SRC:
case SC16IS7XX_IIR_XOFFI_SRC:
rxlen = sc16is7xx_port_read(port, SC16IS7XX_RXLVL_REG);
/*
* There is a silicon bug that makes the chip report a
* time-out interrupt but no data in the FIFO. This is
* described in errata section 18.1.4.
*
* When this happens, read one byte from the FIFO to
* clear the interrupt.
*/
if (iir == SC16IS7XX_IIR_RTOI_SRC && !rxlen)
rxlen = 1;
if (rxlen)
sc16is7xx_handle_rx(port, rxlen, iir);
break;
/* CTSRTS interrupt comes only when CTS goes inactive */
case SC16IS7XX_IIR_CTSRTS_SRC:
case SC16IS7XX_IIR_MSI_SRC:
sc16is7xx_update_mlines(one);
break;
case SC16IS7XX_IIR_THRI_SRC:
sc16is7xx_handle_tx(port);
break;
default:
dev_err_ratelimited(port->dev,
"ttySC%i: Unexpected interrupt: %x",
port->line, iir);
break;
}
out_port_irq:
mutex_unlock(&one->efr_lock);
return rc;
}
static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
{
bool keep_polling;
struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
do {
sc16is7xx: Fix for multi-channel stall The SC16IS752 is a dual-channel device. The two channels are largely independent, but the IRQ signals are wired together as an open-drain, active low signal which will be driven low while either of the channels requires attention, which can be for significant periods of time until operations complete and the interrupt can be acknowledged. In that respect it is should be treated as a true level-sensitive IRQ. The kernel, however, needs to be able to exit interrupt context in order to use I2C or SPI to access the device registers (which may involve sleeping). Therefore the interrupt needs to be masked out or paused in some way. The usual way to manage sleeping from within an interrupt handler is to use a threaded interrupt handler - a regular interrupt routine does the minimum amount of work needed to triage the interrupt before waking the interrupt service thread. If the threaded IRQ is marked as IRQF_ONESHOT the kernel will automatically mask out the interrupt until the thread runs to completion. The sc16is7xx driver used to use a threaded IRQ, but a patch switched to using a kthread_worker in order to set realtime priorities on the handler thread and for other optimisations. The end result is non-threaded IRQ that schedules some work then returns IRQ_HANDLED, making the kernel think that all IRQ processing has completed. The work-around to prevent a constant stream of interrupts is to mark the interrupt as edge-sensitive rather than level-sensitive, but interpreting an active-low source as a falling-edge source requires care to prevent a total cessation of interrupts. Whereas an edge-triggering source will generate a new edge for every interrupt condition a level-triggering source will keep the signal at the interrupting level until it no longer requires attention; in other words, the host won't see another edge until all interrupt conditions are cleared. It is therefore vital that the interrupt handler does not exit with an outstanding interrupt condition, otherwise the kernel will not receive another interrupt unless some other operation causes the interrupt state on the device to be cleared. The existing sc16is7xx driver has a very simple interrupt "thread" (kthread_work job) that processes interrupts on each channel in turn until there are no more. If both channels are active and the first channel starts interrupting while the handler for the second channel is running then it will not be detected and an IRQ stall ensues. This could be handled easily if there was a shared IRQ status register, or a convenient way to determine if the IRQ had been deasserted for any length of time, but both appear to be lacking. Avoid this problem (or at least make it much less likely to happen) by reducing the granularity of per-channel interrupt processing to one condition per iteration, only exiting the overall loop when both channels are no longer interrupting. Signed-off-by: Phil Elwell <phil@raspberrypi.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-09-12 15:31:55 +01:00
int i;
keep_polling = false;
sc16is7xx: Fix for multi-channel stall The SC16IS752 is a dual-channel device. The two channels are largely independent, but the IRQ signals are wired together as an open-drain, active low signal which will be driven low while either of the channels requires attention, which can be for significant periods of time until operations complete and the interrupt can be acknowledged. In that respect it is should be treated as a true level-sensitive IRQ. The kernel, however, needs to be able to exit interrupt context in order to use I2C or SPI to access the device registers (which may involve sleeping). Therefore the interrupt needs to be masked out or paused in some way. The usual way to manage sleeping from within an interrupt handler is to use a threaded interrupt handler - a regular interrupt routine does the minimum amount of work needed to triage the interrupt before waking the interrupt service thread. If the threaded IRQ is marked as IRQF_ONESHOT the kernel will automatically mask out the interrupt until the thread runs to completion. The sc16is7xx driver used to use a threaded IRQ, but a patch switched to using a kthread_worker in order to set realtime priorities on the handler thread and for other optimisations. The end result is non-threaded IRQ that schedules some work then returns IRQ_HANDLED, making the kernel think that all IRQ processing has completed. The work-around to prevent a constant stream of interrupts is to mark the interrupt as edge-sensitive rather than level-sensitive, but interpreting an active-low source as a falling-edge source requires care to prevent a total cessation of interrupts. Whereas an edge-triggering source will generate a new edge for every interrupt condition a level-triggering source will keep the signal at the interrupting level until it no longer requires attention; in other words, the host won't see another edge until all interrupt conditions are cleared. It is therefore vital that the interrupt handler does not exit with an outstanding interrupt condition, otherwise the kernel will not receive another interrupt unless some other operation causes the interrupt state on the device to be cleared. The existing sc16is7xx driver has a very simple interrupt "thread" (kthread_work job) that processes interrupts on each channel in turn until there are no more. If both channels are active and the first channel starts interrupting while the handler for the second channel is running then it will not be detected and an IRQ stall ensues. This could be handled easily if there was a shared IRQ status register, or a convenient way to determine if the IRQ had been deasserted for any length of time, but both appear to be lacking. Avoid this problem (or at least make it much less likely to happen) by reducing the granularity of per-channel interrupt processing to one condition per iteration, only exiting the overall loop when both channels are no longer interrupting. Signed-off-by: Phil Elwell <phil@raspberrypi.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-09-12 15:31:55 +01:00
for (i = 0; i < s->devtype->nr_uart; ++i)
keep_polling |= sc16is7xx_port_irq(s, i);
} while (keep_polling);
return IRQ_HANDLED;
}
static void sc16is7xx_poll_proc(struct kthread_work *ws)
{
struct sc16is7xx_port *s = container_of(ws, struct sc16is7xx_port, poll_work.work);
/* Reuse standard IRQ handler. Interrupt ID is unused in this context. */
sc16is7xx_irq(0, s);
/* Setup delay based on SC16IS7XX_POLL_PERIOD_MS */
kthread_queue_delayed_work(&s->kworker, &s->poll_work,
msecs_to_jiffies(SC16IS7XX_POLL_PERIOD_MS));
}
static void sc16is7xx_tx_proc(struct kthread_work *ws)
{
struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
if ((port->rs485.flags & SER_RS485_ENABLED) &&
(port->rs485.delay_rts_before_send > 0))
msleep(port->rs485.delay_rts_before_send);
mutex_lock(&one->efr_lock);
sc16is7xx_handle_tx(port);
mutex_unlock(&one->efr_lock);
}
static void sc16is7xx_reconf_rs485(struct uart_port *port)
{
const u32 mask = SC16IS7XX_EFCR_AUTO_RS485_BIT |
SC16IS7XX_EFCR_RTS_INVERT_BIT;
u32 efcr = 0;
struct serial_rs485 *rs485 = &port->rs485;
unsigned long irqflags;
uart_port_lock_irqsave(port, &irqflags);
if (rs485->flags & SER_RS485_ENABLED) {
efcr |= SC16IS7XX_EFCR_AUTO_RS485_BIT;
if (rs485->flags & SER_RS485_RTS_AFTER_SEND)
efcr |= SC16IS7XX_EFCR_RTS_INVERT_BIT;
}
uart_port_unlock_irqrestore(port, irqflags);
sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, mask, efcr);
}
static void sc16is7xx_reg_proc(struct kthread_work *ws)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(ws, reg_work);
struct sc16is7xx_one_config config;
unsigned long irqflags;
uart_port_lock_irqsave(&one->port, &irqflags);
config = one->config;
memset(&one->config, 0, sizeof(one->config));
uart_port_unlock_irqrestore(&one->port, irqflags);
if (config.flags & SC16IS7XX_RECONF_MD) {
u8 mcr = 0;
/* Device ignores RTS setting when hardware flow is enabled */
if (one->port.mctrl & TIOCM_RTS)
mcr |= SC16IS7XX_MCR_RTS_BIT;
if (one->port.mctrl & TIOCM_DTR)
mcr |= SC16IS7XX_MCR_DTR_BIT;
if (one->port.mctrl & TIOCM_LOOP)
mcr |= SC16IS7XX_MCR_LOOP_BIT;
sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG,
SC16IS7XX_MCR_RTS_BIT |
SC16IS7XX_MCR_DTR_BIT |
SC16IS7XX_MCR_LOOP_BIT,
mcr);
}
if (config.flags & SC16IS7XX_RECONF_IER)
sc16is7xx_port_update(&one->port, SC16IS7XX_IER_REG,
config.ier_mask, config.ier_val);
if (config.flags & SC16IS7XX_RECONF_RS485)
sc16is7xx_reconf_rs485(&one->port);
}
static void sc16is7xx_ms_proc(struct kthread_work *ws)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(ws, ms_work.work);
struct sc16is7xx_port *s = dev_get_drvdata(one->port.dev);
if (one->port.state) {
mutex_lock(&one->efr_lock);
sc16is7xx_update_mlines(one);
mutex_unlock(&one->efr_lock);
kthread_queue_delayed_work(&s->kworker, &one->ms_work, HZ);
}
}
static void sc16is7xx_enable_ms(struct uart_port *port)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
lockdep_assert_held_once(&port->lock);
kthread_queue_delayed_work(&s->kworker, &one->ms_work, 0);
}
static void sc16is7xx_start_tx(struct uart_port *port)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
kthread: kthread worker API cleanup A good practice is to prefix the names of functions by the name of the subsystem. The kthread worker API is a mix of classic kthreads and workqueues. Each worker has a dedicated kthread. It runs a generic function that process queued works. It is implemented as part of the kthread subsystem. This patch renames the existing kthread worker API to use the corresponding name from the workqueues API prefixed by kthread_: __init_kthread_worker() -> __kthread_init_worker() init_kthread_worker() -> kthread_init_worker() init_kthread_work() -> kthread_init_work() insert_kthread_work() -> kthread_insert_work() queue_kthread_work() -> kthread_queue_work() flush_kthread_work() -> kthread_flush_work() flush_kthread_worker() -> kthread_flush_worker() Note that the names of DEFINE_KTHREAD_WORK*() macros stay as they are. It is common that the "DEFINE_" prefix has precedence over the subsystem names. Note that INIT() macros and init() functions use different naming scheme. There is no good solution. There are several reasons for this solution: + "init" in the function names stands for the verb "initialize" aka "initialize worker". While "INIT" in the macro names stands for the noun "INITIALIZER" aka "worker initializer". + INIT() macros are used only in DEFINE() macros + init() functions are used close to the other kthread() functions. It looks much better if all the functions use the same scheme. + There will be also kthread_destroy_worker() that will be used close to kthread_cancel_work(). It is related to the init() function. Again it looks better if all functions use the same naming scheme. + there are several precedents for such init() function names, e.g. amd_iommu_init_device(), free_area_init_node(), jump_label_init_type(), regmap_init_mmio_clk(), + It is not an argument but it was inconsistent even before. [arnd@arndb.de: fix linux-next merge conflict] Link: http://lkml.kernel.org/r/20160908135724.1311726-1-arnd@arndb.de Link: http://lkml.kernel.org/r/1470754545-17632-3-git-send-email-pmladek@suse.com Suggested-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Petr Mladek <pmladek@suse.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Tejun Heo <tj@kernel.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com> Cc: Josh Triplett <josh@joshtriplett.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jiri Kosina <jkosina@suse.cz> Cc: Borislav Petkov <bp@suse.de> Cc: Michal Hocko <mhocko@suse.cz> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-10-11 13:55:20 -07:00
kthread_queue_work(&s->kworker, &one->tx_work);
}
static void sc16is7xx_throttle(struct uart_port *port)
{
unsigned long flags;
/*
* Hardware flow control is enabled and thus the device ignores RTS
* value set in MCR register. Stop reading data from RX FIFO so the
* AutoRTS feature will de-activate RTS output.
*/
uart_port_lock_irqsave(port, &flags);
sc16is7xx_ier_clear(port, SC16IS7XX_IER_RDI_BIT);
uart_port_unlock_irqrestore(port, flags);
}
static void sc16is7xx_unthrottle(struct uart_port *port)
{
unsigned long flags;
uart_port_lock_irqsave(port, &flags);
sc16is7xx_ier_set(port, SC16IS7XX_IER_RDI_BIT);
uart_port_unlock_irqrestore(port, flags);
}
static unsigned int sc16is7xx_tx_empty(struct uart_port *port)
{
unsigned int lsr;
lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG);
return (lsr & SC16IS7XX_LSR_TEMT_BIT) ? TIOCSER_TEMT : 0;
}
static unsigned int sc16is7xx_get_mctrl(struct uart_port *port)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
/* Called with port lock taken so we can only return cached value */
return one->old_mctrl;
}
static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
one->config.flags |= SC16IS7XX_RECONF_MD;
kthread: kthread worker API cleanup A good practice is to prefix the names of functions by the name of the subsystem. The kthread worker API is a mix of classic kthreads and workqueues. Each worker has a dedicated kthread. It runs a generic function that process queued works. It is implemented as part of the kthread subsystem. This patch renames the existing kthread worker API to use the corresponding name from the workqueues API prefixed by kthread_: __init_kthread_worker() -> __kthread_init_worker() init_kthread_worker() -> kthread_init_worker() init_kthread_work() -> kthread_init_work() insert_kthread_work() -> kthread_insert_work() queue_kthread_work() -> kthread_queue_work() flush_kthread_work() -> kthread_flush_work() flush_kthread_worker() -> kthread_flush_worker() Note that the names of DEFINE_KTHREAD_WORK*() macros stay as they are. It is common that the "DEFINE_" prefix has precedence over the subsystem names. Note that INIT() macros and init() functions use different naming scheme. There is no good solution. There are several reasons for this solution: + "init" in the function names stands for the verb "initialize" aka "initialize worker". While "INIT" in the macro names stands for the noun "INITIALIZER" aka "worker initializer". + INIT() macros are used only in DEFINE() macros + init() functions are used close to the other kthread() functions. It looks much better if all the functions use the same scheme. + There will be also kthread_destroy_worker() that will be used close to kthread_cancel_work(). It is related to the init() function. Again it looks better if all functions use the same naming scheme. + there are several precedents for such init() function names, e.g. amd_iommu_init_device(), free_area_init_node(), jump_label_init_type(), regmap_init_mmio_clk(), + It is not an argument but it was inconsistent even before. [arnd@arndb.de: fix linux-next merge conflict] Link: http://lkml.kernel.org/r/20160908135724.1311726-1-arnd@arndb.de Link: http://lkml.kernel.org/r/1470754545-17632-3-git-send-email-pmladek@suse.com Suggested-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Petr Mladek <pmladek@suse.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Tejun Heo <tj@kernel.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com> Cc: Josh Triplett <josh@joshtriplett.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jiri Kosina <jkosina@suse.cz> Cc: Borislav Petkov <bp@suse.de> Cc: Michal Hocko <mhocko@suse.cz> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-10-11 13:55:20 -07:00
kthread_queue_work(&s->kworker, &one->reg_work);
}
static void sc16is7xx_break_ctl(struct uart_port *port, int break_state)
{
sc16is7xx_port_update(port, SC16IS7XX_LCR_REG,
SC16IS7XX_LCR_TXBREAK_BIT,
break_state ? SC16IS7XX_LCR_TXBREAK_BIT : 0);
}
static void sc16is7xx_set_termios(struct uart_port *port,
struct ktermios *termios,
const struct ktermios *old)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
unsigned int lcr, flow = 0;
int baud;
unsigned long flags;
kthread_cancel_delayed_work_sync(&one->ms_work);
/* Mask termios capabilities we don't support */
termios->c_cflag &= ~CMSPAR;
/* Word size */
switch (termios->c_cflag & CSIZE) {
case CS5:
lcr = SC16IS7XX_LCR_WORD_LEN_5;
break;
case CS6:
lcr = SC16IS7XX_LCR_WORD_LEN_6;
break;
case CS7:
lcr = SC16IS7XX_LCR_WORD_LEN_7;
break;
case CS8:
lcr = SC16IS7XX_LCR_WORD_LEN_8;
break;
default:
lcr = SC16IS7XX_LCR_WORD_LEN_8;
termios->c_cflag &= ~CSIZE;
termios->c_cflag |= CS8;
break;
}
/* Parity */
if (termios->c_cflag & PARENB) {
lcr |= SC16IS7XX_LCR_PARITY_BIT;
if (!(termios->c_cflag & PARODD))
lcr |= SC16IS7XX_LCR_EVENPARITY_BIT;
}
/* Stop bits */
if (termios->c_cflag & CSTOPB)
lcr |= SC16IS7XX_LCR_STOPLEN_BIT; /* 2 stops */
/* Set read status mask */
port->read_status_mask = SC16IS7XX_LSR_OE_BIT;
if (termios->c_iflag & INPCK)
port->read_status_mask |= SC16IS7XX_LSR_PE_BIT |
SC16IS7XX_LSR_FE_BIT;
if (termios->c_iflag & (BRKINT | PARMRK))
port->read_status_mask |= SC16IS7XX_LSR_BI_BIT;
/* Set status ignore mask */
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNBRK)
port->ignore_status_mask |= SC16IS7XX_LSR_BI_BIT;
if (!(termios->c_cflag & CREAD))
port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK;
/* Configure flow control */
port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
if (termios->c_cflag & CRTSCTS) {
flow |= SC16IS7XX_EFR_AUTOCTS_BIT |
SC16IS7XX_EFR_AUTORTS_BIT;
port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
}
if (termios->c_iflag & IXON)
flow |= SC16IS7XX_EFR_SWFLOW3_BIT;
if (termios->c_iflag & IXOFF)
flow |= SC16IS7XX_EFR_SWFLOW1_BIT;
/* Update LCR register */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
/* Update EFR registers */
sc16is7xx_efr_lock(port);
sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]);
sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
SC16IS7XX_EFR_FLOWCTRL_BITS, flow);
sc16is7xx_efr_unlock(port);
/* Get baud rate generator configuration */
baud = uart_get_baud_rate(port, termios, old,
port->uartclk / 16 / 4 / 0xffff,
port->uartclk / 16);
/* Setup baudrate generator */
baud = sc16is7xx_set_baud(port, baud);
uart_port_lock_irqsave(port, &flags);
/* Update timeout according to new baud rate */
uart_update_timeout(port, termios->c_cflag, baud);
if (UART_ENABLE_MS(port, termios->c_cflag))
sc16is7xx_enable_ms(port);
uart_port_unlock_irqrestore(port, flags);
}
static int sc16is7xx_config_rs485(struct uart_port *port, struct ktermios *termios,
struct serial_rs485 *rs485)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
if (rs485->flags & SER_RS485_ENABLED) {
/*
* RTS signal is handled by HW, it's timing can't be influenced.
* However, it's sometimes useful to delay TX even without RTS
* control therefore we try to handle .delay_rts_before_send.
*/
if (rs485->delay_rts_after_send)
return -EINVAL;
}
one->config.flags |= SC16IS7XX_RECONF_RS485;
kthread: kthread worker API cleanup A good practice is to prefix the names of functions by the name of the subsystem. The kthread worker API is a mix of classic kthreads and workqueues. Each worker has a dedicated kthread. It runs a generic function that process queued works. It is implemented as part of the kthread subsystem. This patch renames the existing kthread worker API to use the corresponding name from the workqueues API prefixed by kthread_: __init_kthread_worker() -> __kthread_init_worker() init_kthread_worker() -> kthread_init_worker() init_kthread_work() -> kthread_init_work() insert_kthread_work() -> kthread_insert_work() queue_kthread_work() -> kthread_queue_work() flush_kthread_work() -> kthread_flush_work() flush_kthread_worker() -> kthread_flush_worker() Note that the names of DEFINE_KTHREAD_WORK*() macros stay as they are. It is common that the "DEFINE_" prefix has precedence over the subsystem names. Note that INIT() macros and init() functions use different naming scheme. There is no good solution. There are several reasons for this solution: + "init" in the function names stands for the verb "initialize" aka "initialize worker". While "INIT" in the macro names stands for the noun "INITIALIZER" aka "worker initializer". + INIT() macros are used only in DEFINE() macros + init() functions are used close to the other kthread() functions. It looks much better if all the functions use the same scheme. + There will be also kthread_destroy_worker() that will be used close to kthread_cancel_work(). It is related to the init() function. Again it looks better if all functions use the same naming scheme. + there are several precedents for such init() function names, e.g. amd_iommu_init_device(), free_area_init_node(), jump_label_init_type(), regmap_init_mmio_clk(), + It is not an argument but it was inconsistent even before. [arnd@arndb.de: fix linux-next merge conflict] Link: http://lkml.kernel.org/r/20160908135724.1311726-1-arnd@arndb.de Link: http://lkml.kernel.org/r/1470754545-17632-3-git-send-email-pmladek@suse.com Suggested-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Petr Mladek <pmladek@suse.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Tejun Heo <tj@kernel.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com> Cc: Josh Triplett <josh@joshtriplett.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jiri Kosina <jkosina@suse.cz> Cc: Borislav Petkov <bp@suse.de> Cc: Michal Hocko <mhocko@suse.cz> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-10-11 13:55:20 -07:00
kthread_queue_work(&s->kworker, &one->reg_work);
return 0;
}
static int sc16is7xx_startup(struct uart_port *port)
{
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
unsigned int val;
unsigned long flags;
sc16is7xx_power(port, 1);
/* Reset FIFOs*/
val = SC16IS7XX_FCR_RXRESET_BIT | SC16IS7XX_FCR_TXRESET_BIT;
sc16is7xx_port_write(port, SC16IS7XX_FCR_REG, val);
udelay(5);
sc16is7xx_port_write(port, SC16IS7XX_FCR_REG,
SC16IS7XX_FCR_FIFO_BIT);
/* Enable EFR */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
SC16IS7XX_LCR_CONF_MODE_B);
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
regcache_cache_bypass(one->regmap, true);
/* Enable write access to enhanced features and internal clock div */
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
SC16IS7XX_EFR_ENABLE_BIT,
SC16IS7XX_EFR_ENABLE_BIT);
/* Enable TCR/TLR */
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
SC16IS7XX_MCR_TCRTLR_BIT,
SC16IS7XX_MCR_TCRTLR_BIT);
/* Configure flow control levels */
/* Flow control halt level 48, resume level 24 */
sc16is7xx_port_write(port, SC16IS7XX_TCR_REG,
SC16IS7XX_TCR_RX_RESUME(24) |
SC16IS7XX_TCR_RX_HALT(48));
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
regcache_cache_bypass(one->regmap, false);
/* Now, initialize the UART */
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_WORD_LEN_8);
/* Enable IrDA mode if requested in DT */
/* This bit must be written with LCR[7] = 0 */
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
SC16IS7XX_MCR_IRDA_BIT,
one->irda_mode ?
SC16IS7XX_MCR_IRDA_BIT : 0);
/* Enable the Rx and Tx FIFO */
sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG,
SC16IS7XX_EFCR_RXDISABLE_BIT |
SC16IS7XX_EFCR_TXDISABLE_BIT,
0);
/* Enable RX, CTS change and modem lines interrupts */
val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_CTSI_BIT |
SC16IS7XX_IER_MSI_BIT;
sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val);
/* Enable modem status polling */
uart_port_lock_irqsave(port, &flags);
sc16is7xx_enable_ms(port);
uart_port_unlock_irqrestore(port, flags);
if (s->polling)
kthread_queue_delayed_work(&s->kworker, &s->poll_work,
msecs_to_jiffies(SC16IS7XX_POLL_PERIOD_MS));
return 0;
}
static void sc16is7xx_shutdown(struct uart_port *port)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
kthread_cancel_delayed_work_sync(&one->ms_work);
/* Disable all interrupts */
sc16is7xx_port_write(port, SC16IS7XX_IER_REG, 0);
/* Disable TX/RX */
sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG,
SC16IS7XX_EFCR_RXDISABLE_BIT |
SC16IS7XX_EFCR_TXDISABLE_BIT,
SC16IS7XX_EFCR_RXDISABLE_BIT |
SC16IS7XX_EFCR_TXDISABLE_BIT);
sc16is7xx_power(port, 0);
if (s->polling)
kthread_cancel_delayed_work_sync(&s->poll_work);
kthread: kthread worker API cleanup A good practice is to prefix the names of functions by the name of the subsystem. The kthread worker API is a mix of classic kthreads and workqueues. Each worker has a dedicated kthread. It runs a generic function that process queued works. It is implemented as part of the kthread subsystem. This patch renames the existing kthread worker API to use the corresponding name from the workqueues API prefixed by kthread_: __init_kthread_worker() -> __kthread_init_worker() init_kthread_worker() -> kthread_init_worker() init_kthread_work() -> kthread_init_work() insert_kthread_work() -> kthread_insert_work() queue_kthread_work() -> kthread_queue_work() flush_kthread_work() -> kthread_flush_work() flush_kthread_worker() -> kthread_flush_worker() Note that the names of DEFINE_KTHREAD_WORK*() macros stay as they are. It is common that the "DEFINE_" prefix has precedence over the subsystem names. Note that INIT() macros and init() functions use different naming scheme. There is no good solution. There are several reasons for this solution: + "init" in the function names stands for the verb "initialize" aka "initialize worker". While "INIT" in the macro names stands for the noun "INITIALIZER" aka "worker initializer". + INIT() macros are used only in DEFINE() macros + init() functions are used close to the other kthread() functions. It looks much better if all the functions use the same scheme. + There will be also kthread_destroy_worker() that will be used close to kthread_cancel_work(). It is related to the init() function. Again it looks better if all functions use the same naming scheme. + there are several precedents for such init() function names, e.g. amd_iommu_init_device(), free_area_init_node(), jump_label_init_type(), regmap_init_mmio_clk(), + It is not an argument but it was inconsistent even before. [arnd@arndb.de: fix linux-next merge conflict] Link: http://lkml.kernel.org/r/20160908135724.1311726-1-arnd@arndb.de Link: http://lkml.kernel.org/r/1470754545-17632-3-git-send-email-pmladek@suse.com Suggested-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Petr Mladek <pmladek@suse.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Tejun Heo <tj@kernel.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com> Cc: Josh Triplett <josh@joshtriplett.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jiri Kosina <jkosina@suse.cz> Cc: Borislav Petkov <bp@suse.de> Cc: Michal Hocko <mhocko@suse.cz> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-10-11 13:55:20 -07:00
kthread_flush_worker(&s->kworker);
}
static const char *sc16is7xx_type(struct uart_port *port)
{
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
return (port->type == PORT_SC16IS7XX) ? s->devtype->name : NULL;
}
static int sc16is7xx_request_port(struct uart_port *port)
{
/* Do nothing */
return 0;
}
static void sc16is7xx_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE)
port->type = PORT_SC16IS7XX;
}
static int sc16is7xx_verify_port(struct uart_port *port,
struct serial_struct *s)
{
if ((s->type != PORT_UNKNOWN) && (s->type != PORT_SC16IS7XX))
return -EINVAL;
if (s->irq != port->irq)
return -EINVAL;
return 0;
}
static void sc16is7xx_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
sc16is7xx_power(port, (state == UART_PM_STATE_ON) ? 1 : 0);
}
static void sc16is7xx_null_void(struct uart_port *port)
{
/* Do nothing */
}
static const struct uart_ops sc16is7xx_ops = {
.tx_empty = sc16is7xx_tx_empty,
.set_mctrl = sc16is7xx_set_mctrl,
.get_mctrl = sc16is7xx_get_mctrl,
.stop_tx = sc16is7xx_stop_tx,
.start_tx = sc16is7xx_start_tx,
.throttle = sc16is7xx_throttle,
.unthrottle = sc16is7xx_unthrottle,
.stop_rx = sc16is7xx_stop_rx,
.enable_ms = sc16is7xx_enable_ms,
.break_ctl = sc16is7xx_break_ctl,
.startup = sc16is7xx_startup,
.shutdown = sc16is7xx_shutdown,
.set_termios = sc16is7xx_set_termios,
.type = sc16is7xx_type,
.request_port = sc16is7xx_request_port,
.release_port = sc16is7xx_null_void,
.config_port = sc16is7xx_config_port,
.verify_port = sc16is7xx_verify_port,
.pm = sc16is7xx_pm,
};
#ifdef CONFIG_GPIOLIB
static int sc16is7xx_gpio_get(struct gpio_chip *chip, unsigned offset)
{
unsigned int val;
struct sc16is7xx_port *s = gpiochip_get_data(chip);
struct uart_port *port = &s->p[0].port;
val = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG);
return !!(val & BIT(offset));
}
static int sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned int offset,
int val)
{
struct sc16is7xx_port *s = gpiochip_get_data(chip);
struct uart_port *port = &s->p[0].port;
sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset),
val ? BIT(offset) : 0);
return 0;
}
static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip,
unsigned offset)
{
struct sc16is7xx_port *s = gpiochip_get_data(chip);
struct uart_port *port = &s->p[0].port;
sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset), 0);
return 0;
}
static int sc16is7xx_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int val)
{
struct sc16is7xx_port *s = gpiochip_get_data(chip);
struct uart_port *port = &s->p[0].port;
u8 state = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG);
if (val)
state |= BIT(offset);
else
state &= ~BIT(offset);
/*
* If we write IOSTATE first, and then IODIR, the output value is not
* transferred to the corresponding I/O pin.
* The datasheet states that each register bit will be transferred to
* the corresponding I/O pin programmed as output when writing to
* IOSTATE. Therefore, configure direction first with IODIR, and then
* set value after with IOSTATE.
*/
sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset),
BIT(offset));
sc16is7xx_port_write(port, SC16IS7XX_IOSTATE_REG, state);
return 0;
}
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
static int sc16is7xx_gpio_init_valid_mask(struct gpio_chip *chip,
unsigned long *valid_mask,
unsigned int ngpios)
{
struct sc16is7xx_port *s = gpiochip_get_data(chip);
*valid_mask = s->gpio_valid_mask;
return 0;
}
static int sc16is7xx_setup_gpio_chip(struct sc16is7xx_port *s)
{
struct device *dev = s->p[0].port.dev;
if (!s->devtype->nr_gpio)
return 0;
switch (s->mctrl_mask) {
case 0:
s->gpio_valid_mask = GENMASK(7, 0);
break;
case SC16IS7XX_IOCONTROL_MODEM_A_BIT:
s->gpio_valid_mask = GENMASK(3, 0);
break;
case SC16IS7XX_IOCONTROL_MODEM_B_BIT:
s->gpio_valid_mask = GENMASK(7, 4);
break;
default:
break;
}
if (s->gpio_valid_mask == 0)
return 0;
s->gpio.owner = THIS_MODULE;
s->gpio.parent = dev;
s->gpio.label = dev_name(dev);
s->gpio.init_valid_mask = sc16is7xx_gpio_init_valid_mask;
s->gpio.direction_input = sc16is7xx_gpio_direction_input;
s->gpio.get = sc16is7xx_gpio_get;
s->gpio.direction_output = sc16is7xx_gpio_direction_output;
s->gpio.set_rv = sc16is7xx_gpio_set;
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
s->gpio.base = -1;
s->gpio.ngpio = s->devtype->nr_gpio;
s->gpio.can_sleep = 1;
return gpiochip_add_data(&s->gpio, s);
}
#endif
static void sc16is7xx_setup_irda_ports(struct sc16is7xx_port *s)
{
int i;
int ret;
int count;
u32 irda_port[SC16IS7XX_MAX_PORTS];
struct device *dev = s->p[0].port.dev;
count = device_property_count_u32(dev, "irda-mode-ports");
if (count < 0 || count > ARRAY_SIZE(irda_port))
return;
ret = device_property_read_u32_array(dev, "irda-mode-ports",
irda_port, count);
if (ret)
return;
for (i = 0; i < count; i++) {
if (irda_port[i] < s->devtype->nr_uart)
s->p[irda_port[i]].irda_mode = true;
}
}
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
/*
* Configure ports designated to operate as modem control lines.
*/
static int sc16is7xx_setup_mctrl_ports(struct sc16is7xx_port *s,
struct regmap *regmap)
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
{
int i;
int ret;
int count;
u32 mctrl_port[SC16IS7XX_MAX_PORTS];
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
struct device *dev = s->p[0].port.dev;
count = device_property_count_u32(dev, "nxp,modem-control-line-ports");
if (count < 0 || count > ARRAY_SIZE(mctrl_port))
return 0;
ret = device_property_read_u32_array(dev, "nxp,modem-control-line-ports",
mctrl_port, count);
if (ret)
return ret;
s->mctrl_mask = 0;
for (i = 0; i < count; i++) {
/* Use GPIO lines as modem control lines */
if (mctrl_port[i] == 0)
s->mctrl_mask |= SC16IS7XX_IOCONTROL_MODEM_A_BIT;
else if (mctrl_port[i] == 1)
s->mctrl_mask |= SC16IS7XX_IOCONTROL_MODEM_B_BIT;
}
if (s->mctrl_mask)
regmap_update_bits(
regmap,
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
SC16IS7XX_IOCONTROL_REG,
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
SC16IS7XX_IOCONTROL_MODEM_A_BIT |
SC16IS7XX_IOCONTROL_MODEM_B_BIT, s->mctrl_mask);
return 0;
}
static const struct serial_rs485 sc16is7xx_rs485_supported = {
.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND,
.delay_rts_before_send = 1,
.delay_rts_after_send = 1, /* Not supported but keep returning -EINVAL */
};
/* Reset device, purging any pending irq / data */
static int sc16is7xx_reset(struct device *dev, struct regmap *regmap)
{
struct gpio_desc *reset_gpio;
/* Assert reset GPIO if defined and valid. */
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset_gpio))
return dev_err_probe(dev, PTR_ERR(reset_gpio), "Failed to get reset GPIO\n");
if (reset_gpio) {
/* The minimum reset pulse width is 3 us. */
fsleep(5);
gpiod_set_value_cansleep(reset_gpio, 0); /* Deassert GPIO */
} else {
/* Software reset */
regmap_write(regmap, SC16IS7XX_IOCONTROL_REG,
SC16IS7XX_IOCONTROL_SRESET_BIT);
}
return 0;
}
int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype,
struct regmap *regmaps[], int irq)
{
unsigned long freq = 0, *pfreq = dev_get_platdata(dev);
unsigned int val;
u32 uartclk = 0;
int i, ret;
struct sc16is7xx_port *s;
bool port_registered[SC16IS7XX_MAX_PORTS];
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
for (i = 0; i < devtype->nr_uart; i++)
if (IS_ERR(regmaps[i]))
return PTR_ERR(regmaps[i]);
/*
* This device does not have an identification register that would
* tell us if we are really connected to the correct device.
* The best we can do is to check if communication is at all possible.
*
* Note: regmap[0] is used in the probe function to access registers
* common to all channels/ports, as it is guaranteed to be present on
* all variants.
*/
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
ret = regmap_read(regmaps[0], SC16IS7XX_LSR_REG, &val);
if (ret < 0)
return -EPROBE_DEFER;
/* Alloc port structure */
s = devm_kzalloc(dev, struct_size(s, p, devtype->nr_uart), GFP_KERNEL);
if (!s) {
dev_err(dev, "Error allocating port structure\n");
return -ENOMEM;
}
/* Always ask for fixed clock rate from a property. */
device_property_read_u32(dev, "clock-frequency", &uartclk);
s->polling = (irq <= 0);
if (s->polling)
dev_dbg(dev,
"No interrupt pin definition, falling back to polling mode\n");
s->clk = devm_clk_get_optional(dev, NULL);
if (IS_ERR(s->clk))
return PTR_ERR(s->clk);
ret = clk_prepare_enable(s->clk);
if (ret)
return ret;
freq = clk_get_rate(s->clk);
if (freq == 0) {
if (uartclk)
freq = uartclk;
if (pfreq)
freq = *pfreq;
if (freq)
dev_dbg(dev, "Clock frequency: %luHz\n", freq);
else
return -EINVAL;
}
s->devtype = devtype;
dev_set_drvdata(dev, s);
kthread: kthread worker API cleanup A good practice is to prefix the names of functions by the name of the subsystem. The kthread worker API is a mix of classic kthreads and workqueues. Each worker has a dedicated kthread. It runs a generic function that process queued works. It is implemented as part of the kthread subsystem. This patch renames the existing kthread worker API to use the corresponding name from the workqueues API prefixed by kthread_: __init_kthread_worker() -> __kthread_init_worker() init_kthread_worker() -> kthread_init_worker() init_kthread_work() -> kthread_init_work() insert_kthread_work() -> kthread_insert_work() queue_kthread_work() -> kthread_queue_work() flush_kthread_work() -> kthread_flush_work() flush_kthread_worker() -> kthread_flush_worker() Note that the names of DEFINE_KTHREAD_WORK*() macros stay as they are. It is common that the "DEFINE_" prefix has precedence over the subsystem names. Note that INIT() macros and init() functions use different naming scheme. There is no good solution. There are several reasons for this solution: + "init" in the function names stands for the verb "initialize" aka "initialize worker". While "INIT" in the macro names stands for the noun "INITIALIZER" aka "worker initializer". + INIT() macros are used only in DEFINE() macros + init() functions are used close to the other kthread() functions. It looks much better if all the functions use the same scheme. + There will be also kthread_destroy_worker() that will be used close to kthread_cancel_work(). It is related to the init() function. Again it looks better if all functions use the same naming scheme. + there are several precedents for such init() function names, e.g. amd_iommu_init_device(), free_area_init_node(), jump_label_init_type(), regmap_init_mmio_clk(), + It is not an argument but it was inconsistent even before. [arnd@arndb.de: fix linux-next merge conflict] Link: http://lkml.kernel.org/r/20160908135724.1311726-1-arnd@arndb.de Link: http://lkml.kernel.org/r/1470754545-17632-3-git-send-email-pmladek@suse.com Suggested-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Petr Mladek <pmladek@suse.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Tejun Heo <tj@kernel.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com> Cc: Josh Triplett <josh@joshtriplett.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jiri Kosina <jkosina@suse.cz> Cc: Borislav Petkov <bp@suse.de> Cc: Michal Hocko <mhocko@suse.cz> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-10-11 13:55:20 -07:00
kthread_init_worker(&s->kworker);
s->kworker_task = kthread_run(kthread_worker_fn, &s->kworker,
"sc16is7xx");
if (IS_ERR(s->kworker_task)) {
ret = PTR_ERR(s->kworker_task);
goto out_clk;
}
sched_set_fifo(s->kworker_task);
ret = sc16is7xx_reset(dev, regmaps[0]);
if (ret)
goto out_kthread;
/* Mark each port line and status as uninitialised. */
for (i = 0; i < devtype->nr_uart; ++i) {
s->p[i].port.line = SC16IS7XX_MAX_DEVS;
port_registered[i] = false;
}
for (i = 0; i < devtype->nr_uart; ++i) {
ret = ida_alloc_max(&sc16is7xx_lines,
SC16IS7XX_MAX_DEVS - 1, GFP_KERNEL);
if (ret < 0)
goto out_ports;
s->p[i].port.line = ret;
/* Initialize port data */
s->p[i].port.dev = dev;
s->p[i].port.irq = irq;
s->p[i].port.type = PORT_SC16IS7XX;
s->p[i].port.fifosize = SC16IS7XX_FIFO_SIZE;
s->p[i].port.flags = UPF_FIXED_TYPE | UPF_LOW_LATENCY;
s->p[i].port.iobase = i;
/*
* Use all ones as membase to make sure uart_configure_port() in
* serial_core.c does not abort for SPI/I2C devices where the
* membase address is not applicable.
*/
s->p[i].port.membase = (void __iomem *)~0;
s->p[i].port.iotype = UPIO_PORT;
s->p[i].port.uartclk = freq;
s->p[i].port.rs485_config = sc16is7xx_config_rs485;
s->p[i].port.rs485_supported = sc16is7xx_rs485_supported;
s->p[i].port.ops = &sc16is7xx_ops;
s->p[i].old_mctrl = 0;
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
s->p[i].regmap = regmaps[i];
mutex_init(&s->p[i].efr_lock);
ret = uart_get_rs485_mode(&s->p[i].port);
if (ret)
goto out_ports;
/* Disable all interrupts */
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_IER_REG, 0);
/* Disable TX/RX */
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFCR_REG,
SC16IS7XX_EFCR_RXDISABLE_BIT |
SC16IS7XX_EFCR_TXDISABLE_BIT);
/* Initialize kthread work structs */
kthread: kthread worker API cleanup A good practice is to prefix the names of functions by the name of the subsystem. The kthread worker API is a mix of classic kthreads and workqueues. Each worker has a dedicated kthread. It runs a generic function that process queued works. It is implemented as part of the kthread subsystem. This patch renames the existing kthread worker API to use the corresponding name from the workqueues API prefixed by kthread_: __init_kthread_worker() -> __kthread_init_worker() init_kthread_worker() -> kthread_init_worker() init_kthread_work() -> kthread_init_work() insert_kthread_work() -> kthread_insert_work() queue_kthread_work() -> kthread_queue_work() flush_kthread_work() -> kthread_flush_work() flush_kthread_worker() -> kthread_flush_worker() Note that the names of DEFINE_KTHREAD_WORK*() macros stay as they are. It is common that the "DEFINE_" prefix has precedence over the subsystem names. Note that INIT() macros and init() functions use different naming scheme. There is no good solution. There are several reasons for this solution: + "init" in the function names stands for the verb "initialize" aka "initialize worker". While "INIT" in the macro names stands for the noun "INITIALIZER" aka "worker initializer". + INIT() macros are used only in DEFINE() macros + init() functions are used close to the other kthread() functions. It looks much better if all the functions use the same scheme. + There will be also kthread_destroy_worker() that will be used close to kthread_cancel_work(). It is related to the init() function. Again it looks better if all functions use the same naming scheme. + there are several precedents for such init() function names, e.g. amd_iommu_init_device(), free_area_init_node(), jump_label_init_type(), regmap_init_mmio_clk(), + It is not an argument but it was inconsistent even before. [arnd@arndb.de: fix linux-next merge conflict] Link: http://lkml.kernel.org/r/20160908135724.1311726-1-arnd@arndb.de Link: http://lkml.kernel.org/r/1470754545-17632-3-git-send-email-pmladek@suse.com Suggested-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Petr Mladek <pmladek@suse.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Tejun Heo <tj@kernel.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com> Cc: Josh Triplett <josh@joshtriplett.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jiri Kosina <jkosina@suse.cz> Cc: Borislav Petkov <bp@suse.de> Cc: Michal Hocko <mhocko@suse.cz> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-10-11 13:55:20 -07:00
kthread_init_work(&s->p[i].tx_work, sc16is7xx_tx_proc);
kthread_init_work(&s->p[i].reg_work, sc16is7xx_reg_proc);
kthread_init_delayed_work(&s->p[i].ms_work, sc16is7xx_ms_proc);
/* Register port */
ret = uart_add_one_port(&sc16is7xx_uart, &s->p[i].port);
if (ret)
goto out_ports;
port_registered[i] = true;
/* Enable EFR */
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG,
SC16IS7XX_LCR_CONF_MODE_B);
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
regcache_cache_bypass(regmaps[i], true);
/* Enable write access to enhanced features */
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_EFR_REG,
SC16IS7XX_EFR_ENABLE_BIT);
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
regcache_cache_bypass(regmaps[i], false);
/* Restore access to general registers */
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, 0x00);
/* Go to suspend mode */
sc16is7xx_power(&s->p[i].port, 0);
}
sc16is7xx_setup_irda_ports(s);
ret = sc16is7xx_setup_mctrl_ports(s, regmaps[0]);
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
if (ret)
goto out_ports;
serial: sc16is7xx: setup GPIO controller later in probe The GPIO controller component of the sc16is7xx driver is setup too early, which can result in a race condition where another device tries to utilise the GPIO lines before the sc16is7xx device has finished initialising. This issue manifests itself as an Oops when the GPIO lines are configured: Unable to handle kernel read from unreadable memory at virtual address ... pc : sc16is7xx_gpio_direction_output+0x68/0x108 [sc16is7xx] lr : sc16is7xx_gpio_direction_output+0x4c/0x108 [sc16is7xx] ... Call trace: sc16is7xx_gpio_direction_output+0x68/0x108 [sc16is7xx] gpiod_direction_output_raw_commit+0x64/0x318 gpiod_direction_output+0xb0/0x170 create_gpio_led+0xec/0x198 gpio_led_probe+0x16c/0x4f0 platform_drv_probe+0x5c/0xb0 really_probe+0xe8/0x448 driver_probe_device+0xe8/0x138 __device_attach_driver+0x94/0x118 bus_for_each_drv+0x8c/0xe0 __device_attach+0x100/0x1b8 device_initial_probe+0x28/0x38 bus_probe_device+0xa4/0xb0 deferred_probe_work_func+0x90/0xe0 process_one_work+0x1c4/0x480 worker_thread+0x54/0x430 kthread+0x138/0x150 ret_from_fork+0x10/0x1c This patch moves the setup of the GPIO controller functions to later in the probe function, ensuring the sc16is7xx device has already finished initialising by the time other devices try to make use of the GPIO lines. The error handling has also been reordered to reflect the new initialisation order. Co-developed-by: Wen-chien Jesse Sung <jesse.sung@canonical.com> Signed-off-by: Wen-chien Jesse Sung <jesse.sung@canonical.com> Signed-off-by: Isaac True <isaac.true@canonical.com> Link: https://lore.kernel.org/r/20221130105529.698385-1-isaac.true@canonical.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-11-30 11:55:30 +01:00
#ifdef CONFIG_GPIOLIB
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
ret = sc16is7xx_setup_gpio_chip(s);
if (ret)
goto out_ports;
serial: sc16is7xx: setup GPIO controller later in probe The GPIO controller component of the sc16is7xx driver is setup too early, which can result in a race condition where another device tries to utilise the GPIO lines before the sc16is7xx device has finished initialising. This issue manifests itself as an Oops when the GPIO lines are configured: Unable to handle kernel read from unreadable memory at virtual address ... pc : sc16is7xx_gpio_direction_output+0x68/0x108 [sc16is7xx] lr : sc16is7xx_gpio_direction_output+0x4c/0x108 [sc16is7xx] ... Call trace: sc16is7xx_gpio_direction_output+0x68/0x108 [sc16is7xx] gpiod_direction_output_raw_commit+0x64/0x318 gpiod_direction_output+0xb0/0x170 create_gpio_led+0xec/0x198 gpio_led_probe+0x16c/0x4f0 platform_drv_probe+0x5c/0xb0 really_probe+0xe8/0x448 driver_probe_device+0xe8/0x138 __device_attach_driver+0x94/0x118 bus_for_each_drv+0x8c/0xe0 __device_attach+0x100/0x1b8 device_initial_probe+0x28/0x38 bus_probe_device+0xa4/0xb0 deferred_probe_work_func+0x90/0xe0 process_one_work+0x1c4/0x480 worker_thread+0x54/0x430 kthread+0x138/0x150 ret_from_fork+0x10/0x1c This patch moves the setup of the GPIO controller functions to later in the probe function, ensuring the sc16is7xx device has already finished initialising by the time other devices try to make use of the GPIO lines. The error handling has also been reordered to reflect the new initialisation order. Co-developed-by: Wen-chien Jesse Sung <jesse.sung@canonical.com> Signed-off-by: Wen-chien Jesse Sung <jesse.sung@canonical.com> Signed-off-by: Isaac True <isaac.true@canonical.com> Link: https://lore.kernel.org/r/20221130105529.698385-1-isaac.true@canonical.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-11-30 11:55:30 +01:00
#endif
if (s->polling) {
/* Initialize kernel thread for polling */
kthread_init_delayed_work(&s->poll_work, sc16is7xx_poll_proc);
return 0;
}
/*
* Setup interrupt. We first try to acquire the IRQ line as level IRQ.
* If that succeeds, we can allow sharing the interrupt as well.
* In case the interrupt controller doesn't support that, we fall
* back to a non-shared falling-edge trigger.
*/
ret = devm_request_threaded_irq(dev, irq, NULL, sc16is7xx_irq,
IRQF_TRIGGER_LOW | IRQF_SHARED |
IRQF_ONESHOT,
dev_name(dev), s);
if (!ret)
return 0;
ret = devm_request_threaded_irq(dev, irq, NULL, sc16is7xx_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
dev_name(dev), s);
if (!ret)
return 0;
#ifdef CONFIG_GPIOLIB
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
if (s->gpio_valid_mask)
gpiochip_remove(&s->gpio);
#endif
serial: sc16is7xx: setup GPIO controller later in probe The GPIO controller component of the sc16is7xx driver is setup too early, which can result in a race condition where another device tries to utilise the GPIO lines before the sc16is7xx device has finished initialising. This issue manifests itself as an Oops when the GPIO lines are configured: Unable to handle kernel read from unreadable memory at virtual address ... pc : sc16is7xx_gpio_direction_output+0x68/0x108 [sc16is7xx] lr : sc16is7xx_gpio_direction_output+0x4c/0x108 [sc16is7xx] ... Call trace: sc16is7xx_gpio_direction_output+0x68/0x108 [sc16is7xx] gpiod_direction_output_raw_commit+0x64/0x318 gpiod_direction_output+0xb0/0x170 create_gpio_led+0xec/0x198 gpio_led_probe+0x16c/0x4f0 platform_drv_probe+0x5c/0xb0 really_probe+0xe8/0x448 driver_probe_device+0xe8/0x138 __device_attach_driver+0x94/0x118 bus_for_each_drv+0x8c/0xe0 __device_attach+0x100/0x1b8 device_initial_probe+0x28/0x38 bus_probe_device+0xa4/0xb0 deferred_probe_work_func+0x90/0xe0 process_one_work+0x1c4/0x480 worker_thread+0x54/0x430 kthread+0x138/0x150 ret_from_fork+0x10/0x1c This patch moves the setup of the GPIO controller functions to later in the probe function, ensuring the sc16is7xx device has already finished initialising by the time other devices try to make use of the GPIO lines. The error handling has also been reordered to reflect the new initialisation order. Co-developed-by: Wen-chien Jesse Sung <jesse.sung@canonical.com> Signed-off-by: Wen-chien Jesse Sung <jesse.sung@canonical.com> Signed-off-by: Isaac True <isaac.true@canonical.com> Link: https://lore.kernel.org/r/20221130105529.698385-1-isaac.true@canonical.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-11-30 11:55:30 +01:00
out_ports:
for (i = 0; i < devtype->nr_uart; i++) {
if (s->p[i].port.line < SC16IS7XX_MAX_DEVS)
ida_free(&sc16is7xx_lines, s->p[i].port.line);
if (port_registered[i])
uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
}
serial: sc16is7xx: setup GPIO controller later in probe The GPIO controller component of the sc16is7xx driver is setup too early, which can result in a race condition where another device tries to utilise the GPIO lines before the sc16is7xx device has finished initialising. This issue manifests itself as an Oops when the GPIO lines are configured: Unable to handle kernel read from unreadable memory at virtual address ... pc : sc16is7xx_gpio_direction_output+0x68/0x108 [sc16is7xx] lr : sc16is7xx_gpio_direction_output+0x4c/0x108 [sc16is7xx] ... Call trace: sc16is7xx_gpio_direction_output+0x68/0x108 [sc16is7xx] gpiod_direction_output_raw_commit+0x64/0x318 gpiod_direction_output+0xb0/0x170 create_gpio_led+0xec/0x198 gpio_led_probe+0x16c/0x4f0 platform_drv_probe+0x5c/0xb0 really_probe+0xe8/0x448 driver_probe_device+0xe8/0x138 __device_attach_driver+0x94/0x118 bus_for_each_drv+0x8c/0xe0 __device_attach+0x100/0x1b8 device_initial_probe+0x28/0x38 bus_probe_device+0xa4/0xb0 deferred_probe_work_func+0x90/0xe0 process_one_work+0x1c4/0x480 worker_thread+0x54/0x430 kthread+0x138/0x150 ret_from_fork+0x10/0x1c This patch moves the setup of the GPIO controller functions to later in the probe function, ensuring the sc16is7xx device has already finished initialising by the time other devices try to make use of the GPIO lines. The error handling has also been reordered to reflect the new initialisation order. Co-developed-by: Wen-chien Jesse Sung <jesse.sung@canonical.com> Signed-off-by: Wen-chien Jesse Sung <jesse.sung@canonical.com> Signed-off-by: Isaac True <isaac.true@canonical.com> Link: https://lore.kernel.org/r/20221130105529.698385-1-isaac.true@canonical.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2022-11-30 11:55:30 +01:00
out_kthread:
kthread_stop(s->kworker_task);
out_clk:
clk_disable_unprepare(s->clk);
return ret;
}
EXPORT_SYMBOL_GPL(sc16is7xx_probe);
void sc16is7xx_remove(struct device *dev)
{
struct sc16is7xx_port *s = dev_get_drvdata(dev);
int i;
#ifdef CONFIG_GPIOLIB
serial: sc16is7xx: fix regression with GPIO configuration Commit 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") and commit 21144bab4f11 ("sc16is7xx: Handle modem status lines") changed the function of the GPIOs pins to act as modem control lines without any possibility of selecting GPIO function. As a consequence, applications that depends on GPIO lines configured by default as GPIO pins no longer work as expected. Also, the change to select modem control lines function was done only for channel A of dual UART variants (752/762). This was not documented in the log message. Allow to specify GPIO or modem control line function in the device tree, and for each of the ports (A or B). Do so by using the new device-tree property named "nxp,modem-control-line-ports" (property added in separate patch). When registering GPIO chip controller, mask-out GPIO pins declared as modem control lines according to this new DT property. Fixes: 679875d1d880 ("sc16is7xx: Separate GPIOs from modem control lines") Fixes: 21144bab4f11 ("sc16is7xx: Handle modem status lines") Cc: stable@vger.kernel.org Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Lech Perczak <lech.perczak@camlingroup.com> Tested-by: Lech Perczak <lech.perczak@camlingroup.com> Acked-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230807214556.540627-5-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-08-07 17:45:54 -04:00
if (s->gpio_valid_mask)
gpiochip_remove(&s->gpio);
#endif
for (i = 0; i < s->devtype->nr_uart; i++) {
kthread_cancel_delayed_work_sync(&s->p[i].ms_work);
ida_free(&sc16is7xx_lines, s->p[i].port.line);
uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
sc16is7xx_power(&s->p[i].port, 0);
}
if (s->polling)
kthread_cancel_delayed_work_sync(&s->poll_work);
kthread: kthread worker API cleanup A good practice is to prefix the names of functions by the name of the subsystem. The kthread worker API is a mix of classic kthreads and workqueues. Each worker has a dedicated kthread. It runs a generic function that process queued works. It is implemented as part of the kthread subsystem. This patch renames the existing kthread worker API to use the corresponding name from the workqueues API prefixed by kthread_: __init_kthread_worker() -> __kthread_init_worker() init_kthread_worker() -> kthread_init_worker() init_kthread_work() -> kthread_init_work() insert_kthread_work() -> kthread_insert_work() queue_kthread_work() -> kthread_queue_work() flush_kthread_work() -> kthread_flush_work() flush_kthread_worker() -> kthread_flush_worker() Note that the names of DEFINE_KTHREAD_WORK*() macros stay as they are. It is common that the "DEFINE_" prefix has precedence over the subsystem names. Note that INIT() macros and init() functions use different naming scheme. There is no good solution. There are several reasons for this solution: + "init" in the function names stands for the verb "initialize" aka "initialize worker". While "INIT" in the macro names stands for the noun "INITIALIZER" aka "worker initializer". + INIT() macros are used only in DEFINE() macros + init() functions are used close to the other kthread() functions. It looks much better if all the functions use the same scheme. + There will be also kthread_destroy_worker() that will be used close to kthread_cancel_work(). It is related to the init() function. Again it looks better if all functions use the same naming scheme. + there are several precedents for such init() function names, e.g. amd_iommu_init_device(), free_area_init_node(), jump_label_init_type(), regmap_init_mmio_clk(), + It is not an argument but it was inconsistent even before. [arnd@arndb.de: fix linux-next merge conflict] Link: http://lkml.kernel.org/r/20160908135724.1311726-1-arnd@arndb.de Link: http://lkml.kernel.org/r/1470754545-17632-3-git-send-email-pmladek@suse.com Suggested-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Petr Mladek <pmladek@suse.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Tejun Heo <tj@kernel.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com> Cc: Josh Triplett <josh@joshtriplett.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Jiri Kosina <jkosina@suse.cz> Cc: Borislav Petkov <bp@suse.de> Cc: Michal Hocko <mhocko@suse.cz> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-10-11 13:55:20 -07:00
kthread_flush_worker(&s->kworker);
kthread_stop(s->kworker_task);
clk_disable_unprepare(s->clk);
}
EXPORT_SYMBOL_GPL(sc16is7xx_remove);
const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = {
{ .compatible = "nxp,sc16is740", .data = &sc16is74x_devtype, },
{ .compatible = "nxp,sc16is741", .data = &sc16is74x_devtype, },
{ .compatible = "nxp,sc16is750", .data = &sc16is750_devtype, },
{ .compatible = "nxp,sc16is752", .data = &sc16is752_devtype, },
{ .compatible = "nxp,sc16is760", .data = &sc16is760_devtype, },
{ .compatible = "nxp,sc16is762", .data = &sc16is762_devtype, },
{ }
};
EXPORT_SYMBOL_GPL(sc16is7xx_dt_ids);
MODULE_DEVICE_TABLE(of, sc16is7xx_dt_ids);
const struct regmap_config sc16is7xx_regcfg = {
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
.reg_bits = 5,
.pad_bits = 3,
.val_bits = 8,
.cache_type = REGCACHE_MAPLE,
.volatile_reg = sc16is7xx_regmap_volatile,
.precious_reg = sc16is7xx_regmap_precious,
.writeable_noinc_reg = sc16is7xx_regmap_noinc,
.readable_noinc_reg = sc16is7xx_regmap_noinc,
.max_raw_read = SC16IS7XX_FIFO_SIZE,
.max_raw_write = SC16IS7XX_FIFO_SIZE,
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
.max_register = SC16IS7XX_EFCR_REG,
};
EXPORT_SYMBOL_GPL(sc16is7xx_regcfg);
const char *sc16is7xx_regmap_name(u8 port_id)
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
{
switch (port_id) {
case 0: return "port0";
case 1: return "port1";
default:
WARN_ON(true);
return NULL;
}
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
}
EXPORT_SYMBOL_GPL(sc16is7xx_regmap_name);
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
unsigned int sc16is7xx_regmap_port_mask(unsigned int port_id)
serial: sc16is7xx: improve regmap debugfs by using one regmap per port With this current driver regmap implementation, it is hard to make sense of the register addresses displayed using the regmap debugfs interface, because they do not correspond to the actual register addresses documented in the datasheet. For example, register 1 is displayed as registers 04 thru 07: $ cat /sys/kernel/debug/regmap/spi0.0/registers 04: 10 -> Port 0, register offset 1 05: 10 -> Port 1, register offset 1 06: 00 -> Port 2, register offset 1 -> invalid 07: 00 -> port 3, register offset 1 -> invalid ... The reason is that bits 0 and 1 of the register address correspond to the channel (port) bits, so the register address itself starts at bit 2, and we must 'mentally' shift each register address by 2 bits to get its real address/offset. Also, only channels 0 and 1 are supported by the chip, so channel mask combinations of 10b and 11b are invalid, and the display of these registers is useless. This patch adds a separate regmap configuration for each port, similar to what is done in the max310x driver, so that register addresses displayed match the register addresses in the chip datasheet. Also, each port now has its own debugfs entry. Example with new regmap implementation: $ cat /sys/kernel/debug/regmap/spi0.0-port0/registers 1: 10 2: 01 3: 00 ... $ cat /sys/kernel/debug/regmap/spi0.0-port1/registers 1: 10 2: 01 3: 00 As an added bonus, this also simplifies some operations (read/write/modify) because it is no longer necessary to manually shift register addresses. Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231030211447.974779-1-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-10-30 17:14:47 -04:00
{
/* CH1,CH0 are at bits 2:1. */
return port_id << 1;
}
EXPORT_SYMBOL_GPL(sc16is7xx_regmap_port_mask);
static int __init sc16is7xx_init(void)
{
return uart_register_driver(&sc16is7xx_uart);
}
module_init(sc16is7xx_init);
static void __exit sc16is7xx_exit(void)
{
uart_unregister_driver(&sc16is7xx_uart);
}
module_exit(sc16is7xx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>");
MODULE_DESCRIPTION("SC16IS7xx tty serial core driver");