// SPDX-License-Identifier: GPL-2.0+ /* * Cadence UART driver (found in Xilinx Zynq) * * Copyright (c) 2011 - 2014 Xilinx, Inc. * * This driver has originally been pushed by Xilinx using a Zynq-branding. This * still shows in the naming of this file, the kconfig symbols and some symbols * in the code. */ #include <linux/platform_device.h> #include <linux/serial.h> #include <linux/console.h> #include <linux/serial_core.h> #include <linux/slab.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/clk.h> #include <linux/irq.h> #include <linux/io.h> #include <linux/of.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/delay.h> #include <linux/reset.h> #define CDNS_UART_TTY_NAME … #define CDNS_UART_NAME … #define CDNS_UART_MAJOR … #define CDNS_UART_MINOR … #define CDNS_UART_NR_PORTS … #define CDNS_UART_FIFO_SIZE … #define CDNS_UART_REGISTER_SPACE … #define TX_TIMEOUT … /* Rx Trigger level */ static int rx_trigger_level = …; module_param(rx_trigger_level, uint, 0444); MODULE_PARM_DESC(…) …; /* Rx Timeout */ static int rx_timeout = …; module_param(rx_timeout, uint, 0444); MODULE_PARM_DESC(…) …; /* Register offsets for the UART. */ #define CDNS_UART_CR … #define CDNS_UART_MR … #define CDNS_UART_IER … #define CDNS_UART_IDR … #define CDNS_UART_IMR … #define CDNS_UART_ISR … #define CDNS_UART_BAUDGEN … #define CDNS_UART_RXTOUT … #define CDNS_UART_RXWM … #define CDNS_UART_MODEMCR … #define CDNS_UART_MODEMSR … #define CDNS_UART_SR … #define CDNS_UART_FIFO … #define CDNS_UART_BAUDDIV … #define CDNS_UART_FLOWDEL … #define CDNS_UART_IRRX_PWIDTH … #define CDNS_UART_IRTX_PWIDTH … #define CDNS_UART_TXWM … #define CDNS_UART_RXBS … /* Control Register Bit Definitions */ #define CDNS_UART_CR_STOPBRK … #define CDNS_UART_CR_STARTBRK … #define CDNS_UART_CR_TX_DIS … #define CDNS_UART_CR_TX_EN … #define CDNS_UART_CR_RX_DIS … #define CDNS_UART_CR_RX_EN … #define CDNS_UART_CR_TXRST … #define CDNS_UART_CR_RXRST … #define CDNS_UART_CR_RST_TO … #define CDNS_UART_RXBS_PARITY … #define CDNS_UART_RXBS_FRAMING … #define CDNS_UART_RXBS_BRK … /* * Mode Register: * The mode register (MR) defines the mode of transfer as well as the data * format. If this register is modified during transmission or reception, * data validity cannot be guaranteed. */ #define CDNS_UART_MR_CLKSEL … #define CDNS_UART_MR_CHMODE_L_LOOP … #define CDNS_UART_MR_CHMODE_NORM … #define CDNS_UART_MR_CHMODE_MASK … #define CDNS_UART_MR_STOPMODE_2_BIT … #define CDNS_UART_MR_STOPMODE_1_BIT … #define CDNS_UART_MR_PARITY_NONE … #define CDNS_UART_MR_PARITY_MARK … #define CDNS_UART_MR_PARITY_SPACE … #define CDNS_UART_MR_PARITY_ODD … #define CDNS_UART_MR_PARITY_EVEN … #define CDNS_UART_MR_CHARLEN_6_BIT … #define CDNS_UART_MR_CHARLEN_7_BIT … #define CDNS_UART_MR_CHARLEN_8_BIT … /* * Interrupt Registers: * Interrupt control logic uses the interrupt enable register (IER) and the * interrupt disable register (IDR) to set the value of the bits in the * interrupt mask register (IMR). The IMR determines whether to pass an * interrupt to the interrupt status register (ISR). * Writing a 1 to IER Enables an interrupt, writing a 1 to IDR disables an * interrupt. IMR and ISR are read only, and IER and IDR are write only. * Reading either IER or IDR returns 0x00. * All four registers have the same bit definitions. */ #define CDNS_UART_IXR_TOUT … #define CDNS_UART_IXR_PARITY … #define CDNS_UART_IXR_FRAMING … #define CDNS_UART_IXR_OVERRUN … #define CDNS_UART_IXR_TXFULL … #define CDNS_UART_IXR_TXEMPTY … #define CDNS_UART_ISR_RXEMPTY … #define CDNS_UART_IXR_RXTRIG … #define CDNS_UART_IXR_RXFULL … #define CDNS_UART_IXR_RXEMPTY … #define CDNS_UART_IXR_RXMASK … /* * Do not enable parity error interrupt for the following * reason: When parity error interrupt is enabled, each Rx * parity error always results in 2 events. The first one * being parity error interrupt and the second one with a * proper Rx interrupt with the incoming data. Disabling * parity error interrupt ensures better handling of parity * error events. With this change, for a parity error case, we * get a Rx interrupt with parity error set in ISR register * and we still handle parity errors in the desired way. */ #define CDNS_UART_RX_IRQS … /* Goes in read_status_mask for break detection as the HW doesn't do it*/ #define CDNS_UART_IXR_BRK … #define CDNS_UART_RXBS_SUPPORT … /* * Modem Control register: * The read/write Modem Control register controls the interface with the modem * or data set, or a peripheral device emulating a modem. */ #define CDNS_UART_MODEMCR_FCM … #define CDNS_UART_MODEMCR_RTS … #define CDNS_UART_MODEMCR_DTR … /* * Modem Status register: * The read/write Modem Status register reports the interface with the modem * or data set, or a peripheral device emulating a modem. */ #define CDNS_UART_MODEMSR_DCD … #define CDNS_UART_MODEMSR_RI … #define CDNS_UART_MODEMSR_DSR … #define CDNS_UART_MODEMSR_CTS … /* * Channel Status Register: * The channel status register (CSR) is provided to enable the control logic * to monitor the status of bits in the channel interrupt status register, * even if these are masked out by the interrupt mask register. */ #define CDNS_UART_SR_RXEMPTY … #define CDNS_UART_SR_TXEMPTY … #define CDNS_UART_SR_TXFULL … #define CDNS_UART_SR_RXTRIG … #define CDNS_UART_SR_TACTIVE … /* baud dividers min/max values */ #define CDNS_UART_BDIV_MIN … #define CDNS_UART_BDIV_MAX … #define CDNS_UART_CD_MAX … #define UART_AUTOSUSPEND_TIMEOUT … /** * struct cdns_uart - device data * @port: Pointer to the UART port * @uartclk: Reference clock * @pclk: APB clock * @cdns_uart_driver: Pointer to UART driver * @baud: Current baud rate * @clk_rate_change_nb: Notifier block for clock changes * @quirks: Flags for RXBS support. * @cts_override: Modem control state override * @gpiod_rts: Pointer to the gpio descriptor * @rs485_tx_started: RS485 tx state * @tx_timer: Timer for tx * @rstc: Pointer to the reset control */ struct cdns_uart { … }; struct cdns_platform_data { … }; struct serial_rs485 cdns_rs485_supported = …; #define to_cdns_uart(_nb) … /** * cdns_uart_handle_rx - Handle the received bytes along with Rx errors. * @dev_id: Id of the UART port * @isrstatus: The interrupt status register value as read * Return: None */ static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus) { … } /** * cdns_rts_gpio_enable - Configure RTS/GPIO to high/low * @cdns_uart: Handle to the cdns_uart * @enable: Value to be set to RTS/GPIO */ static void cdns_rts_gpio_enable(struct cdns_uart *cdns_uart, bool enable) { … } /** * cdns_rs485_tx_setup - Tx setup specific to rs485 * @cdns_uart: Handle to the cdns_uart */ static void cdns_rs485_tx_setup(struct cdns_uart *cdns_uart) { … } /** * cdns_rs485_rx_setup - Rx setup specific to rs485 * @cdns_uart: Handle to the cdns_uart */ static void cdns_rs485_rx_setup(struct cdns_uart *cdns_uart) { … } /** * cdns_uart_tx_empty - Check whether TX is empty * @port: Handle to the uart port structure * * Return: TIOCSER_TEMT on success, 0 otherwise */ static unsigned int cdns_uart_tx_empty(struct uart_port *port) { … } /** * cdns_rs485_rx_callback - Timer rx callback handler for rs485. * @t: Handle to the hrtimer structure */ static enum hrtimer_restart cdns_rs485_rx_callback(struct hrtimer *t) { … } /** * cdns_calc_after_tx_delay - calculate delay required for after tx. * @cdns_uart: Handle to the cdns_uart */ static u64 cdns_calc_after_tx_delay(struct cdns_uart *cdns_uart) { … } /** * cdns_uart_handle_tx - Handle the bytes to be transmitted. * @dev_id: Id of the UART port * Return: None */ static void cdns_uart_handle_tx(void *dev_id) { … } /** * cdns_uart_isr - Interrupt handler * @irq: Irq number * @dev_id: Id of the port * * Return: IRQHANDLED */ static irqreturn_t cdns_uart_isr(int irq, void *dev_id) { … } /** * cdns_uart_calc_baud_divs - Calculate baud rate divisors * @clk: UART module input clock * @baud: Desired baud rate * @rbdiv: BDIV value (return value) * @rcd: CD value (return value) * @div8: Value for clk_sel bit in mod (return value) * Return: baud rate, requested baud when possible, or actual baud when there * was too much error, zero if no valid divisors are found. * * Formula to obtain baud rate is * baud_tx/rx rate = clk/CD * (BDIV + 1) * input_clk = (Uart User Defined Clock or Apb Clock) * depends on UCLKEN in MR Reg * clk = input_clk or input_clk/8; * depends on CLKS in MR reg * CD and BDIV depends on values in * baud rate generate register * baud rate clock divisor register */ static unsigned int cdns_uart_calc_baud_divs(unsigned int clk, unsigned int baud, u32 *rbdiv, u32 *rcd, int *div8) { … } /** * cdns_uart_set_baud_rate - Calculate and set the baud rate * @port: Handle to the uart port structure * @baud: Baud rate to set * Return: baud rate, requested baud when possible, or actual baud when there * was too much error, zero if no valid divisors are found. */ static unsigned int cdns_uart_set_baud_rate(struct uart_port *port, unsigned int baud) { … } #ifdef CONFIG_COMMON_CLK /** * cdns_uart_clk_notifier_cb - Clock notifier callback * @nb: Notifier block * @event: Notify event * @data: Notifier data * Return: NOTIFY_OK or NOTIFY_DONE on success, NOTIFY_BAD on error. */ static int cdns_uart_clk_notifier_cb(struct notifier_block *nb, unsigned long event, void *data) { … } #endif /** * cdns_rs485_tx_callback - Timer tx callback handler for rs485. * @t: Handle to the hrtimer structure */ static enum hrtimer_restart cdns_rs485_tx_callback(struct hrtimer *t) { … } /** * cdns_uart_start_tx - Start transmitting bytes * @port: Handle to the uart port structure */ static void cdns_uart_start_tx(struct uart_port *port) { … } /** * cdns_uart_stop_tx - Stop TX * @port: Handle to the uart port structure */ static void cdns_uart_stop_tx(struct uart_port *port) { … } /** * cdns_uart_stop_rx - Stop RX * @port: Handle to the uart port structure */ static void cdns_uart_stop_rx(struct uart_port *port) { … } /** * cdns_uart_break_ctl - Based on the input ctl we have to start or stop * transmitting char breaks * @port: Handle to the uart port structure * @ctl: Value based on which start or stop decision is taken */ static void cdns_uart_break_ctl(struct uart_port *port, int ctl) { … } /** * cdns_uart_set_termios - termios operations, handling data length, parity, * stop bits, flow control, baud rate * @port: Handle to the uart port structure * @termios: Handle to the input termios structure * @old: Values of the previously saved termios structure */ static void cdns_uart_set_termios(struct uart_port *port, struct ktermios *termios, const struct ktermios *old) { … } /** * cdns_uart_startup - Called when an application opens a cdns_uart port * @port: Handle to the uart port structure * * Return: 0 on success, negative errno otherwise */ static int cdns_uart_startup(struct uart_port *port) { … } /** * cdns_uart_shutdown - Called when an application closes a cdns_uart port * @port: Handle to the uart port structure */ static void cdns_uart_shutdown(struct uart_port *port) { … } /** * cdns_uart_type - Set UART type to cdns_uart port * @port: Handle to the uart port structure * * Return: string on success, NULL otherwise */ static const char *cdns_uart_type(struct uart_port *port) { … } /** * cdns_uart_verify_port - Verify the port params * @port: Handle to the uart port structure * @ser: Handle to the structure whose members are compared * * Return: 0 on success, negative errno otherwise. */ static int cdns_uart_verify_port(struct uart_port *port, struct serial_struct *ser) { … } /** * cdns_uart_request_port - Claim the memory region attached to cdns_uart port, * called when the driver adds a cdns_uart port via * uart_add_one_port() * @port: Handle to the uart port structure * * Return: 0 on success, negative errno otherwise. */ static int cdns_uart_request_port(struct uart_port *port) { … } /** * cdns_uart_release_port - Release UART port * @port: Handle to the uart port structure * * Release the memory region attached to a cdns_uart port. Called when the * driver removes a cdns_uart port via uart_remove_one_port(). */ static void cdns_uart_release_port(struct uart_port *port) { … } /** * cdns_uart_config_port - Configure UART port * @port: Handle to the uart port structure * @flags: If any */ static void cdns_uart_config_port(struct uart_port *port, int flags) { … } /** * cdns_uart_get_mctrl - Get the modem control state * @port: Handle to the uart port structure * * Return: the modem control state */ static unsigned int cdns_uart_get_mctrl(struct uart_port *port) { … } static void cdns_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) { … } #ifdef CONFIG_CONSOLE_POLL static int cdns_uart_poll_get_char(struct uart_port *port) { … } static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c) { … } #endif static void cdns_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { … } static const struct uart_ops cdns_uart_ops = …; static struct uart_driver cdns_uart_uart_driver; #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE /** * cdns_uart_console_putchar - write the character to the FIFO buffer * @port: Handle to the uart port structure * @ch: Character to be written */ static void cdns_uart_console_putchar(struct uart_port *port, unsigned char ch) { … } static void cdns_early_write(struct console *con, const char *s, unsigned int n) { … } static int __init cdns_early_console_setup(struct earlycon_device *device, const char *opt) { … } OF_EARLYCON_DECLARE(…); OF_EARLYCON_DECLARE(…); OF_EARLYCON_DECLARE(…); OF_EARLYCON_DECLARE(…); /* Static pointer to console port */ static struct uart_port *console_port; /** * cdns_uart_console_write - perform write operation * @co: Console handle * @s: Pointer to character array * @count: No of characters */ static void cdns_uart_console_write(struct console *co, const char *s, unsigned int count) { … } /** * cdns_uart_console_setup - Initialize the uart to default config * @co: Console handle * @options: Initial settings of uart * * Return: 0 on success, negative errno otherwise. */ static int cdns_uart_console_setup(struct console *co, char *options) { … } static struct console cdns_uart_console = …; #endif /* CONFIG_SERIAL_XILINX_PS_UART_CONSOLE */ #ifdef CONFIG_PM_SLEEP /** * cdns_uart_suspend - suspend event * @device: Pointer to the device structure * * Return: 0 */ static int cdns_uart_suspend(struct device *device) { … } /** * cdns_uart_resume - Resume after a previous suspend * @device: Pointer to the device structure * * Return: 0 */ static int cdns_uart_resume(struct device *device) { … } #endif /* ! CONFIG_PM_SLEEP */ static int __maybe_unused cdns_runtime_suspend(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); struct cdns_uart *cdns_uart = port->private_data; clk_disable(cdns_uart->uartclk); clk_disable(cdns_uart->pclk); return 0; }; static int __maybe_unused cdns_runtime_resume(struct device *dev) { struct uart_port *port = dev_get_drvdata(dev); struct cdns_uart *cdns_uart = port->private_data; int ret; ret = clk_enable(cdns_uart->pclk); if (ret) return ret; ret = clk_enable(cdns_uart->uartclk); if (ret) { clk_disable(cdns_uart->pclk); return ret; } return 0; }; static const struct dev_pm_ops cdns_uart_dev_pm_ops = …; static const struct cdns_platform_data zynqmp_uart_def = …; /* Match table for of_platform binding */ static const struct of_device_id cdns_uart_of_match[] = …; MODULE_DEVICE_TABLE(of, cdns_uart_of_match); /* Temporary variable for storing number of instances */ static int instances; /** * cdns_rs485_config - Called when an application calls TIOCSRS485 ioctl. * @port: Pointer to the uart_port structure * @termios: Pointer to the ktermios structure * @rs485: Pointer to the serial_rs485 structure * * Return: 0 */ static int cdns_rs485_config(struct uart_port *port, struct ktermios *termios, struct serial_rs485 *rs485) { … } /** * cdns_uart_probe - Platform driver probe * @pdev: Pointer to the platform device structure * * Return: 0 on success, negative errno otherwise */ static int cdns_uart_probe(struct platform_device *pdev) { … } /** * cdns_uart_remove - called when the platform driver is unregistered * @pdev: Pointer to the platform device structure */ static void cdns_uart_remove(struct platform_device *pdev) { … } static struct platform_driver cdns_uart_platform_driver = …; static int __init cdns_uart_init(void) { … } static void __exit cdns_uart_exit(void) { … } arch_initcall(cdns_uart_init); module_exit(cdns_uart_exit); MODULE_DESCRIPTION(…) …; MODULE_AUTHOR(…) …; MODULE_LICENSE(…) …;