2017-11-06 18:11:51 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2014-04-24 20:56:06 -04:00
|
|
|
/*
|
2024-04-09 11:42:51 -04:00
|
|
|
* SC16IS7xx tty serial driver - common code
|
2014-04-24 20:56:06 -04:00
|
|
|
*
|
2024-04-09 11:42:51 -04:00
|
|
|
* Copyright (C) 2014 GridPoint
|
|
|
|
* Author: Jon Ringle <jringle@gridpoint.com>
|
|
|
|
* Based on max310x.c, by Alexander Shiyan <shc_work@mail.ru>
|
2014-04-24 20:56:06 -04:00
|
|
|
*/
|
|
|
|
|
2024-04-09 11:42:51 -04:00
|
|
|
#undef DEFAULT_SYMBOL_NAMESPACE
|
2024-12-03 19:21:07 +09:00
|
|
|
#define DEFAULT_SYMBOL_NAMESPACE "SERIAL_NXP_SC16IS7XX"
|
2015-07-31 14:44:23 +02:00
|
|
|
|
2024-08-26 17:42:45 +02:00
|
|
|
#include <linux/bits.h>
|
2014-04-24 20:56:06 -04:00
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/device.h>
|
2024-04-09 11:42:51 -04:00
|
|
|
#include <linux/export.h>
|
2024-06-18 19:26:20 +08:00
|
|
|
#include <linux/gpio/consumer.h>
|
2015-12-08 23:15:53 +01:00
|
|
|
#include <linux/gpio/driver.h>
|
2024-04-09 11:42:52 -04:00
|
|
|
#include <linux/idr.h>
|
2024-04-09 11:42:51 -04:00
|
|
|
#include <linux/kthread.h>
|
2019-03-18 12:29:16 +02:00
|
|
|
#include <linux/mod_devicetable.h>
|
2014-04-24 20:56:06 -04:00
|
|
|
#include <linux/module.h>
|
2019-03-18 12:29:15 +02:00
|
|
|
#include <linux/property.h>
|
2014-04-24 20:56:06 -04:00
|
|
|
#include <linux/regmap.h>
|
2024-04-09 11:42:49 -04:00
|
|
|
#include <linux/sched.h>
|
2014-04-24 20:56:06 -04:00
|
|
|
#include <linux/serial_core.h>
|
|
|
|
#include <linux/serial.h>
|
2024-04-09 11:42:51 -04:00
|
|
|
#include <linux/string.h>
|
2014-04-24 20:56:06 -04:00
|
|
|
#include <linux/tty.h>
|
|
|
|
#include <linux/tty_flip.h>
|
2014-04-25 15:53:10 -04:00
|
|
|
#include <linux/uaccess.h>
|
2023-12-21 18:18:10 -05:00
|
|
|
#include <linux/units.h>
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2024-04-09 11:42:51 -04:00
|
|
|
#include "sc16is7xx.h"
|
|
|
|
|
2015-07-31 14:44:23 +02:00
|
|
|
#define SC16IS7XX_MAX_DEVS 8
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
/* 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 */
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_IER_RDI_BIT BIT(0) /* Enable RX data interrupt */
|
|
|
|
#define SC16IS7XX_IER_THRI_BIT BIT(1) /* Enable TX holding register
|
2014-04-24 20:56:06 -04:00
|
|
|
* interrupt */
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_IER_RLSI_BIT BIT(2) /* Enable RX line status
|
2014-04-24 20:56:06 -04:00
|
|
|
* interrupt */
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_IER_MSI_BIT BIT(3) /* Enable Modem status
|
2014-04-24 20:56:06 -04:00
|
|
|
* interrupt */
|
|
|
|
|
|
|
|
/* IER register bits - write only if (EFR[4] == 1) */
|
2024-08-26 17:42:45 +02:00
|
|
|
#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 */
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
/* FCR register bits */
|
2024-08-26 17:42:45 +02:00
|
|
|
#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 */
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
/* FCR register bits - write only if (EFR[4] == 1) */
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_FCR_TXLVLL_BIT BIT(4) /* TX Trigger level LSB */
|
|
|
|
#define SC16IS7XX_FCR_TXLVLH_BIT BIT(5) /* TX Trigger level MSB */
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
/* IIR register bits */
|
2024-08-26 17:42:45 +02:00
|
|
|
#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)
|
|
|
|
*/
|
2014-04-24 20:56:06 -04:00
|
|
|
/* LCR register bits */
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_LCR_LENGTH0_BIT BIT(0) /* Word length bit 0 */
|
|
|
|
#define SC16IS7XX_LCR_LENGTH1_BIT BIT(1) /* Word length bit 1
|
2014-04-24 20:56:06 -04:00
|
|
|
*
|
|
|
|
* Word length bits table:
|
|
|
|
* 00 -> 5 bit words
|
|
|
|
* 01 -> 6 bit words
|
|
|
|
* 10 -> 7 bit words
|
|
|
|
* 11 -> 8 bit words
|
|
|
|
*/
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_LCR_STOPLEN_BIT BIT(2) /* STOP length bit
|
2014-04-24 20:56:06 -04:00
|
|
|
*
|
|
|
|
* STOP length bit table:
|
|
|
|
* 0 -> 1 stop bit
|
|
|
|
* 1 -> 1-1.5 stop bits if
|
|
|
|
* word length is 5,
|
|
|
|
* 2 stop bits otherwise
|
|
|
|
*/
|
2024-08-26 17:42:45 +02:00
|
|
|
#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 */
|
2014-04-24 20:56:06 -04:00
|
|
|
#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 */
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_MCR_DTR_BIT BIT(0) /* DTR complement
|
2014-04-24 20:56:06 -04:00
|
|
|
* - only on 75x/76x
|
|
|
|
*/
|
2024-08-26 17:42:45 +02:00
|
|
|
#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
|
2014-04-24 20:56:06 -04:00
|
|
|
* - write enabled
|
|
|
|
* if (EFR[4] == 1)
|
|
|
|
*/
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_MCR_IRDA_BIT BIT(6) /* Enable IrDA mode
|
2014-04-24 20:56:06 -04:00
|
|
|
* - write enabled
|
|
|
|
* if (EFR[4] == 1)
|
|
|
|
*/
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_MCR_CLKSEL_BIT BIT(7) /* Divide clock by 4
|
2014-04-24 20:56:06 -04:00
|
|
|
* - write enabled
|
|
|
|
* if (EFR[4] == 1)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* LSR register bits */
|
2024-08-26 17:42:45 +02:00
|
|
|
#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 */
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
/* MSR register bits */
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_MSR_DCTS_BIT BIT(0) /* Delta CTS Clear To Send */
|
|
|
|
#define SC16IS7XX_MSR_DDSR_BIT BIT(1) /* Delta DSR Data Set Ready
|
2014-04-24 20:56:06 -04:00
|
|
|
* or (IO4)
|
|
|
|
* - only on 75x/76x
|
|
|
|
*/
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_MSR_DRI_BIT BIT(2) /* Delta RI Ring Indicator
|
2014-04-24 20:56:06 -04:00
|
|
|
* or (IO7)
|
|
|
|
* - only on 75x/76x
|
|
|
|
*/
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_MSR_DCD_BIT BIT(3) /* Delta CD Carrier Detect
|
2014-04-24 20:56:06 -04:00
|
|
|
* or (IO6)
|
|
|
|
* - only on 75x/76x
|
|
|
|
*/
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_MSR_CTS_BIT BIT(4) /* CTS */
|
|
|
|
#define SC16IS7XX_MSR_DSR_BIT BIT(5) /* DSR (IO4)
|
2014-04-24 20:56:06 -04:00
|
|
|
* - only on 75x/76x
|
|
|
|
*/
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_MSR_RI_BIT BIT(6) /* RI (IO7)
|
2014-04-24 20:56:06 -04:00
|
|
|
* - only on 75x/76x
|
|
|
|
*/
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_MSR_CD_BIT BIT(7) /* CD (IO6)
|
2014-04-24 20:56:06 -04:00
|
|
|
* - 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.
|
|
|
|
*
|
2023-09-05 11:13:00 -04:00
|
|
|
* When the trigger level setting in TLR is zero, the SC16IS74x/75x/76x uses the
|
2014-04-24 20:56:06 -04:00
|
|
|
* 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)
|
|
|
|
|
2023-09-05 11:13:00 -04:00
|
|
|
/* IOControl register bits (Only 75x/76x) */
|
2024-08-26 17:42:45 +02:00
|
|
|
#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 */
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
/* EFCR register bits */
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_EFCR_9BIT_MODE_BIT BIT(0) /* Enable 9-bit or Multidrop
|
2014-04-24 20:56:06 -04:00
|
|
|
* mode (RS485) */
|
2024-08-26 17:42:45 +02:00
|
|
|
#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
|
2014-04-24 20:56:06 -04:00
|
|
|
* 0 = rate upto 115.2 kbit/s
|
2023-09-05 11:13:00 -04:00
|
|
|
* - Only 75x/76x
|
2014-04-24 20:56:06 -04:00
|
|
|
* 1 = rate upto 1.152 Mbit/s
|
2023-09-05 11:13:00 -04:00
|
|
|
* - Only 76x
|
2014-04-24 20:56:06 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
/* EFR register bits */
|
2024-08-26 17:42:45 +02:00
|
|
|
#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
|
2014-04-24 20:56:06 -04:00
|
|
|
* and writing to IER[7:4],
|
|
|
|
* FCR[5:4], MCR[7:5]
|
|
|
|
*/
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_EFR_SWFLOW3_BIT BIT(3)
|
|
|
|
#define SC16IS7XX_EFR_SWFLOW2_BIT BIT(2)
|
2024-08-26 17:42:01 +02:00
|
|
|
/*
|
2014-04-24 20:56:06 -04:00
|
|
|
* 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
|
|
|
|
*/
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_EFR_SWFLOW1_BIT BIT(1)
|
|
|
|
#define SC16IS7XX_EFR_SWFLOW0_BIT BIT(0)
|
2024-08-26 17:42:01 +02:00
|
|
|
/*
|
|
|
|
* SWFLOW bits 1 & 0 table:
|
2014-04-24 20:56:06 -04:00
|
|
|
* 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
|
|
|
|
*/
|
2022-02-21 11:56:13 +01:00
|
|
|
#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)
|
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
/* Misc definitions */
|
|
|
|
#define SC16IS7XX_FIFO_SIZE (64)
|
2023-08-07 17:45:54 -04:00
|
|
|
#define SC16IS7XX_GPIOS_PER_BANK 4
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2025-01-10 08:31:04 +01:00
|
|
|
#define SC16IS7XX_POLL_PERIOD_MS 10
|
2024-08-26 17:42:45 +02:00
|
|
|
#define SC16IS7XX_RECONF_MD BIT(0)
|
|
|
|
#define SC16IS7XX_RECONF_IER BIT(1)
|
|
|
|
#define SC16IS7XX_RECONF_RS485 BIT(2)
|
2015-05-29 21:20:31 +02:00
|
|
|
|
|
|
|
struct sc16is7xx_one_config {
|
|
|
|
unsigned int flags;
|
2022-03-01 07:03:30 +01:00
|
|
|
u8 ier_mask;
|
|
|
|
u8 ier_val;
|
2015-05-29 21:20:31 +02:00
|
|
|
};
|
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
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;
|
2023-12-11 12:13:51 -05:00
|
|
|
struct mutex efr_lock; /* EFR registers access */
|
2015-05-29 21:20:29 +02:00
|
|
|
struct kthread_work tx_work;
|
2015-05-29 21:20:31 +02:00
|
|
|
struct kthread_work reg_work;
|
2022-03-01 07:03:31 +01:00
|
|
|
struct kthread_delayed_work ms_work;
|
2015-05-29 21:20:31 +02:00
|
|
|
struct sc16is7xx_one_config config;
|
serial: sc16is7xx: fix TX fifo corruption
Sometimes, when a packet is received on channel A at almost the same time
as a packet is about to be transmitted on channel B, we observe with a
logic analyzer that the received packet on channel A is transmitted on
channel B. In other words, the Tx buffer data on channel B is corrupted
with data from channel A.
The problem appeared since commit 4409df5866b7 ("serial: sc16is7xx: change
EFR lock to operate on each channels"), which changed the EFR locking to
operate on each channel instead of chip-wise.
This commit has introduced a regression, because the EFR lock is used not
only to protect the EFR registers access, but also, in a very obscure and
undocumented way, to protect access to the data buffer, which is shared by
the Tx and Rx handlers, but also by each channel of the IC.
Fix this regression first by switching to kfifo_out_linear_ptr() in
sc16is7xx_handle_tx() to eliminate the need for a shared Rx/Tx buffer.
Secondly, replace the chip-wise Rx buffer with a separate Rx buffer for
each channel.
Fixes: 4409df5866b7 ("serial: sc16is7xx: change EFR lock to operate on each channels")
Cc: stable@vger.kernel.org
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Link: https://lore.kernel.org/r/20240723125302.1305372-2-hugo@hugovil.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-07-23 08:53:00 -04:00
|
|
|
unsigned char buf[SC16IS7XX_FIFO_SIZE]; /* Rx buffer. */
|
2022-03-01 07:03:31 +01:00
|
|
|
unsigned int old_mctrl;
|
2023-12-21 18:18:23 -05:00
|
|
|
u8 old_lcr; /* Value before EFR access. */
|
|
|
|
bool irda_mode;
|
2014-04-24 20:56:06 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
struct sc16is7xx_port {
|
2015-07-31 14:44:24 +02:00
|
|
|
const struct sc16is7xx_devtype *devtype;
|
2014-04-24 20:56:06 -04:00
|
|
|
struct clk *clk;
|
|
|
|
#ifdef CONFIG_GPIOLIB
|
|
|
|
struct gpio_chip gpio;
|
2023-08-07 17:45:54 -04:00
|
|
|
unsigned long gpio_valid_mask;
|
2014-04-24 20:56:06 -04:00
|
|
|
#endif
|
2023-08-07 17:45:54 -04:00
|
|
|
u8 mctrl_mask;
|
2015-05-29 21:20:29 +02:00
|
|
|
struct kthread_worker kworker;
|
|
|
|
struct task_struct *kworker_task;
|
2025-01-10 08:31:04 +01:00
|
|
|
struct kthread_delayed_work poll_work;
|
|
|
|
bool polling;
|
2020-02-12 18:46:11 -06:00
|
|
|
struct sc16is7xx_one p[];
|
2014-04-24 20:56:06 -04:00
|
|
|
};
|
|
|
|
|
2024-04-09 11:42:52 -04:00
|
|
|
static DEFINE_IDA(sc16is7xx_lines);
|
2015-07-31 14:44:23 +02:00
|
|
|
|
|
|
|
static struct uart_driver sc16is7xx_uart = {
|
|
|
|
.owner = THIS_MODULE,
|
2023-12-21 18:18:16 -05:00
|
|
|
.driver_name = SC16IS7XX_NAME,
|
2015-07-31 14:44:23 +02:00
|
|
|
.dev_name = "ttySC",
|
|
|
|
.nr = SC16IS7XX_MAX_DEVS,
|
|
|
|
};
|
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
#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);
|
2014-04-24 20:56:06 -04:00
|
|
|
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);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
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);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
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);
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
2023-12-21 18:18:21 -05:00
|
|
|
static void sc16is7xx_fifo_read(struct uart_port *port, u8 *rxbuf, unsigned int rxlen)
|
2015-06-13 13:40:20 +02:00
|
|
|
{
|
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);
|
2015-06-13 13:40:20 +02:00
|
|
|
|
2023-12-21 18:18:21 -05:00
|
|
|
regmap_noinc_read(one->regmap, SC16IS7XX_RHR_REG, rxbuf, rxlen);
|
2015-06-13 13:40:20 +02:00
|
|
|
}
|
|
|
|
|
2023-12-21 18:18:21 -05:00
|
|
|
static void sc16is7xx_fifo_write(struct uart_port *port, u8 *txbuf, u8 to_send)
|
2015-06-13 13:40:20 +02:00
|
|
|
{
|
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);
|
2015-06-13 13:40:20 +02:00
|
|
|
|
sc16is7xx: Fix TX buffer overrun caused by wrong tx fifo level read-out
We found that our sc16is7xx on spi reported a TX fifo free space value
(TXLVL_REG) of 255 ocassionally, which is obviously wrong, with a
64 byte fifo and caused a buffer overrun and a kernel crash.
To trigger this, a large write to the tty is sufficient. The fifo fills,
TXLVL_REG reads zero, but the handle_tx function does a zero-data-length
write to the TX fifo anyways through sc16is7xx_fifo_write. The next
TXLVL_REG read then yields 255, for unknown reasons. A subsequent read
is ok.
Prevent zero-data-length writes if the TX fifo is full, because they are
pointless, and because they trigger wrong TXLVL read-outs.
Furthermore, prevent a TX buffer overrun if the peripheral reports values
larger than the buffer size and thus, don't allow the peripheral to crash
the kernel.
Signed-off-by: Florian Achleitner <achleitner.florian@fronius.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2015-11-18 09:04:12 +01:00
|
|
|
/*
|
|
|
|
* Don't send zero-length data, at least on SPI it confuses the chip
|
|
|
|
* delivering wrong TXLVL data.
|
|
|
|
*/
|
|
|
|
if (unlikely(!to_send))
|
|
|
|
return;
|
|
|
|
|
2023-12-21 18:18:21 -05:00
|
|
|
regmap_noinc_write(one->regmap, SC16IS7XX_THR_REG, txbuf, to_send);
|
2015-06-13 13:40:20 +02:00
|
|
|
}
|
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
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);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
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);
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-12-21 18:18:23 -05:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2023-12-21 18:18:22 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2024-04-09 11:42:51 -04:00
|
|
|
const struct sc16is7xx_devtype sc16is74x_devtype = {
|
2014-04-24 20:56:06 -04:00
|
|
|
.name = "SC16IS74X",
|
|
|
|
.nr_gpio = 0,
|
|
|
|
.nr_uart = 1,
|
|
|
|
};
|
2024-04-09 11:42:51 -04:00
|
|
|
EXPORT_SYMBOL_GPL(sc16is74x_devtype);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2024-04-09 11:42:51 -04:00
|
|
|
const struct sc16is7xx_devtype sc16is750_devtype = {
|
2014-04-24 20:56:06 -04:00
|
|
|
.name = "SC16IS750",
|
2023-08-07 17:45:54 -04:00
|
|
|
.nr_gpio = 8,
|
2014-04-24 20:56:06 -04:00
|
|
|
.nr_uart = 1,
|
|
|
|
};
|
2024-04-09 11:42:51 -04:00
|
|
|
EXPORT_SYMBOL_GPL(sc16is750_devtype);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2024-04-09 11:42:51 -04:00
|
|
|
const struct sc16is7xx_devtype sc16is752_devtype = {
|
2014-04-24 20:56:06 -04:00
|
|
|
.name = "SC16IS752",
|
2023-08-07 17:45:54 -04:00
|
|
|
.nr_gpio = 8,
|
2014-04-24 20:56:06 -04:00
|
|
|
.nr_uart = 2,
|
|
|
|
};
|
2024-04-09 11:42:51 -04:00
|
|
|
EXPORT_SYMBOL_GPL(sc16is752_devtype);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2024-04-09 11:42:51 -04:00
|
|
|
const struct sc16is7xx_devtype sc16is760_devtype = {
|
2014-04-24 20:56:06 -04:00
|
|
|
.name = "SC16IS760",
|
2023-08-07 17:45:54 -04:00
|
|
|
.nr_gpio = 8,
|
2014-04-24 20:56:06 -04:00
|
|
|
.nr_uart = 1,
|
|
|
|
};
|
2024-04-09 11:42:51 -04:00
|
|
|
EXPORT_SYMBOL_GPL(sc16is760_devtype);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2024-04-09 11:42:51 -04:00
|
|
|
const struct sc16is7xx_devtype sc16is762_devtype = {
|
2014-04-24 20:56:06 -04:00
|
|
|
.name = "SC16IS762",
|
2023-08-07 17:45:54 -04:00
|
|
|
.nr_gpio = 8,
|
2014-04-24 20:56:06 -04:00
|
|
|
.nr_uart = 2,
|
|
|
|
};
|
2024-04-09 11:42:51 -04:00
|
|
|
EXPORT_SYMBOL_GPL(sc16is762_devtype);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
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) {
|
2014-04-24 20:56:06 -04:00
|
|
|
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:
|
2023-07-24 11:47:27 +08:00
|
|
|
case SC16IS7XX_IOCONTROL_REG:
|
2014-04-24 20:56:06 -04:00
|
|
|
return true;
|
|
|
|
default:
|
2023-12-21 18:18:18 -05:00
|
|
|
return false;
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2014-04-24 20:56:06 -04:00
|
|
|
case SC16IS7XX_RHR_REG:
|
|
|
|
return true;
|
|
|
|
default:
|
2023-12-21 18:18:18 -05:00
|
|
|
return false;
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-11 12:13:52 -05:00
|
|
|
static bool sc16is7xx_regmap_noinc(struct device *dev, unsigned int reg)
|
|
|
|
{
|
|
|
|
return reg == SC16IS7XX_RHR_REG;
|
|
|
|
}
|
|
|
|
|
2024-04-30 16:04:30 -04:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2014-04-24 20:56:06 -04:00
|
|
|
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);
|
2014-04-24 20:56:06 -04:00
|
|
|
u8 lcr;
|
2024-04-30 16:04:30 -04:00
|
|
|
unsigned int prescaler = 1;
|
2014-04-24 20:56:06 -04:00
|
|
|
unsigned long clk = port->uartclk, div = clk / 16 / baud;
|
|
|
|
|
2023-12-21 18:18:19 -05:00
|
|
|
if (div >= BIT(16)) {
|
2024-04-30 16:04:30 -04:00
|
|
|
prescaler = 4;
|
|
|
|
div /= prescaler;
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable enhanced features */
|
2023-12-21 18:18:23 -05:00
|
|
|
sc16is7xx_efr_lock(port);
|
2022-02-21 11:56:13 +01:00
|
|
|
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
|
|
|
|
SC16IS7XX_EFR_ENABLE_BIT,
|
|
|
|
SC16IS7XX_EFR_ENABLE_BIT);
|
2023-12-21 18:18:23 -05:00
|
|
|
sc16is7xx_efr_unlock(port);
|
2018-09-12 15:31:56 +01:00
|
|
|
|
2024-04-30 16:04:30 -04:00
|
|
|
/* If bit MCR_CLKSEL is set, the divide by 4 prescaler is activated. */
|
2014-04-24 20:56:06 -04:00
|
|
|
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
|
|
|
|
SC16IS7XX_MCR_CLKSEL_BIT,
|
2024-04-30 16:04:30 -04:00
|
|
|
prescaler == 1 ? 0 : SC16IS7XX_MCR_CLKSEL_BIT);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2024-07-23 08:53:01 -04:00
|
|
|
mutex_lock(&one->efr_lock);
|
|
|
|
|
2023-12-21 18:18:23 -05:00
|
|
|
/* Backup LCR and access special register set (DLL/DLH) */
|
|
|
|
lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
|
2014-04-24 20:56:06 -04:00
|
|
|
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);
|
2014-04-24 20:56:06 -04:00
|
|
|
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);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2023-12-21 18:18:23 -05:00
|
|
|
/* Restore LCR and access to general register set */
|
2014-04-24 20:56:06 -04:00
|
|
|
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
|
|
|
|
|
2024-07-23 08:53:01 -04:00
|
|
|
mutex_unlock(&one->efr_lock);
|
|
|
|
|
2024-04-30 16:04:30 -04:00
|
|
|
return DIV_ROUND_CLOSEST((clk / prescaler) / 16, div);
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
|
|
|
|
unsigned int iir)
|
|
|
|
{
|
serial: sc16is7xx: fix TX fifo corruption
Sometimes, when a packet is received on channel A at almost the same time
as a packet is about to be transmitted on channel B, we observe with a
logic analyzer that the received packet on channel A is transmitted on
channel B. In other words, the Tx buffer data on channel B is corrupted
with data from channel A.
The problem appeared since commit 4409df5866b7 ("serial: sc16is7xx: change
EFR lock to operate on each channels"), which changed the EFR locking to
operate on each channel instead of chip-wise.
This commit has introduced a regression, because the EFR lock is used not
only to protect the EFR registers access, but also, in a very obscure and
undocumented way, to protect access to the data buffer, which is shared by
the Tx and Rx handlers, but also by each channel of the IC.
Fix this regression first by switching to kfifo_out_linear_ptr() in
sc16is7xx_handle_tx() to eliminate the need for a shared Rx/Tx buffer.
Secondly, replace the chip-wise Rx buffer with a separate Rx buffer for
each channel.
Fixes: 4409df5866b7 ("serial: sc16is7xx: change EFR lock to operate on each channels")
Cc: stable@vger.kernel.org
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Link: https://lore.kernel.org/r/20240723125302.1305372-2-hugo@hugovil.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-07-23 08:53:00 -04:00
|
|
|
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
2023-07-12 10:18:11 +02:00
|
|
|
unsigned int lsr = 0, bytes_read, i;
|
2014-04-24 20:56:06 -04:00
|
|
|
bool read_lsr = (iir == SC16IS7XX_IIR_RLSE_SRC) ? true : false;
|
2023-07-12 10:18:11 +02:00
|
|
|
u8 ch, flag;
|
2014-04-24 20:56:06 -04:00
|
|
|
|
serial: sc16is7xx: fix TX fifo corruption
Sometimes, when a packet is received on channel A at almost the same time
as a packet is about to be transmitted on channel B, we observe with a
logic analyzer that the received packet on channel A is transmitted on
channel B. In other words, the Tx buffer data on channel B is corrupted
with data from channel A.
The problem appeared since commit 4409df5866b7 ("serial: sc16is7xx: change
EFR lock to operate on each channels"), which changed the EFR locking to
operate on each channel instead of chip-wise.
This commit has introduced a regression, because the EFR lock is used not
only to protect the EFR registers access, but also, in a very obscure and
undocumented way, to protect access to the data buffer, which is shared by
the Tx and Rx handlers, but also by each channel of the IC.
Fix this regression first by switching to kfifo_out_linear_ptr() in
sc16is7xx_handle_tx() to eliminate the need for a shared Rx/Tx buffer.
Secondly, replace the chip-wise Rx buffer with a separate Rx buffer for
each channel.
Fixes: 4409df5866b7 ("serial: sc16is7xx: change EFR lock to operate on each channels")
Cc: stable@vger.kernel.org
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Link: https://lore.kernel.org/r/20240723125302.1305372-2-hugo@hugovil.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-07-23 08:53:00 -04:00
|
|
|
if (unlikely(rxlen >= sizeof(one->buf))) {
|
2014-04-24 20:56:06 -04:00
|
|
|
dev_warn_ratelimited(port->dev,
|
2015-07-31 14:44:22 +02:00
|
|
|
"ttySC%i: Possible RX FIFO overrun: %d\n",
|
2014-04-24 20:56:06 -04:00
|
|
|
port->line, rxlen);
|
|
|
|
port->icount.buf_overrun++;
|
|
|
|
/* Ensure sanity of RX level */
|
serial: sc16is7xx: fix TX fifo corruption
Sometimes, when a packet is received on channel A at almost the same time
as a packet is about to be transmitted on channel B, we observe with a
logic analyzer that the received packet on channel A is transmitted on
channel B. In other words, the Tx buffer data on channel B is corrupted
with data from channel A.
The problem appeared since commit 4409df5866b7 ("serial: sc16is7xx: change
EFR lock to operate on each channels"), which changed the EFR locking to
operate on each channel instead of chip-wise.
This commit has introduced a regression, because the EFR lock is used not
only to protect the EFR registers access, but also, in a very obscure and
undocumented way, to protect access to the data buffer, which is shared by
the Tx and Rx handlers, but also by each channel of the IC.
Fix this regression first by switching to kfifo_out_linear_ptr() in
sc16is7xx_handle_tx() to eliminate the need for a shared Rx/Tx buffer.
Secondly, replace the chip-wise Rx buffer with a separate Rx buffer for
each channel.
Fixes: 4409df5866b7 ("serial: sc16is7xx: change EFR lock to operate on each channels")
Cc: stable@vger.kernel.org
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Link: https://lore.kernel.org/r/20240723125302.1305372-2-hugo@hugovil.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-07-23 08:53:00 -04:00
|
|
|
rxlen = sizeof(one->buf);
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
serial: sc16is7xx: fix TX fifo corruption
Sometimes, when a packet is received on channel A at almost the same time
as a packet is about to be transmitted on channel B, we observe with a
logic analyzer that the received packet on channel A is transmitted on
channel B. In other words, the Tx buffer data on channel B is corrupted
with data from channel A.
The problem appeared since commit 4409df5866b7 ("serial: sc16is7xx: change
EFR lock to operate on each channels"), which changed the EFR locking to
operate on each channel instead of chip-wise.
This commit has introduced a regression, because the EFR lock is used not
only to protect the EFR registers access, but also, in a very obscure and
undocumented way, to protect access to the data buffer, which is shared by
the Tx and Rx handlers, but also by each channel of the IC.
Fix this regression first by switching to kfifo_out_linear_ptr() in
sc16is7xx_handle_tx() to eliminate the need for a shared Rx/Tx buffer.
Secondly, replace the chip-wise Rx buffer with a separate Rx buffer for
each channel.
Fixes: 4409df5866b7 ("serial: sc16is7xx: change EFR lock to operate on each channels")
Cc: stable@vger.kernel.org
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Link: https://lore.kernel.org/r/20240723125302.1305372-2-hugo@hugovil.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-07-23 08:53:00 -04:00
|
|
|
one->buf[0] = sc16is7xx_port_read(port, SC16IS7XX_RHR_REG);
|
2014-04-24 20:56:06 -04:00
|
|
|
bytes_read = 1;
|
|
|
|
} else {
|
serial: sc16is7xx: fix TX fifo corruption
Sometimes, when a packet is received on channel A at almost the same time
as a packet is about to be transmitted on channel B, we observe with a
logic analyzer that the received packet on channel A is transmitted on
channel B. In other words, the Tx buffer data on channel B is corrupted
with data from channel A.
The problem appeared since commit 4409df5866b7 ("serial: sc16is7xx: change
EFR lock to operate on each channels"), which changed the EFR locking to
operate on each channel instead of chip-wise.
This commit has introduced a regression, because the EFR lock is used not
only to protect the EFR registers access, but also, in a very obscure and
undocumented way, to protect access to the data buffer, which is shared by
the Tx and Rx handlers, but also by each channel of the IC.
Fix this regression first by switching to kfifo_out_linear_ptr() in
sc16is7xx_handle_tx() to eliminate the need for a shared Rx/Tx buffer.
Secondly, replace the chip-wise Rx buffer with a separate Rx buffer for
each channel.
Fixes: 4409df5866b7 ("serial: sc16is7xx: change EFR lock to operate on each channels")
Cc: stable@vger.kernel.org
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Link: https://lore.kernel.org/r/20240723125302.1305372-2-hugo@hugovil.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-07-23 08:53:00 -04:00
|
|
|
sc16is7xx_fifo_read(port, one->buf, rxlen);
|
2014-04-24 20:56:06 -04:00
|
|
|
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) {
|
serial: sc16is7xx: fix TX fifo corruption
Sometimes, when a packet is received on channel A at almost the same time
as a packet is about to be transmitted on channel B, we observe with a
logic analyzer that the received packet on channel A is transmitted on
channel B. In other words, the Tx buffer data on channel B is corrupted
with data from channel A.
The problem appeared since commit 4409df5866b7 ("serial: sc16is7xx: change
EFR lock to operate on each channels"), which changed the EFR locking to
operate on each channel instead of chip-wise.
This commit has introduced a regression, because the EFR lock is used not
only to protect the EFR registers access, but also, in a very obscure and
undocumented way, to protect access to the data buffer, which is shared by
the Tx and Rx handlers, but also by each channel of the IC.
Fix this regression first by switching to kfifo_out_linear_ptr() in
sc16is7xx_handle_tx() to eliminate the need for a shared Rx/Tx buffer.
Secondly, replace the chip-wise Rx buffer with a separate Rx buffer for
each channel.
Fixes: 4409df5866b7 ("serial: sc16is7xx: change EFR lock to operate on each channels")
Cc: stable@vger.kernel.org
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Link: https://lore.kernel.org/r/20240723125302.1305372-2-hugo@hugovil.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-07-23 08:53:00 -04:00
|
|
|
ch = one->buf[i];
|
2014-04-24 20:56:06 -04:00
|
|
|
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)
|
|
|
|
{
|
2024-04-05 08:08:23 +02:00
|
|
|
struct tty_port *tport = &port->state->port;
|
2022-03-01 07:03:30 +01:00
|
|
|
unsigned long flags;
|
2024-04-05 08:08:23 +02:00
|
|
|
unsigned int txlen;
|
serial: sc16is7xx: fix TX fifo corruption
Sometimes, when a packet is received on channel A at almost the same time
as a packet is about to be transmitted on channel B, we observe with a
logic analyzer that the received packet on channel A is transmitted on
channel B. In other words, the Tx buffer data on channel B is corrupted
with data from channel A.
The problem appeared since commit 4409df5866b7 ("serial: sc16is7xx: change
EFR lock to operate on each channels"), which changed the EFR locking to
operate on each channel instead of chip-wise.
This commit has introduced a regression, because the EFR lock is used not
only to protect the EFR registers access, but also, in a very obscure and
undocumented way, to protect access to the data buffer, which is shared by
the Tx and Rx handlers, but also by each channel of the IC.
Fix this regression first by switching to kfifo_out_linear_ptr() in
sc16is7xx_handle_tx() to eliminate the need for a shared Rx/Tx buffer.
Secondly, replace the chip-wise Rx buffer with a separate Rx buffer for
each channel.
Fixes: 4409df5866b7 ("serial: sc16is7xx: change EFR lock to operate on each channels")
Cc: stable@vger.kernel.org
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Link: https://lore.kernel.org/r/20240723125302.1305372-2-hugo@hugovil.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-07-23 08:53:00 -04:00
|
|
|
unsigned char *tail;
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
if (unlikely(port->x_char)) {
|
|
|
|
sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char);
|
|
|
|
port->icount.tx++;
|
|
|
|
port->x_char = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-04-05 08:08:23 +02:00
|
|
|
if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_lock_irqsave(port, &flags);
|
2022-03-01 07:03:30 +01:00
|
|
|
sc16is7xx_stop_tx(port);
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_unlock_irqrestore(port, flags);
|
2014-04-24 20:56:06 -04:00
|
|
|
return;
|
2022-03-01 07:03:30 +01:00
|
|
|
}
|
2014-04-24 20:56:06 -04:00
|
|
|
|
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;
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
serial: sc16is7xx: fix TX fifo corruption
Sometimes, when a packet is received on channel A at almost the same time
as a packet is about to be transmitted on channel B, we observe with a
logic analyzer that the received packet on channel A is transmitted on
channel B. In other words, the Tx buffer data on channel B is corrupted
with data from channel A.
The problem appeared since commit 4409df5866b7 ("serial: sc16is7xx: change
EFR lock to operate on each channels"), which changed the EFR locking to
operate on each channel instead of chip-wise.
This commit has introduced a regression, because the EFR lock is used not
only to protect the EFR registers access, but also, in a very obscure and
undocumented way, to protect access to the data buffer, which is shared by
the Tx and Rx handlers, but also by each channel of the IC.
Fix this regression first by switching to kfifo_out_linear_ptr() in
sc16is7xx_handle_tx() to eliminate the need for a shared Rx/Tx buffer.
Secondly, replace the chip-wise Rx buffer with a separate Rx buffer for
each channel.
Fixes: 4409df5866b7 ("serial: sc16is7xx: change EFR lock to operate on each channels")
Cc: stable@vger.kernel.org
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Link: https://lore.kernel.org/r/20240723125302.1305372-2-hugo@hugovil.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2024-07-23 08:53:00 -04:00
|
|
|
txlen = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen);
|
|
|
|
sc16is7xx_fifo_write(port, tail, txlen);
|
|
|
|
uart_xmit_advance(port, txlen);
|
2024-04-05 08:08:23 +02:00
|
|
|
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_lock_irqsave(port, &flags);
|
2024-04-05 08:08:23 +02:00
|
|
|
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
|
2014-04-24 20:56:06 -04:00
|
|
|
uart_write_wakeup(port);
|
2022-03-01 07:03:30 +01:00
|
|
|
|
2024-04-05 08:08:23 +02:00
|
|
|
if (kfifo_is_empty(&tport->xmit_fifo))
|
2022-03-01 07:03:30 +01:00
|
|
|
sc16is7xx_stop_tx(port);
|
2023-12-11 12:13:53 -05:00
|
|
|
else
|
|
|
|
sc16is7xx_ier_set(port, SC16IS7XX_IER_THRI_BIT);
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_unlock_irqrestore(port, flags);
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
2022-03-01 07:03:31 +01:00
|
|
|
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;
|
|
|
|
|
2023-12-11 12:13:51 -05:00
|
|
|
lockdep_assert_held_once(&one->efr_lock);
|
2022-03-01 07:03:31 +01:00
|
|
|
|
|
|
|
status = sc16is7xx_get_hwmctrl(port);
|
|
|
|
changed = status ^ one->old_mctrl;
|
|
|
|
|
|
|
|
if (changed == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
one->old_mctrl = status;
|
|
|
|
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_lock_irqsave(port, &flags);
|
2022-03-01 07:03:31 +01:00
|
|
|
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);
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_unlock_irqrestore(port, flags);
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
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)
|
2014-04-24 20:56:06 -04:00
|
|
|
{
|
2023-12-11 12:13:51 -05:00
|
|
|
bool rc = true;
|
2023-12-21 18:18:11 -05:00
|
|
|
unsigned int iir, rxlen;
|
2014-04-24 20:56:06 -04:00
|
|
|
struct uart_port *port = &s->p[portno].port;
|
2023-12-11 12:13:51 -05:00
|
|
|
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
|
|
|
|
|
|
|
mutex_lock(&one->efr_lock);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2023-12-21 18:18:11 -05:00
|
|
|
iir = sc16is7xx_port_read(port, SC16IS7XX_IIR_REG);
|
|
|
|
if (iir & SC16IS7XX_IIR_NO_INT_BIT) {
|
|
|
|
rc = false;
|
|
|
|
goto out_port_irq;
|
|
|
|
}
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2023-12-21 18:18:11 -05:00
|
|
|
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;
|
2022-03-01 07:03:31 +01:00
|
|
|
/* CTSRTS interrupt comes only when CTS goes inactive */
|
2023-12-21 18:18:11 -05:00
|
|
|
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;
|
|
|
|
}
|
2023-12-11 12:13:51 -05:00
|
|
|
|
|
|
|
out_port_irq:
|
|
|
|
mutex_unlock(&one->efr_lock);
|
|
|
|
|
|
|
|
return rc;
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
2020-05-21 11:11:50 +02:00
|
|
|
static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
|
2014-04-24 20:56:06 -04:00
|
|
|
{
|
2023-12-21 18:18:12 -05:00
|
|
|
bool keep_polling;
|
|
|
|
|
2020-05-21 11:11:50 +02:00
|
|
|
struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2023-12-21 18:18:12 -05:00
|
|
|
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;
|
|
|
|
|
2023-12-21 18:18:12 -05:00
|
|
|
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);
|
2023-12-21 18:18:12 -05:00
|
|
|
} while (keep_polling);
|
2018-09-12 15:31:56 +01:00
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2025-01-10 08:31:04 +01:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2015-05-29 21:20:29 +02:00
|
|
|
static void sc16is7xx_tx_proc(struct kthread_work *ws)
|
2014-04-24 20:56:06 -04:00
|
|
|
{
|
2015-05-29 21:20:30 +02:00
|
|
|
struct uart_port *port = &(to_sc16is7xx_one(ws, tx_work)->port);
|
2023-12-11 12:13:51 -05:00
|
|
|
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2015-05-29 21:20:30 +02:00
|
|
|
if ((port->rs485.flags & SER_RS485_ENABLED) &&
|
|
|
|
(port->rs485.delay_rts_before_send > 0))
|
|
|
|
msleep(port->rs485.delay_rts_before_send);
|
|
|
|
|
2023-12-11 12:13:51 -05:00
|
|
|
mutex_lock(&one->efr_lock);
|
2015-05-29 21:20:30 +02:00
|
|
|
sc16is7xx_handle_tx(port);
|
2023-12-11 12:13:51 -05:00
|
|
|
mutex_unlock(&one->efr_lock);
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
2015-05-29 21:20:33 +02:00
|
|
|
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;
|
|
|
|
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_lock_irqsave(port, &irqflags);
|
2015-05-29 21:20:33 +02:00
|
|
|
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;
|
|
|
|
}
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_unlock_irqrestore(port, irqflags);
|
2015-05-29 21:20:33 +02:00
|
|
|
|
|
|
|
sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG, mask, efcr);
|
|
|
|
}
|
|
|
|
|
2015-05-29 21:20:31 +02:00
|
|
|
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;
|
|
|
|
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_lock_irqsave(&one->port, &irqflags);
|
2015-05-29 21:20:31 +02:00
|
|
|
config = one->config;
|
|
|
|
memset(&one->config, 0, sizeof(one->config));
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_unlock_irqrestore(&one->port, irqflags);
|
2015-05-29 21:20:31 +02:00
|
|
|
|
2016-02-14 15:05:25 +01:00
|
|
|
if (config.flags & SC16IS7XX_RECONF_MD) {
|
2022-02-21 11:56:14 +01:00
|
|
|
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;
|
2015-05-29 21:20:31 +02:00
|
|
|
sc16is7xx_port_update(&one->port, SC16IS7XX_MCR_REG,
|
2022-02-21 11:56:14 +01:00
|
|
|
SC16IS7XX_MCR_RTS_BIT |
|
|
|
|
SC16IS7XX_MCR_DTR_BIT |
|
2015-05-29 21:20:31 +02:00
|
|
|
SC16IS7XX_MCR_LOOP_BIT,
|
2022-02-21 11:56:14 +01:00
|
|
|
mcr);
|
2016-02-14 15:05:25 +01:00
|
|
|
}
|
2022-02-21 11:56:14 +01:00
|
|
|
|
2015-05-29 21:20:32 +02:00
|
|
|
if (config.flags & SC16IS7XX_RECONF_IER)
|
|
|
|
sc16is7xx_port_update(&one->port, SC16IS7XX_IER_REG,
|
2022-03-01 07:03:30 +01:00
|
|
|
config.ier_mask, config.ier_val);
|
2015-05-29 21:20:33 +02:00
|
|
|
|
|
|
|
if (config.flags & SC16IS7XX_RECONF_RS485)
|
|
|
|
sc16is7xx_reconf_rs485(&one->port);
|
2015-05-29 21:20:31 +02:00
|
|
|
}
|
|
|
|
|
2022-03-01 07:03:31 +01:00
|
|
|
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) {
|
2023-12-11 12:13:51 -05:00
|
|
|
mutex_lock(&one->efr_lock);
|
2022-03-01 07:03:31 +01:00
|
|
|
sc16is7xx_update_mlines(one);
|
2023-12-11 12:13:51 -05:00
|
|
|
mutex_unlock(&one->efr_lock);
|
2022-03-01 07:03:31 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
static void sc16is7xx_start_tx(struct uart_port *port)
|
|
|
|
{
|
2015-05-29 21:20:29 +02:00
|
|
|
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
2014-04-24 20:56:06 -04:00
|
|
|
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
|
|
|
|
2016-10-11 13:55:20 -07:00
|
|
|
kthread_queue_work(&s->kworker, &one->tx_work);
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
2022-03-01 07:03:32 +01:00
|
|
|
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.
|
|
|
|
*/
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_lock_irqsave(port, &flags);
|
2022-03-01 07:03:32 +01:00
|
|
|
sc16is7xx_ier_clear(port, SC16IS7XX_IER_RDI_BIT);
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_unlock_irqrestore(port, flags);
|
2022-03-01 07:03:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void sc16is7xx_unthrottle(struct uart_port *port)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_lock_irqsave(port, &flags);
|
2022-03-01 07:03:32 +01:00
|
|
|
sc16is7xx_ier_set(port, SC16IS7XX_IER_RDI_BIT);
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_unlock_irqrestore(port, flags);
|
2022-03-01 07:03:32 +01:00
|
|
|
}
|
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
static unsigned int sc16is7xx_tx_empty(struct uart_port *port)
|
|
|
|
{
|
2015-05-29 21:20:28 +02:00
|
|
|
unsigned int lsr;
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
lsr = sc16is7xx_port_read(port, SC16IS7XX_LSR_REG);
|
|
|
|
|
2015-05-29 21:20:28 +02:00
|
|
|
return (lsr & SC16IS7XX_LSR_TEMT_BIT) ? TIOCSER_TEMT : 0;
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int sc16is7xx_get_mctrl(struct uart_port *port)
|
|
|
|
{
|
2022-03-01 07:03:31 +01:00
|
|
|
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;
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
|
|
|
|
{
|
2015-05-29 21:20:31 +02:00
|
|
|
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
2014-04-24 20:56:06 -04:00
|
|
|
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
|
|
|
|
2015-05-29 21:20:31 +02:00
|
|
|
one->config.flags |= SC16IS7XX_RECONF_MD;
|
2016-10-11 13:55:20 -07:00
|
|
|
kthread_queue_work(&s->kworker, &one->reg_work);
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2022-08-16 14:57:37 +03:00
|
|
|
const struct ktermios *old)
|
2014-04-24 20:56:06 -04:00
|
|
|
{
|
2022-03-01 07:03:31 +01:00
|
|
|
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
2014-04-24 20:56:06 -04:00
|
|
|
unsigned int lcr, flow = 0;
|
|
|
|
int baud;
|
2022-03-01 07:03:31 +01:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
kthread_cancel_delayed_work_sync(&one->ms_work);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
/* 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 */
|
2022-03-01 07:03:32 +01:00
|
|
|
port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
|
|
|
|
if (termios->c_cflag & CRTSCTS) {
|
2014-04-24 20:56:06 -04:00
|
|
|
flow |= SC16IS7XX_EFR_AUTOCTS_BIT |
|
|
|
|
SC16IS7XX_EFR_AUTORTS_BIT;
|
2022-03-01 07:03:32 +01:00
|
|
|
port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
|
|
|
|
}
|
2014-04-24 20:56:06 -04:00
|
|
|
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);
|
|
|
|
|
2023-12-21 18:18:23 -05:00
|
|
|
/* 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);
|
2018-09-12 15:31:56 +01:00
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
/* 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);
|
|
|
|
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_lock_irqsave(port, &flags);
|
2022-03-01 07:03:31 +01:00
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
/* Update timeout according to new baud rate */
|
|
|
|
uart_update_timeout(port, termios->c_cflag, baud);
|
2022-03-01 07:03:31 +01:00
|
|
|
|
|
|
|
if (UART_ENABLE_MS(port, termios->c_cflag))
|
|
|
|
sc16is7xx_enable_ms(port);
|
|
|
|
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_unlock_irqrestore(port, flags);
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
2022-06-24 23:42:08 +03:00
|
|
|
static int sc16is7xx_config_rs485(struct uart_port *port, struct ktermios *termios,
|
2015-03-31 21:11:40 +02:00
|
|
|
struct serial_rs485 *rs485)
|
2014-04-24 20:56:06 -04:00
|
|
|
{
|
2015-05-29 21:20:33 +02:00
|
|
|
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
|
|
|
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
2015-03-31 21:11:40 +02:00
|
|
|
|
|
|
|
if (rs485->flags & SER_RS485_ENABLED) {
|
2015-05-29 21:20:26 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
2015-03-31 21:11:40 +02:00
|
|
|
}
|
|
|
|
|
2015-05-29 21:20:33 +02:00
|
|
|
one->config.flags |= SC16IS7XX_RECONF_RS485;
|
2016-10-11 13:55:20 -07:00
|
|
|
kthread_queue_work(&s->kworker, &one->reg_work);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2014-11-06 09:22:54 +01:00
|
|
|
return 0;
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int sc16is7xx_startup(struct uart_port *port)
|
|
|
|
{
|
2020-05-29 07:50:58 +02:00
|
|
|
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
2025-01-10 08:31:04 +01:00
|
|
|
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
2014-04-24 20:56:06 -04:00
|
|
|
unsigned int val;
|
2022-03-01 07:03:31 +01:00
|
|
|
unsigned long flags;
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
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);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
/* Enable write access to enhanced features and internal clock div */
|
2022-02-21 11:56:13 +01:00
|
|
|
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
|
|
|
|
SC16IS7XX_EFR_ENABLE_BIT,
|
|
|
|
SC16IS7XX_EFR_ENABLE_BIT);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
/* 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);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
/* Now, initialize the UART */
|
|
|
|
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_WORD_LEN_8);
|
|
|
|
|
2020-05-29 07:50:58 +02:00
|
|
|
/* 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);
|
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
/* Enable the Rx and Tx FIFO */
|
|
|
|
sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG,
|
|
|
|
SC16IS7XX_EFCR_RXDISABLE_BIT |
|
|
|
|
SC16IS7XX_EFCR_TXDISABLE_BIT,
|
|
|
|
0);
|
|
|
|
|
2022-03-01 07:03:31 +01:00
|
|
|
/* Enable RX, CTS change and modem lines interrupts */
|
|
|
|
val = SC16IS7XX_IER_RDI_BIT | SC16IS7XX_IER_CTSI_BIT |
|
|
|
|
SC16IS7XX_IER_MSI_BIT;
|
2014-04-24 20:56:06 -04:00
|
|
|
sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val);
|
|
|
|
|
2022-03-01 07:03:31 +01:00
|
|
|
/* Enable modem status polling */
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_lock_irqsave(port, &flags);
|
2022-03-01 07:03:31 +01:00
|
|
|
sc16is7xx_enable_ms(port);
|
2023-09-14 20:44:12 +02:06
|
|
|
uart_port_unlock_irqrestore(port, flags);
|
2022-03-01 07:03:31 +01:00
|
|
|
|
2025-01-10 08:31:04 +01:00
|
|
|
if (s->polling)
|
|
|
|
kthread_queue_delayed_work(&s->kworker, &s->poll_work,
|
|
|
|
msecs_to_jiffies(SC16IS7XX_POLL_PERIOD_MS));
|
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sc16is7xx_shutdown(struct uart_port *port)
|
|
|
|
{
|
2015-05-29 21:20:29 +02:00
|
|
|
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
2022-03-01 07:03:31 +01:00
|
|
|
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
|
|
|
|
|
|
|
kthread_cancel_delayed_work_sync(&one->ms_work);
|
2015-05-29 21:20:29 +02:00
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
/* Disable all interrupts */
|
|
|
|
sc16is7xx_port_write(port, SC16IS7XX_IER_REG, 0);
|
2022-04-18 17:43:39 +08:00
|
|
|
/* Disable TX/RX */
|
2015-03-17 00:28:47 +01:00
|
|
|
sc16is7xx_port_update(port, SC16IS7XX_EFCR_REG,
|
|
|
|
SC16IS7XX_EFCR_RXDISABLE_BIT |
|
2022-04-18 17:43:39 +08:00
|
|
|
SC16IS7XX_EFCR_TXDISABLE_BIT,
|
2015-03-17 00:28:47 +01:00
|
|
|
SC16IS7XX_EFCR_RXDISABLE_BIT |
|
|
|
|
SC16IS7XX_EFCR_TXDISABLE_BIT);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
sc16is7xx_power(port, 0);
|
2015-05-29 21:20:29 +02:00
|
|
|
|
2025-01-10 08:31:04 +01:00
|
|
|
if (s->polling)
|
|
|
|
kthread_cancel_delayed_work_sync(&s->poll_work);
|
|
|
|
|
2016-10-11 13:55:20 -07:00
|
|
|
kthread_flush_worker(&s->kworker);
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2022-03-01 07:03:32 +01:00
|
|
|
.throttle = sc16is7xx_throttle,
|
|
|
|
.unthrottle = sc16is7xx_unthrottle,
|
2014-04-24 20:56:06 -04:00
|
|
|
.stop_rx = sc16is7xx_stop_rx,
|
2022-03-01 07:03:31 +01:00
|
|
|
.enable_ms = sc16is7xx_enable_ms,
|
2014-04-24 20:56:06 -04:00
|
|
|
.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;
|
2015-12-08 23:15:53 +01:00
|
|
|
struct sc16is7xx_port *s = gpiochip_get_data(chip);
|
2014-04-24 20:56:06 -04:00
|
|
|
struct uart_port *port = &s->p[0].port;
|
|
|
|
|
|
|
|
val = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG);
|
|
|
|
|
|
|
|
return !!(val & BIT(offset));
|
|
|
|
}
|
|
|
|
|
2025-04-08 09:42:01 +02:00
|
|
|
static int sc16is7xx_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
|
|
|
int val)
|
2014-04-24 20:56:06 -04:00
|
|
|
{
|
2015-12-08 23:15:53 +01:00
|
|
|
struct sc16is7xx_port *s = gpiochip_get_data(chip);
|
2014-04-24 20:56:06 -04:00
|
|
|
struct uart_port *port = &s->p[0].port;
|
|
|
|
|
|
|
|
sc16is7xx_port_update(port, SC16IS7XX_IOSTATE_REG, BIT(offset),
|
|
|
|
val ? BIT(offset) : 0);
|
2025-04-08 09:42:01 +02:00
|
|
|
|
|
|
|
return 0;
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int sc16is7xx_gpio_direction_input(struct gpio_chip *chip,
|
|
|
|
unsigned offset)
|
|
|
|
{
|
2015-12-08 23:15:53 +01:00
|
|
|
struct sc16is7xx_port *s = gpiochip_get_data(chip);
|
2014-04-24 20:56:06 -04:00
|
|
|
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)
|
|
|
|
{
|
2015-12-08 23:15:53 +01:00
|
|
|
struct sc16is7xx_port *s = gpiochip_get_data(chip);
|
2014-04-24 20:56:06 -04:00
|
|
|
struct uart_port *port = &s->p[0].port;
|
2016-10-25 13:24:13 +01:00
|
|
|
u8 state = sc16is7xx_port_read(port, SC16IS7XX_IOSTATE_REG);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2016-10-25 13:24:13 +01:00
|
|
|
if (val)
|
|
|
|
state |= BIT(offset);
|
|
|
|
else
|
|
|
|
state &= ~BIT(offset);
|
2023-08-07 17:45:55 -04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2014-04-24 20:56:06 -04:00
|
|
|
sc16is7xx_port_update(port, SC16IS7XX_IODIR_REG, BIT(offset),
|
|
|
|
BIT(offset));
|
2023-08-07 17:45:55 -04:00
|
|
|
sc16is7xx_port_write(port, SC16IS7XX_IOSTATE_REG, state);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
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;
|
2025-04-08 09:42:01 +02:00
|
|
|
s->gpio.set_rv = sc16is7xx_gpio_set;
|
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);
|
|
|
|
}
|
2014-04-24 20:56:06 -04:00
|
|
|
#endif
|
|
|
|
|
2023-09-27 12:01:52 -04:00
|
|
|
static void sc16is7xx_setup_irda_ports(struct sc16is7xx_port *s)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int ret;
|
|
|
|
int count;
|
2023-12-21 18:18:17 -05:00
|
|
|
u32 irda_port[SC16IS7XX_MAX_PORTS];
|
2023-09-27 12:01:52 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-07 17:45:54 -04:00
|
|
|
/*
|
|
|
|
* Configure ports designated to operate as modem control lines.
|
|
|
|
*/
|
2023-12-11 12:13:49 -05:00
|
|
|
static int sc16is7xx_setup_mctrl_ports(struct sc16is7xx_port *s,
|
|
|
|
struct regmap *regmap)
|
2023-08-07 17:45:54 -04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int ret;
|
|
|
|
int count;
|
2023-12-21 18:18:17 -05:00
|
|
|
u32 mctrl_port[SC16IS7XX_MAX_PORTS];
|
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(
|
2023-12-11 12:13:49 -05:00
|
|
|
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,
|
2023-08-07 17:45:54 -04:00
|
|
|
SC16IS7XX_IOCONTROL_MODEM_A_BIT |
|
|
|
|
SC16IS7XX_IOCONTROL_MODEM_B_BIT, s->mctrl_mask);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-06-06 13:04:17 +03:00
|
|
|
static const struct serial_rs485 sc16is7xx_rs485_supported = {
|
2024-10-07 12:27:15 -04:00
|
|
|
.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND,
|
2022-06-06 13:04:17 +03:00
|
|
|
.delay_rts_before_send = 1,
|
|
|
|
.delay_rts_after_send = 1, /* Not supported but keep returning -EINVAL */
|
|
|
|
};
|
|
|
|
|
2024-06-18 19:26:20 +08:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2024-04-09 11:42:51 -04:00
|
|
|
int sc16is7xx_probe(struct device *dev, const struct sc16is7xx_devtype *devtype,
|
|
|
|
struct regmap *regmaps[], int irq)
|
2014-04-24 20:56:06 -04:00
|
|
|
{
|
2019-03-18 12:29:15 +02:00
|
|
|
unsigned long freq = 0, *pfreq = dev_get_platdata(dev);
|
2020-05-21 11:11:52 +02:00
|
|
|
unsigned int val;
|
2019-03-18 12:29:15 +02:00
|
|
|
u32 uartclk = 0;
|
2014-04-24 20:56:06 -04:00
|
|
|
int i, ret;
|
|
|
|
struct sc16is7xx_port *s;
|
2024-04-09 11:42:52 -04:00
|
|
|
bool port_registered[SC16IS7XX_MAX_PORTS];
|
2014-04-24 20:56:06 -04:00
|
|
|
|
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]);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2020-05-21 11:11:52 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
2023-12-11 12:13:49 -05:00
|
|
|
*
|
|
|
|
* 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.
|
2020-05-21 11:11:52 +02:00
|
|
|
*/
|
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);
|
2020-05-21 11:11:52 +02:00
|
|
|
if (ret < 0)
|
2021-03-29 20:10:49 +00:00
|
|
|
return -EPROBE_DEFER;
|
2020-05-21 11:11:52 +02:00
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
/* Alloc port structure */
|
2019-01-04 11:48:19 -06:00
|
|
|
s = devm_kzalloc(dev, struct_size(s, p, devtype->nr_uart), GFP_KERNEL);
|
2014-04-24 20:56:06 -04:00
|
|
|
if (!s) {
|
|
|
|
dev_err(dev, "Error allocating port structure\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2019-03-18 12:29:15 +02:00
|
|
|
/* Always ask for fixed clock rate from a property. */
|
|
|
|
device_property_read_u32(dev, "clock-frequency", &uartclk);
|
|
|
|
|
2025-01-21 08:18:19 +01:00
|
|
|
s->polling = (irq <= 0);
|
2025-01-10 08:31:04 +01:00
|
|
|
if (s->polling)
|
|
|
|
dev_dbg(dev,
|
|
|
|
"No interrupt pin definition, falling back to polling mode\n");
|
|
|
|
|
2021-05-17 20:34:15 +03:00
|
|
|
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) {
|
2019-03-18 12:29:15 +02:00
|
|
|
if (uartclk)
|
|
|
|
freq = uartclk;
|
2014-04-24 20:56:06 -04:00
|
|
|
if (pfreq)
|
|
|
|
freq = *pfreq;
|
2019-03-18 12:29:15 +02:00
|
|
|
if (freq)
|
|
|
|
dev_dbg(dev, "Clock frequency: %luHz\n", freq);
|
2014-04-24 20:56:06 -04:00
|
|
|
else
|
2021-05-17 20:34:15 +03:00
|
|
|
return -EINVAL;
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
s->devtype = devtype;
|
|
|
|
dev_set_drvdata(dev, s);
|
|
|
|
|
2016-10-11 13:55:20 -07:00
|
|
|
kthread_init_worker(&s->kworker);
|
2015-05-29 21:20:29 +02:00
|
|
|
s->kworker_task = kthread_run(kthread_worker_fn, &s->kworker,
|
|
|
|
"sc16is7xx");
|
|
|
|
if (IS_ERR(s->kworker_task)) {
|
|
|
|
ret = PTR_ERR(s->kworker_task);
|
2015-07-31 14:44:23 +02:00
|
|
|
goto out_clk;
|
2015-05-29 21:20:29 +02:00
|
|
|
}
|
2020-04-21 12:09:13 +02:00
|
|
|
sched_set_fifo(s->kworker_task);
|
2015-05-29 21:20:29 +02:00
|
|
|
|
2024-06-18 19:26:20 +08:00
|
|
|
ret = sc16is7xx_reset(dev, regmaps[0]);
|
|
|
|
if (ret)
|
|
|
|
goto out_kthread;
|
2016-07-19 16:29:36 +02:00
|
|
|
|
2024-04-09 11:42:52 -04:00
|
|
|
/* Mark each port line and status as uninitialised. */
|
2014-04-24 20:56:06 -04:00
|
|
|
for (i = 0; i < devtype->nr_uart; ++i) {
|
2024-04-09 11:42:52 -04:00
|
|
|
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)
|
2023-12-21 18:18:08 -05:00
|
|
|
goto out_ports;
|
2024-04-09 11:42:52 -04:00
|
|
|
|
|
|
|
s->p[i].port.line = ret;
|
2023-12-21 18:18:08 -05:00
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
/* 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;
|
2020-09-01 14:03:29 +02:00
|
|
|
s->p[i].port.iobase = i;
|
2023-08-07 17:45:51 -04:00
|
|
|
/*
|
|
|
|
* 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;
|
2014-04-24 20:56:06 -04:00
|
|
|
s->p[i].port.iotype = UPIO_PORT;
|
|
|
|
s->p[i].port.uartclk = freq;
|
2014-11-06 09:22:54 +01:00
|
|
|
s->p[i].port.rs485_config = sc16is7xx_config_rs485;
|
2022-07-04 12:45:14 +03:00
|
|
|
s->p[i].port.rs485_supported = sc16is7xx_rs485_supported;
|
2014-04-24 20:56:06 -04:00
|
|
|
s->p[i].port.ops = &sc16is7xx_ops;
|
2022-03-01 07:03:31 +01:00
|
|
|
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];
|
2022-03-01 07:03:31 +01:00
|
|
|
|
2023-12-11 12:13:51 -05:00
|
|
|
mutex_init(&s->p[i].efr_lock);
|
|
|
|
|
2023-08-07 17:45:56 -04:00
|
|
|
ret = uart_get_rs485_mode(&s->p[i].port);
|
|
|
|
if (ret)
|
|
|
|
goto out_ports;
|
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
/* 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);
|
2022-03-01 07:03:31 +01:00
|
|
|
|
2015-05-29 21:20:31 +02:00
|
|
|
/* Initialize kthread work structs */
|
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);
|
2022-03-01 07:03:31 +01:00
|
|
|
kthread_init_delayed_work(&s->p[i].ms_work, sc16is7xx_ms_proc);
|
2023-12-21 18:18:08 -05:00
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
/* Register port */
|
2023-12-21 18:18:08 -05:00
|
|
|
ret = uart_add_one_port(&sc16is7xx_uart, &s->p[i].port);
|
|
|
|
if (ret)
|
|
|
|
goto out_ports;
|
|
|
|
|
2024-04-09 11:42:52 -04:00
|
|
|
port_registered[i] = true;
|
2016-07-19 16:29:36 +02:00
|
|
|
|
|
|
|
/* 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);
|
2016-07-19 16:29:36 +02:00
|
|
|
|
|
|
|
/* 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);
|
2016-07-19 16:29:36 +02:00
|
|
|
|
|
|
|
/* Restore access to general registers */
|
|
|
|
sc16is7xx_port_write(&s->p[i].port, SC16IS7XX_LCR_REG, 0x00);
|
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
/* Go to suspend mode */
|
|
|
|
sc16is7xx_power(&s->p[i].port, 0);
|
|
|
|
}
|
|
|
|
|
2023-09-27 12:01:52 -04:00
|
|
|
sc16is7xx_setup_irda_ports(s);
|
2020-05-29 07:50:58 +02:00
|
|
|
|
2023-12-11 12:13:49 -05:00
|
|
|
ret = sc16is7xx_setup_mctrl_ports(s, regmaps[0]);
|
2023-08-07 17:45:54 -04:00
|
|
|
if (ret)
|
|
|
|
goto out_ports;
|
|
|
|
|
2022-11-30 11:55:30 +01:00
|
|
|
#ifdef CONFIG_GPIOLIB
|
2023-08-07 17:45:54 -04:00
|
|
|
ret = sc16is7xx_setup_gpio_chip(s);
|
|
|
|
if (ret)
|
|
|
|
goto out_ports;
|
2022-11-30 11:55:30 +01:00
|
|
|
#endif
|
|
|
|
|
2025-01-10 08:31:04 +01:00
|
|
|
if (s->polling) {
|
|
|
|
/* Initialize kernel thread for polling */
|
|
|
|
kthread_init_delayed_work(&s->poll_work, sc16is7xx_poll_proc);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-21 11:11:51 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2020-05-21 11:11:50 +02:00
|
|
|
ret = devm_request_threaded_irq(dev, irq, NULL, sc16is7xx_irq,
|
|
|
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
|
|
|
dev_name(dev), s);
|
2014-04-24 20:56:06 -04:00
|
|
|
if (!ret)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#ifdef CONFIG_GPIOLIB
|
2023-08-07 17:45:54 -04:00
|
|
|
if (s->gpio_valid_mask)
|
2014-09-16 15:14:46 -07:00
|
|
|
gpiochip_remove(&s->gpio);
|
2014-04-24 20:56:06 -04:00
|
|
|
#endif
|
2022-11-30 11:55:30 +01:00
|
|
|
|
|
|
|
out_ports:
|
2024-04-09 11:42:52 -04:00
|
|
|
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])
|
2023-12-21 18:18:08 -05:00
|
|
|
uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
|
2024-04-09 11:42:52 -04:00
|
|
|
}
|
2022-11-30 11:55:30 +01:00
|
|
|
|
2024-06-18 19:26:20 +08:00
|
|
|
out_kthread:
|
2015-05-29 21:20:29 +02:00
|
|
|
kthread_stop(s->kworker_task);
|
|
|
|
|
2014-04-24 20:56:06 -04:00
|
|
|
out_clk:
|
2021-05-17 20:34:15 +03:00
|
|
|
clk_disable_unprepare(s->clk);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2024-04-09 11:42:51 -04:00
|
|
|
EXPORT_SYMBOL_GPL(sc16is7xx_probe);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2024-04-09 11:42:51 -04:00
|
|
|
void sc16is7xx_remove(struct device *dev)
|
2014-04-24 20:56:06 -04:00
|
|
|
{
|
|
|
|
struct sc16is7xx_port *s = dev_get_drvdata(dev);
|
2014-09-16 15:14:46 -07:00
|
|
|
int i;
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
#ifdef CONFIG_GPIOLIB
|
2023-08-07 17:45:54 -04:00
|
|
|
if (s->gpio_valid_mask)
|
2014-09-16 15:14:46 -07:00
|
|
|
gpiochip_remove(&s->gpio);
|
2014-04-24 20:56:06 -04:00
|
|
|
#endif
|
|
|
|
|
2015-07-31 14:44:23 +02:00
|
|
|
for (i = 0; i < s->devtype->nr_uart; i++) {
|
2022-03-01 07:03:31 +01:00
|
|
|
kthread_cancel_delayed_work_sync(&s->p[i].ms_work);
|
2024-04-09 11:42:52 -04:00
|
|
|
ida_free(&sc16is7xx_lines, s->p[i].port.line);
|
2024-04-09 11:42:50 -04:00
|
|
|
uart_remove_one_port(&sc16is7xx_uart, &s->p[i].port);
|
2014-04-24 20:56:06 -04:00
|
|
|
sc16is7xx_power(&s->p[i].port, 0);
|
|
|
|
}
|
|
|
|
|
2025-01-10 08:31:04 +01:00
|
|
|
if (s->polling)
|
|
|
|
kthread_cancel_delayed_work_sync(&s->poll_work);
|
|
|
|
|
2016-10-11 13:55:20 -07:00
|
|
|
kthread_flush_worker(&s->kworker);
|
2015-05-29 21:20:29 +02:00
|
|
|
kthread_stop(s->kworker_task);
|
|
|
|
|
2021-05-17 20:34:15 +03:00
|
|
|
clk_disable_unprepare(s->clk);
|
2014-04-24 20:56:06 -04:00
|
|
|
}
|
2024-04-09 11:42:51 -04:00
|
|
|
EXPORT_SYMBOL_GPL(sc16is7xx_remove);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2024-04-09 11:42:51 -04:00
|
|
|
const struct of_device_id __maybe_unused sc16is7xx_dt_ids[] = {
|
2014-04-24 20:56:06 -04:00
|
|
|
{ .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, },
|
|
|
|
{ }
|
|
|
|
};
|
2024-04-09 11:42:51 -04:00
|
|
|
EXPORT_SYMBOL_GPL(sc16is7xx_dt_ids);
|
2014-04-24 20:56:06 -04:00
|
|
|
MODULE_DEVICE_TABLE(of, sc16is7xx_dt_ids);
|
|
|
|
|
2024-04-09 11:42:53 -04:00
|
|
|
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,
|
2014-04-24 20:56:06 -04:00
|
|
|
.val_bits = 8,
|
2024-03-18 14:42:16 +08:00
|
|
|
.cache_type = REGCACHE_MAPLE,
|
2014-04-24 20:56:06 -04:00
|
|
|
.volatile_reg = sc16is7xx_regmap_volatile,
|
|
|
|
.precious_reg = sc16is7xx_regmap_precious,
|
2023-12-11 12:13:52 -05:00
|
|
|
.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,
|
2014-04-24 20:56:06 -04:00
|
|
|
};
|
2024-04-09 11:42:51 -04:00
|
|
|
EXPORT_SYMBOL_GPL(sc16is7xx_regcfg);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
2024-04-09 11:42:51 -04:00
|
|
|
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
|
|
|
{
|
2023-12-11 12:13:48 -05: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
|
|
|
}
|
2024-04-09 11:42:51 -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
|
|
|
|
2024-04-09 11:42:51 -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;
|
|
|
|
}
|
2024-04-09 11:42:51 -04:00
|
|
|
EXPORT_SYMBOL_GPL(sc16is7xx_regmap_port_mask);
|
2015-05-25 11:51:09 +05:30
|
|
|
|
|
|
|
static int __init sc16is7xx_init(void)
|
|
|
|
{
|
2024-04-09 11:42:51 -04:00
|
|
|
return uart_register_driver(&sc16is7xx_uart);
|
2015-05-25 11:51:09 +05:30
|
|
|
}
|
|
|
|
module_init(sc16is7xx_init);
|
|
|
|
|
|
|
|
static void __exit sc16is7xx_exit(void)
|
|
|
|
{
|
2015-07-31 14:44:23 +02:00
|
|
|
uart_unregister_driver(&sc16is7xx_uart);
|
2015-05-25 11:51:09 +05:30
|
|
|
}
|
|
|
|
module_exit(sc16is7xx_exit);
|
2014-04-24 20:56:06 -04:00
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_AUTHOR("Jon Ringle <jringle@gridpoint.com>");
|
2024-04-09 11:42:51 -04:00
|
|
|
MODULE_DESCRIPTION("SC16IS7xx tty serial core driver");
|