18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Serial Port driver for Aspeed VUART device 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp. 68c2ecf20Sopenharmony_ci * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of_address.h> 118c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 128c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 138c2ecf20Sopenharmony_ci#include <linux/regmap.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 158c2ecf20Sopenharmony_ci#include <linux/tty.h> 168c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 178c2ecf20Sopenharmony_ci#include <linux/clk.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "8250.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define ASPEED_VUART_GCRA 0x20 228c2ecf20Sopenharmony_ci#define ASPEED_VUART_GCRA_VUART_EN BIT(0) 238c2ecf20Sopenharmony_ci#define ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY BIT(1) 248c2ecf20Sopenharmony_ci#define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5) 258c2ecf20Sopenharmony_ci#define ASPEED_VUART_GCRB 0x24 268c2ecf20Sopenharmony_ci#define ASPEED_VUART_GCRB_HOST_SIRQ_MASK GENMASK(7, 4) 278c2ecf20Sopenharmony_ci#define ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT 4 288c2ecf20Sopenharmony_ci#define ASPEED_VUART_ADDRL 0x28 298c2ecf20Sopenharmony_ci#define ASPEED_VUART_ADDRH 0x2c 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct aspeed_vuart { 328c2ecf20Sopenharmony_ci struct device *dev; 338c2ecf20Sopenharmony_ci void __iomem *regs; 348c2ecf20Sopenharmony_ci struct clk *clk; 358c2ecf20Sopenharmony_ci int line; 368c2ecf20Sopenharmony_ci struct timer_list unthrottle_timer; 378c2ecf20Sopenharmony_ci struct uart_8250_port *port; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * If we fill the tty flip buffers, we throttle the data ready interrupt 428c2ecf20Sopenharmony_ci * to prevent dropped characters. This timeout defines how long we wait 438c2ecf20Sopenharmony_ci * to (conditionally, depending on buffer state) unthrottle. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistatic const int unthrottle_timeout = HZ/10; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* 488c2ecf20Sopenharmony_ci * The VUART is basically two UART 'front ends' connected by their FIFO 498c2ecf20Sopenharmony_ci * (no actual serial line in between). One is on the BMC side (management 508c2ecf20Sopenharmony_ci * controller) and one is on the host CPU side. 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * It allows the BMC to provide to the host a "UART" that pipes into 538c2ecf20Sopenharmony_ci * the BMC itself and can then be turned by the BMC into a network console 548c2ecf20Sopenharmony_ci * of some sort for example. 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * This driver is for the BMC side. The sysfs files allow the BMC 578c2ecf20Sopenharmony_ci * userspace which owns the system configuration policy, to specify 588c2ecf20Sopenharmony_ci * at what IO port and interrupt number the host side will appear 598c2ecf20Sopenharmony_ci * to the host on the Host <-> BMC LPC bus. It could be different on a 608c2ecf20Sopenharmony_ci * different system (though most of them use 3f8/4). 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic ssize_t lpc_address_show(struct device *dev, 648c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart = dev_get_drvdata(dev); 678c2ecf20Sopenharmony_ci u16 addr; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci addr = (readb(vuart->regs + ASPEED_VUART_ADDRH) << 8) | 708c2ecf20Sopenharmony_ci (readb(vuart->regs + ASPEED_VUART_ADDRL)); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE - 1, "0x%x\n", addr); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic ssize_t lpc_address_store(struct device *dev, 768c2ecf20Sopenharmony_ci struct device_attribute *attr, 778c2ecf20Sopenharmony_ci const char *buf, size_t count) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart = dev_get_drvdata(dev); 808c2ecf20Sopenharmony_ci unsigned long val; 818c2ecf20Sopenharmony_ci int err; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci err = kstrtoul(buf, 0, &val); 848c2ecf20Sopenharmony_ci if (err) 858c2ecf20Sopenharmony_ci return err; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci writeb(val >> 8, vuart->regs + ASPEED_VUART_ADDRH); 888c2ecf20Sopenharmony_ci writeb(val >> 0, vuart->regs + ASPEED_VUART_ADDRL); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return count; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(lpc_address); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic ssize_t sirq_show(struct device *dev, 968c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart = dev_get_drvdata(dev); 998c2ecf20Sopenharmony_ci u8 reg; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci reg = readb(vuart->regs + ASPEED_VUART_GCRB); 1028c2ecf20Sopenharmony_ci reg &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; 1038c2ecf20Sopenharmony_ci reg >>= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic ssize_t sirq_store(struct device *dev, struct device_attribute *attr, 1098c2ecf20Sopenharmony_ci const char *buf, size_t count) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart = dev_get_drvdata(dev); 1128c2ecf20Sopenharmony_ci unsigned long val; 1138c2ecf20Sopenharmony_ci int err; 1148c2ecf20Sopenharmony_ci u8 reg; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci err = kstrtoul(buf, 0, &val); 1178c2ecf20Sopenharmony_ci if (err) 1188c2ecf20Sopenharmony_ci return err; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci val <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT; 1218c2ecf20Sopenharmony_ci val &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci reg = readb(vuart->regs + ASPEED_VUART_GCRB); 1248c2ecf20Sopenharmony_ci reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK; 1258c2ecf20Sopenharmony_ci reg |= val; 1268c2ecf20Sopenharmony_ci writeb(reg, vuart->regs + ASPEED_VUART_GCRB); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return count; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(sirq); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic ssize_t sirq_polarity_show(struct device *dev, 1348c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart = dev_get_drvdata(dev); 1378c2ecf20Sopenharmony_ci u8 reg; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci reg = readb(vuart->regs + ASPEED_VUART_GCRA); 1408c2ecf20Sopenharmony_ci reg &= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE - 1, "%u\n", reg ? 1 : 0); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void aspeed_vuart_set_sirq_polarity(struct aspeed_vuart *vuart, 1468c2ecf20Sopenharmony_ci bool polarity) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci u8 reg = readb(vuart->regs + ASPEED_VUART_GCRA); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (polarity) 1518c2ecf20Sopenharmony_ci reg |= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY; 1528c2ecf20Sopenharmony_ci else 1538c2ecf20Sopenharmony_ci reg &= ~ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci writeb(reg, vuart->regs + ASPEED_VUART_GCRA); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic ssize_t sirq_polarity_store(struct device *dev, 1598c2ecf20Sopenharmony_ci struct device_attribute *attr, 1608c2ecf20Sopenharmony_ci const char *buf, size_t count) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart = dev_get_drvdata(dev); 1638c2ecf20Sopenharmony_ci unsigned long val; 1648c2ecf20Sopenharmony_ci int err; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci err = kstrtoul(buf, 0, &val); 1678c2ecf20Sopenharmony_ci if (err) 1688c2ecf20Sopenharmony_ci return err; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci aspeed_vuart_set_sirq_polarity(vuart, val != 0); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return count; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(sirq_polarity); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic struct attribute *aspeed_vuart_attrs[] = { 1788c2ecf20Sopenharmony_ci &dev_attr_sirq.attr, 1798c2ecf20Sopenharmony_ci &dev_attr_sirq_polarity.attr, 1808c2ecf20Sopenharmony_ci &dev_attr_lpc_address.attr, 1818c2ecf20Sopenharmony_ci NULL, 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic const struct attribute_group aspeed_vuart_attr_group = { 1858c2ecf20Sopenharmony_ci .attrs = aspeed_vuart_attrs, 1868c2ecf20Sopenharmony_ci}; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void aspeed_vuart_set_enabled(struct aspeed_vuart *vuart, bool enabled) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci u8 reg = readb(vuart->regs + ASPEED_VUART_GCRA); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (enabled) 1938c2ecf20Sopenharmony_ci reg |= ASPEED_VUART_GCRA_VUART_EN; 1948c2ecf20Sopenharmony_ci else 1958c2ecf20Sopenharmony_ci reg &= ~ASPEED_VUART_GCRA_VUART_EN; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci writeb(reg, vuart->regs + ASPEED_VUART_GCRA); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void aspeed_vuart_set_host_tx_discard(struct aspeed_vuart *vuart, 2018c2ecf20Sopenharmony_ci bool discard) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci u8 reg; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci reg = readb(vuart->regs + ASPEED_VUART_GCRA); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* If the DISABLE_HOST_TX_DISCARD bit is set, discard is disabled */ 2088c2ecf20Sopenharmony_ci if (!discard) 2098c2ecf20Sopenharmony_ci reg |= ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD; 2108c2ecf20Sopenharmony_ci else 2118c2ecf20Sopenharmony_ci reg &= ~ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci writeb(reg, vuart->regs + ASPEED_VUART_GCRA); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int aspeed_vuart_startup(struct uart_port *uart_port) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port); 2198c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart = uart_8250_port->port.private_data; 2208c2ecf20Sopenharmony_ci int rc; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci rc = serial8250_do_startup(uart_port); 2238c2ecf20Sopenharmony_ci if (rc) 2248c2ecf20Sopenharmony_ci return rc; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci aspeed_vuart_set_host_tx_discard(vuart, false); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic void aspeed_vuart_shutdown(struct uart_port *uart_port) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port); 2348c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart = uart_8250_port->port.private_data; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci aspeed_vuart_set_host_tx_discard(vuart, true); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci serial8250_do_shutdown(uart_port); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void __aspeed_vuart_set_throttle(struct uart_8250_port *up, 2428c2ecf20Sopenharmony_ci bool throttle) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci unsigned char irqs = UART_IER_RLSI | UART_IER_RDI; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci up->ier &= ~irqs; 2478c2ecf20Sopenharmony_ci if (!throttle) 2488c2ecf20Sopenharmony_ci up->ier |= irqs; 2498c2ecf20Sopenharmony_ci serial_out(up, UART_IER, up->ier); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_cistatic void aspeed_vuart_set_throttle(struct uart_port *port, bool throttle) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct uart_8250_port *up = up_to_u8250p(port); 2548c2ecf20Sopenharmony_ci unsigned long flags; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 2578c2ecf20Sopenharmony_ci __aspeed_vuart_set_throttle(up, throttle); 2588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&port->lock, flags); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic void aspeed_vuart_throttle(struct uart_port *port) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci aspeed_vuart_set_throttle(port, true); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic void aspeed_vuart_unthrottle(struct uart_port *port) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci aspeed_vuart_set_throttle(port, false); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void aspeed_vuart_unthrottle_exp(struct timer_list *timer) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart = from_timer(vuart, timer, unthrottle_timer); 2748c2ecf20Sopenharmony_ci struct uart_8250_port *up = vuart->port; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (!tty_buffer_space_avail(&up->port.state->port)) { 2778c2ecf20Sopenharmony_ci mod_timer(&vuart->unthrottle_timer, 2788c2ecf20Sopenharmony_ci jiffies + unthrottle_timeout); 2798c2ecf20Sopenharmony_ci return; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci aspeed_vuart_unthrottle(&up->port); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/* 2868c2ecf20Sopenharmony_ci * Custom interrupt handler to manage finer-grained flow control. Although we 2878c2ecf20Sopenharmony_ci * have throttle/unthrottle callbacks, we've seen that the VUART device can 2888c2ecf20Sopenharmony_ci * deliver characters faster than the ldisc has a chance to check buffer space 2898c2ecf20Sopenharmony_ci * against the throttle threshold. This results in dropped characters before 2908c2ecf20Sopenharmony_ci * the throttle. 2918c2ecf20Sopenharmony_ci * 2928c2ecf20Sopenharmony_ci * We do this by checking for flip buffer space before RX. If we have no space, 2938c2ecf20Sopenharmony_ci * throttle now and schedule an unthrottle for later, once the ldisc has had 2948c2ecf20Sopenharmony_ci * a chance to drain the buffers. 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_cistatic int aspeed_vuart_handle_irq(struct uart_port *port) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct uart_8250_port *up = up_to_u8250p(port); 2998c2ecf20Sopenharmony_ci unsigned int iir, lsr; 3008c2ecf20Sopenharmony_ci unsigned long flags; 3018c2ecf20Sopenharmony_ci int space, count; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci iir = serial_port_in(port, UART_IIR); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (iir & UART_IIR_NO_INT) 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci spin_lock_irqsave(&port->lock, flags); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci lsr = serial_port_in(port, UART_LSR); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (lsr & (UART_LSR_DR | UART_LSR_BI)) { 3138c2ecf20Sopenharmony_ci space = tty_buffer_space_avail(&port->state->port); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (!space) { 3168c2ecf20Sopenharmony_ci /* throttle and schedule an unthrottle later */ 3178c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart = port->private_data; 3188c2ecf20Sopenharmony_ci __aspeed_vuart_set_throttle(up, true); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (!timer_pending(&vuart->unthrottle_timer)) { 3218c2ecf20Sopenharmony_ci vuart->port = up; 3228c2ecf20Sopenharmony_ci mod_timer(&vuart->unthrottle_timer, 3238c2ecf20Sopenharmony_ci jiffies + unthrottle_timeout); 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci } else { 3278c2ecf20Sopenharmony_ci count = min(space, 256); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci do { 3308c2ecf20Sopenharmony_ci serial8250_read_char(up, lsr); 3318c2ecf20Sopenharmony_ci lsr = serial_in(up, UART_LSR); 3328c2ecf20Sopenharmony_ci if (--count == 0) 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci } while (lsr & (UART_LSR_DR | UART_LSR_BI)); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci tty_flip_buffer_push(&port->state->port); 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci serial8250_modem_status(up); 3418c2ecf20Sopenharmony_ci if (lsr & UART_LSR_THRE) 3428c2ecf20Sopenharmony_ci serial8250_tx_chars(up); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci uart_unlock_and_check_sysrq(port, flags); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return 1; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic void aspeed_vuart_auto_configure_sirq_polarity( 3508c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart, struct device_node *syscon_np, 3518c2ecf20Sopenharmony_ci u32 reg_offset, u32 reg_mask) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct regmap *regmap; 3548c2ecf20Sopenharmony_ci u32 value; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci regmap = syscon_node_to_regmap(syscon_np); 3578c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) { 3588c2ecf20Sopenharmony_ci dev_warn(vuart->dev, 3598c2ecf20Sopenharmony_ci "could not get regmap for aspeed,sirq-polarity-sense\n"); 3608c2ecf20Sopenharmony_ci return; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci if (regmap_read(regmap, reg_offset, &value)) { 3638c2ecf20Sopenharmony_ci dev_warn(vuart->dev, "could not read hw strap table\n"); 3648c2ecf20Sopenharmony_ci return; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci aspeed_vuart_set_sirq_polarity(vuart, (value & reg_mask) == 0); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int aspeed_vuart_probe(struct platform_device *pdev) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct of_phandle_args sirq_polarity_sense_args; 3738c2ecf20Sopenharmony_ci struct uart_8250_port port; 3748c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart; 3758c2ecf20Sopenharmony_ci struct device_node *np; 3768c2ecf20Sopenharmony_ci struct resource *res; 3778c2ecf20Sopenharmony_ci u32 clk, prop; 3788c2ecf20Sopenharmony_ci int rc; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci np = pdev->dev.of_node; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci vuart = devm_kzalloc(&pdev->dev, sizeof(*vuart), GFP_KERNEL); 3838c2ecf20Sopenharmony_ci if (!vuart) 3848c2ecf20Sopenharmony_ci return -ENOMEM; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci vuart->dev = &pdev->dev; 3878c2ecf20Sopenharmony_ci timer_setup(&vuart->unthrottle_timer, aspeed_vuart_unthrottle_exp, 0); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3908c2ecf20Sopenharmony_ci vuart->regs = devm_ioremap_resource(&pdev->dev, res); 3918c2ecf20Sopenharmony_ci if (IS_ERR(vuart->regs)) 3928c2ecf20Sopenharmony_ci return PTR_ERR(vuart->regs); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci memset(&port, 0, sizeof(port)); 3958c2ecf20Sopenharmony_ci port.port.private_data = vuart; 3968c2ecf20Sopenharmony_ci port.port.membase = vuart->regs; 3978c2ecf20Sopenharmony_ci port.port.mapbase = res->start; 3988c2ecf20Sopenharmony_ci port.port.mapsize = resource_size(res); 3998c2ecf20Sopenharmony_ci port.port.startup = aspeed_vuart_startup; 4008c2ecf20Sopenharmony_ci port.port.shutdown = aspeed_vuart_shutdown; 4018c2ecf20Sopenharmony_ci port.port.throttle = aspeed_vuart_throttle; 4028c2ecf20Sopenharmony_ci port.port.unthrottle = aspeed_vuart_unthrottle; 4038c2ecf20Sopenharmony_ci port.port.status = UPSTAT_SYNC_FIFO; 4048c2ecf20Sopenharmony_ci port.port.dev = &pdev->dev; 4058c2ecf20Sopenharmony_ci port.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); 4068c2ecf20Sopenharmony_ci port.bugs |= UART_BUG_TXRACE; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); 4098c2ecf20Sopenharmony_ci if (rc < 0) 4108c2ecf20Sopenharmony_ci return rc; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "clock-frequency", &clk)) { 4138c2ecf20Sopenharmony_ci vuart->clk = devm_clk_get(&pdev->dev, NULL); 4148c2ecf20Sopenharmony_ci if (IS_ERR(vuart->clk)) { 4158c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 4168c2ecf20Sopenharmony_ci "clk or clock-frequency not defined\n"); 4178c2ecf20Sopenharmony_ci rc = PTR_ERR(vuart->clk); 4188c2ecf20Sopenharmony_ci goto err_sysfs_remove; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci rc = clk_prepare_enable(vuart->clk); 4228c2ecf20Sopenharmony_ci if (rc < 0) 4238c2ecf20Sopenharmony_ci goto err_sysfs_remove; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci clk = clk_get_rate(vuart->clk); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* If current-speed was set, then try not to change it. */ 4298c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "current-speed", &prop) == 0) 4308c2ecf20Sopenharmony_ci port.port.custom_divisor = clk / (16 * prop); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* Check for shifted address mapping */ 4338c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "reg-offset", &prop) == 0) 4348c2ecf20Sopenharmony_ci port.port.mapbase += prop; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* Check for registers offset within the devices address range */ 4378c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "reg-shift", &prop) == 0) 4388c2ecf20Sopenharmony_ci port.port.regshift = prop; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* Check for fifo size */ 4418c2ecf20Sopenharmony_ci if (of_property_read_u32(np, "fifo-size", &prop) == 0) 4428c2ecf20Sopenharmony_ci port.port.fifosize = prop; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Check for a fixed line number */ 4458c2ecf20Sopenharmony_ci rc = of_alias_get_id(np, "serial"); 4468c2ecf20Sopenharmony_ci if (rc >= 0) 4478c2ecf20Sopenharmony_ci port.port.line = rc; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci port.port.irq = irq_of_parse_and_map(np, 0); 4508c2ecf20Sopenharmony_ci port.port.handle_irq = aspeed_vuart_handle_irq; 4518c2ecf20Sopenharmony_ci port.port.iotype = UPIO_MEM; 4528c2ecf20Sopenharmony_ci port.port.type = PORT_16550A; 4538c2ecf20Sopenharmony_ci port.port.uartclk = clk; 4548c2ecf20Sopenharmony_ci port.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF 4558c2ecf20Sopenharmony_ci | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_NO_THRE_TEST; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "no-loopback-test")) 4588c2ecf20Sopenharmony_ci port.port.flags |= UPF_SKIP_TEST; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (port.port.fifosize) 4618c2ecf20Sopenharmony_ci port.capabilities = UART_CAP_FIFO; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "auto-flow-control")) 4648c2ecf20Sopenharmony_ci port.capabilities |= UART_CAP_AFE; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci rc = serial8250_register_8250_port(&port); 4678c2ecf20Sopenharmony_ci if (rc < 0) 4688c2ecf20Sopenharmony_ci goto err_clk_disable; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci vuart->line = rc; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci rc = of_parse_phandle_with_fixed_args( 4738c2ecf20Sopenharmony_ci np, "aspeed,sirq-polarity-sense", 2, 0, 4748c2ecf20Sopenharmony_ci &sirq_polarity_sense_args); 4758c2ecf20Sopenharmony_ci if (rc < 0) { 4768c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, 4778c2ecf20Sopenharmony_ci "aspeed,sirq-polarity-sense property not found\n"); 4788c2ecf20Sopenharmony_ci } else { 4798c2ecf20Sopenharmony_ci aspeed_vuart_auto_configure_sirq_polarity( 4808c2ecf20Sopenharmony_ci vuart, sirq_polarity_sense_args.np, 4818c2ecf20Sopenharmony_ci sirq_polarity_sense_args.args[0], 4828c2ecf20Sopenharmony_ci BIT(sirq_polarity_sense_args.args[1])); 4838c2ecf20Sopenharmony_ci of_node_put(sirq_polarity_sense_args.np); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci aspeed_vuart_set_enabled(vuart, true); 4878c2ecf20Sopenharmony_ci aspeed_vuart_set_host_tx_discard(vuart, true); 4888c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, vuart); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci return 0; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cierr_clk_disable: 4938c2ecf20Sopenharmony_ci clk_disable_unprepare(vuart->clk); 4948c2ecf20Sopenharmony_ci irq_dispose_mapping(port.port.irq); 4958c2ecf20Sopenharmony_cierr_sysfs_remove: 4968c2ecf20Sopenharmony_ci sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); 4978c2ecf20Sopenharmony_ci return rc; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int aspeed_vuart_remove(struct platform_device *pdev) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct aspeed_vuart *vuart = platform_get_drvdata(pdev); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci del_timer_sync(&vuart->unthrottle_timer); 5058c2ecf20Sopenharmony_ci aspeed_vuart_set_enabled(vuart, false); 5068c2ecf20Sopenharmony_ci serial8250_unregister_port(vuart->line); 5078c2ecf20Sopenharmony_ci sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group); 5088c2ecf20Sopenharmony_ci clk_disable_unprepare(vuart->clk); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic const struct of_device_id aspeed_vuart_table[] = { 5148c2ecf20Sopenharmony_ci { .compatible = "aspeed,ast2400-vuart" }, 5158c2ecf20Sopenharmony_ci { .compatible = "aspeed,ast2500-vuart" }, 5168c2ecf20Sopenharmony_ci { }, 5178c2ecf20Sopenharmony_ci}; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic struct platform_driver aspeed_vuart_driver = { 5208c2ecf20Sopenharmony_ci .driver = { 5218c2ecf20Sopenharmony_ci .name = "aspeed-vuart", 5228c2ecf20Sopenharmony_ci .of_match_table = aspeed_vuart_table, 5238c2ecf20Sopenharmony_ci }, 5248c2ecf20Sopenharmony_ci .probe = aspeed_vuart_probe, 5258c2ecf20Sopenharmony_ci .remove = aspeed_vuart_remove, 5268c2ecf20Sopenharmony_ci}; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cimodule_platform_driver(aspeed_vuart_driver); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>"); 5318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Aspeed VUART device"); 533