162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Serial Port driver for Aspeed VUART device
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *    Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp.
662306a36Sopenharmony_ci *    Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/device.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/of_address.h>
1162306a36Sopenharmony_ci#include <linux/of_irq.h>
1262306a36Sopenharmony_ci#include <linux/of_platform.h>
1362306a36Sopenharmony_ci#include <linux/regmap.h>
1462306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1562306a36Sopenharmony_ci#include <linux/tty.h>
1662306a36Sopenharmony_ci#include <linux/tty_flip.h>
1762306a36Sopenharmony_ci#include <linux/clk.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "8250.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define ASPEED_VUART_GCRA		0x20
2262306a36Sopenharmony_ci#define ASPEED_VUART_GCRA_VUART_EN		BIT(0)
2362306a36Sopenharmony_ci#define ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY	BIT(1)
2462306a36Sopenharmony_ci#define ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD BIT(5)
2562306a36Sopenharmony_ci#define ASPEED_VUART_GCRB		0x24
2662306a36Sopenharmony_ci#define ASPEED_VUART_GCRB_HOST_SIRQ_MASK	GENMASK(7, 4)
2762306a36Sopenharmony_ci#define ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT	4
2862306a36Sopenharmony_ci#define ASPEED_VUART_ADDRL		0x28
2962306a36Sopenharmony_ci#define ASPEED_VUART_ADDRH		0x2c
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define ASPEED_VUART_DEFAULT_LPC_ADDR	0x3f8
3262306a36Sopenharmony_ci#define ASPEED_VUART_DEFAULT_SIRQ	4
3362306a36Sopenharmony_ci#define ASPEED_VUART_DEFAULT_SIRQ_POLARITY	IRQ_TYPE_LEVEL_LOW
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct aspeed_vuart {
3662306a36Sopenharmony_ci	struct device		*dev;
3762306a36Sopenharmony_ci	struct clk		*clk;
3862306a36Sopenharmony_ci	int			line;
3962306a36Sopenharmony_ci	struct timer_list	unthrottle_timer;
4062306a36Sopenharmony_ci	struct uart_8250_port	*port;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*
4462306a36Sopenharmony_ci * If we fill the tty flip buffers, we throttle the data ready interrupt
4562306a36Sopenharmony_ci * to prevent dropped characters. This timeout defines how long we wait
4662306a36Sopenharmony_ci * to (conditionally, depending on buffer state) unthrottle.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic const int unthrottle_timeout = HZ/10;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * The VUART is basically two UART 'front ends' connected by their FIFO
5262306a36Sopenharmony_ci * (no actual serial line in between). One is on the BMC side (management
5362306a36Sopenharmony_ci * controller) and one is on the host CPU side.
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * It allows the BMC to provide to the host a "UART" that pipes into
5662306a36Sopenharmony_ci * the BMC itself and can then be turned by the BMC into a network console
5762306a36Sopenharmony_ci * of some sort for example.
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci * This driver is for the BMC side. The sysfs files allow the BMC
6062306a36Sopenharmony_ci * userspace which owns the system configuration policy, to specify
6162306a36Sopenharmony_ci * at what IO port and interrupt number the host side will appear
6262306a36Sopenharmony_ci * to the host on the Host <-> BMC LPC bus. It could be different on a
6362306a36Sopenharmony_ci * different system (though most of them use 3f8/4).
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic inline u8 aspeed_vuart_readb(struct aspeed_vuart *vuart, u8 reg)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	return readb(vuart->port->port.membase + reg);
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic inline void aspeed_vuart_writeb(struct aspeed_vuart *vuart, u8 val, u8 reg)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	writeb(val, vuart->port->port.membase + reg);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic ssize_t lpc_address_show(struct device *dev,
7762306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct aspeed_vuart *vuart = dev_get_drvdata(dev);
8062306a36Sopenharmony_ci	u16 addr;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	addr = (aspeed_vuart_readb(vuart, ASPEED_VUART_ADDRH) << 8) |
8362306a36Sopenharmony_ci		(aspeed_vuart_readb(vuart, ASPEED_VUART_ADDRL));
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return sysfs_emit(buf, "0x%x\n", addr);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int aspeed_vuart_set_lpc_address(struct aspeed_vuart *vuart, u32 addr)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	if (addr > U16_MAX)
9162306a36Sopenharmony_ci		return -EINVAL;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	aspeed_vuart_writeb(vuart, addr >> 8, ASPEED_VUART_ADDRH);
9462306a36Sopenharmony_ci	aspeed_vuart_writeb(vuart, addr >> 0, ASPEED_VUART_ADDRL);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic ssize_t lpc_address_store(struct device *dev,
10062306a36Sopenharmony_ci				 struct device_attribute *attr,
10162306a36Sopenharmony_ci				 const char *buf, size_t count)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct aspeed_vuart *vuart = dev_get_drvdata(dev);
10462306a36Sopenharmony_ci	u32 val;
10562306a36Sopenharmony_ci	int err;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	err = kstrtou32(buf, 0, &val);
10862306a36Sopenharmony_ci	if (err)
10962306a36Sopenharmony_ci		return err;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	err = aspeed_vuart_set_lpc_address(vuart, val);
11262306a36Sopenharmony_ci	return err ? : count;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic DEVICE_ATTR_RW(lpc_address);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic ssize_t sirq_show(struct device *dev,
11862306a36Sopenharmony_ci			 struct device_attribute *attr, char *buf)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct aspeed_vuart *vuart = dev_get_drvdata(dev);
12162306a36Sopenharmony_ci	u8 reg;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRB);
12462306a36Sopenharmony_ci	reg &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK;
12562306a36Sopenharmony_ci	reg >>= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", reg);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int aspeed_vuart_set_sirq(struct aspeed_vuart *vuart, u32 sirq)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	u8 reg;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (sirq > (ASPEED_VUART_GCRB_HOST_SIRQ_MASK >> ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT))
13562306a36Sopenharmony_ci		return -EINVAL;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	sirq <<= ASPEED_VUART_GCRB_HOST_SIRQ_SHIFT;
13862306a36Sopenharmony_ci	sirq &= ASPEED_VUART_GCRB_HOST_SIRQ_MASK;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRB);
14162306a36Sopenharmony_ci	reg &= ~ASPEED_VUART_GCRB_HOST_SIRQ_MASK;
14262306a36Sopenharmony_ci	reg |= sirq;
14362306a36Sopenharmony_ci	aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRB);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return 0;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic ssize_t sirq_store(struct device *dev, struct device_attribute *attr,
14962306a36Sopenharmony_ci			  const char *buf, size_t count)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct aspeed_vuart *vuart = dev_get_drvdata(dev);
15262306a36Sopenharmony_ci	unsigned long val;
15362306a36Sopenharmony_ci	int err;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	err = kstrtoul(buf, 0, &val);
15662306a36Sopenharmony_ci	if (err)
15762306a36Sopenharmony_ci		return err;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	err = aspeed_vuart_set_sirq(vuart, val);
16062306a36Sopenharmony_ci	return err ? : count;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(sirq);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic ssize_t sirq_polarity_show(struct device *dev,
16662306a36Sopenharmony_ci				  struct device_attribute *attr, char *buf)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct aspeed_vuart *vuart = dev_get_drvdata(dev);
16962306a36Sopenharmony_ci	u8 reg;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA);
17262306a36Sopenharmony_ci	reg &= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", reg ? 1 : 0);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void aspeed_vuart_set_sirq_polarity(struct aspeed_vuart *vuart,
17862306a36Sopenharmony_ci					   bool polarity)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	u8 reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (polarity)
18362306a36Sopenharmony_ci		reg |= ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
18462306a36Sopenharmony_ci	else
18562306a36Sopenharmony_ci		reg &= ~ASPEED_VUART_GCRA_HOST_SIRQ_POLARITY;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRA);
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic ssize_t sirq_polarity_store(struct device *dev,
19162306a36Sopenharmony_ci				   struct device_attribute *attr,
19262306a36Sopenharmony_ci				   const char *buf, size_t count)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct aspeed_vuart *vuart = dev_get_drvdata(dev);
19562306a36Sopenharmony_ci	unsigned long val;
19662306a36Sopenharmony_ci	int err;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	err = kstrtoul(buf, 0, &val);
19962306a36Sopenharmony_ci	if (err)
20062306a36Sopenharmony_ci		return err;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	aspeed_vuart_set_sirq_polarity(vuart, val != 0);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return count;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(sirq_polarity);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic struct attribute *aspeed_vuart_attrs[] = {
21062306a36Sopenharmony_ci	&dev_attr_sirq.attr,
21162306a36Sopenharmony_ci	&dev_attr_sirq_polarity.attr,
21262306a36Sopenharmony_ci	&dev_attr_lpc_address.attr,
21362306a36Sopenharmony_ci	NULL,
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic const struct attribute_group aspeed_vuart_attr_group = {
21762306a36Sopenharmony_ci	.attrs = aspeed_vuart_attrs,
21862306a36Sopenharmony_ci};
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic void aspeed_vuart_set_enabled(struct aspeed_vuart *vuart, bool enabled)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	u8 reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (enabled)
22562306a36Sopenharmony_ci		reg |= ASPEED_VUART_GCRA_VUART_EN;
22662306a36Sopenharmony_ci	else
22762306a36Sopenharmony_ci		reg &= ~ASPEED_VUART_GCRA_VUART_EN;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRA);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic void aspeed_vuart_set_host_tx_discard(struct aspeed_vuart *vuart,
23362306a36Sopenharmony_ci					     bool discard)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	u8 reg;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	reg = aspeed_vuart_readb(vuart, ASPEED_VUART_GCRA);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* If the DISABLE_HOST_TX_DISCARD bit is set, discard is disabled */
24062306a36Sopenharmony_ci	if (!discard)
24162306a36Sopenharmony_ci		reg |= ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD;
24262306a36Sopenharmony_ci	else
24362306a36Sopenharmony_ci		reg &= ~ASPEED_VUART_GCRA_DISABLE_HOST_TX_DISCARD;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	aspeed_vuart_writeb(vuart, reg, ASPEED_VUART_GCRA);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic int aspeed_vuart_startup(struct uart_port *uart_port)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port);
25162306a36Sopenharmony_ci	struct aspeed_vuart *vuart = uart_8250_port->port.private_data;
25262306a36Sopenharmony_ci	int rc;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	rc = serial8250_do_startup(uart_port);
25562306a36Sopenharmony_ci	if (rc)
25662306a36Sopenharmony_ci		return rc;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	aspeed_vuart_set_host_tx_discard(vuart, false);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return 0;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic void aspeed_vuart_shutdown(struct uart_port *uart_port)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct uart_8250_port *uart_8250_port = up_to_u8250p(uart_port);
26662306a36Sopenharmony_ci	struct aspeed_vuart *vuart = uart_8250_port->port.private_data;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	aspeed_vuart_set_host_tx_discard(vuart, true);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	serial8250_do_shutdown(uart_port);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void __aspeed_vuart_set_throttle(struct uart_8250_port *up,
27462306a36Sopenharmony_ci		bool throttle)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	unsigned char irqs = UART_IER_RLSI | UART_IER_RDI;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* Port locked to synchronize UART_IER access against the console. */
27962306a36Sopenharmony_ci	lockdep_assert_held_once(&up->port.lock);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	up->ier &= ~irqs;
28262306a36Sopenharmony_ci	if (!throttle)
28362306a36Sopenharmony_ci		up->ier |= irqs;
28462306a36Sopenharmony_ci	serial_out(up, UART_IER, up->ier);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_cistatic void aspeed_vuart_set_throttle(struct uart_port *port, bool throttle)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	struct uart_8250_port *up = up_to_u8250p(port);
28962306a36Sopenharmony_ci	unsigned long flags;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	spin_lock_irqsave(&port->lock, flags);
29262306a36Sopenharmony_ci	__aspeed_vuart_set_throttle(up, throttle);
29362306a36Sopenharmony_ci	spin_unlock_irqrestore(&port->lock, flags);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic void aspeed_vuart_throttle(struct uart_port *port)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	aspeed_vuart_set_throttle(port, true);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic void aspeed_vuart_unthrottle(struct uart_port *port)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	aspeed_vuart_set_throttle(port, false);
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic void aspeed_vuart_unthrottle_exp(struct timer_list *timer)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct aspeed_vuart *vuart = from_timer(vuart, timer, unthrottle_timer);
30962306a36Sopenharmony_ci	struct uart_8250_port *up = vuart->port;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (!tty_buffer_space_avail(&up->port.state->port)) {
31262306a36Sopenharmony_ci		mod_timer(&vuart->unthrottle_timer,
31362306a36Sopenharmony_ci			  jiffies + unthrottle_timeout);
31462306a36Sopenharmony_ci		return;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	aspeed_vuart_unthrottle(&up->port);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci/*
32162306a36Sopenharmony_ci * Custom interrupt handler to manage finer-grained flow control. Although we
32262306a36Sopenharmony_ci * have throttle/unthrottle callbacks, we've seen that the VUART device can
32362306a36Sopenharmony_ci * deliver characters faster than the ldisc has a chance to check buffer space
32462306a36Sopenharmony_ci * against the throttle threshold. This results in dropped characters before
32562306a36Sopenharmony_ci * the throttle.
32662306a36Sopenharmony_ci *
32762306a36Sopenharmony_ci * We do this by checking for flip buffer space before RX. If we have no space,
32862306a36Sopenharmony_ci * throttle now and schedule an unthrottle for later, once the ldisc has had
32962306a36Sopenharmony_ci * a chance to drain the buffers.
33062306a36Sopenharmony_ci */
33162306a36Sopenharmony_cistatic int aspeed_vuart_handle_irq(struct uart_port *port)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct uart_8250_port *up = up_to_u8250p(port);
33462306a36Sopenharmony_ci	unsigned int iir, lsr;
33562306a36Sopenharmony_ci	unsigned long flags;
33662306a36Sopenharmony_ci	unsigned int space, count;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	iir = serial_port_in(port, UART_IIR);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (iir & UART_IIR_NO_INT)
34162306a36Sopenharmony_ci		return 0;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	spin_lock_irqsave(&port->lock, flags);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	lsr = serial_port_in(port, UART_LSR);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (lsr & (UART_LSR_DR | UART_LSR_BI)) {
34862306a36Sopenharmony_ci		space = tty_buffer_space_avail(&port->state->port);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		if (!space) {
35162306a36Sopenharmony_ci			/* throttle and schedule an unthrottle later */
35262306a36Sopenharmony_ci			struct aspeed_vuart *vuart = port->private_data;
35362306a36Sopenharmony_ci			__aspeed_vuart_set_throttle(up, true);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci			if (!timer_pending(&vuart->unthrottle_timer))
35662306a36Sopenharmony_ci				mod_timer(&vuart->unthrottle_timer,
35762306a36Sopenharmony_ci					  jiffies + unthrottle_timeout);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		} else {
36062306a36Sopenharmony_ci			count = min(space, 256U);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci			do {
36362306a36Sopenharmony_ci				serial8250_read_char(up, lsr);
36462306a36Sopenharmony_ci				lsr = serial_in(up, UART_LSR);
36562306a36Sopenharmony_ci				if (--count == 0)
36662306a36Sopenharmony_ci					break;
36762306a36Sopenharmony_ci			} while (lsr & (UART_LSR_DR | UART_LSR_BI));
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci			tty_flip_buffer_push(&port->state->port);
37062306a36Sopenharmony_ci		}
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	serial8250_modem_status(up);
37462306a36Sopenharmony_ci	if (lsr & UART_LSR_THRE)
37562306a36Sopenharmony_ci		serial8250_tx_chars(up);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	uart_unlock_and_check_sysrq_irqrestore(port, flags);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return 1;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void aspeed_vuart_auto_configure_sirq_polarity(
38362306a36Sopenharmony_ci	struct aspeed_vuart *vuart, struct device_node *syscon_np,
38462306a36Sopenharmony_ci	u32 reg_offset, u32 reg_mask)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct regmap *regmap;
38762306a36Sopenharmony_ci	u32 value;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	regmap = syscon_node_to_regmap(syscon_np);
39062306a36Sopenharmony_ci	if (IS_ERR(regmap)) {
39162306a36Sopenharmony_ci		dev_warn(vuart->dev,
39262306a36Sopenharmony_ci			 "could not get regmap for aspeed,sirq-polarity-sense\n");
39362306a36Sopenharmony_ci		return;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci	if (regmap_read(regmap, reg_offset, &value)) {
39662306a36Sopenharmony_ci		dev_warn(vuart->dev, "could not read hw strap table\n");
39762306a36Sopenharmony_ci		return;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	aspeed_vuart_set_sirq_polarity(vuart, (value & reg_mask) == 0);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic int aspeed_vuart_map_irq_polarity(u32 dt)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	switch (dt) {
40662306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_LOW:
40762306a36Sopenharmony_ci		return 0;
40862306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_HIGH:
40962306a36Sopenharmony_ci		return 1;
41062306a36Sopenharmony_ci	default:
41162306a36Sopenharmony_ci		return -EINVAL;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic int aspeed_vuart_probe(struct platform_device *pdev)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct of_phandle_args sirq_polarity_sense_args;
41862306a36Sopenharmony_ci	struct uart_8250_port port;
41962306a36Sopenharmony_ci	struct aspeed_vuart *vuart;
42062306a36Sopenharmony_ci	struct device_node *np;
42162306a36Sopenharmony_ci	struct resource *res;
42262306a36Sopenharmony_ci	u32 clk, prop, sirq[2];
42362306a36Sopenharmony_ci	int rc, sirq_polarity;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	np = pdev->dev.of_node;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	vuart = devm_kzalloc(&pdev->dev, sizeof(*vuart), GFP_KERNEL);
42862306a36Sopenharmony_ci	if (!vuart)
42962306a36Sopenharmony_ci		return -ENOMEM;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	vuart->dev = &pdev->dev;
43262306a36Sopenharmony_ci	timer_setup(&vuart->unthrottle_timer, aspeed_vuart_unthrottle_exp, 0);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
43562306a36Sopenharmony_ci	if (!res)
43662306a36Sopenharmony_ci		return -EINVAL;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	memset(&port, 0, sizeof(port));
43962306a36Sopenharmony_ci	port.port.private_data = vuart;
44062306a36Sopenharmony_ci	port.port.mapbase = res->start;
44162306a36Sopenharmony_ci	port.port.mapsize = resource_size(res);
44262306a36Sopenharmony_ci	port.port.startup = aspeed_vuart_startup;
44362306a36Sopenharmony_ci	port.port.shutdown = aspeed_vuart_shutdown;
44462306a36Sopenharmony_ci	port.port.throttle = aspeed_vuart_throttle;
44562306a36Sopenharmony_ci	port.port.unthrottle = aspeed_vuart_unthrottle;
44662306a36Sopenharmony_ci	port.port.status = UPSTAT_SYNC_FIFO;
44762306a36Sopenharmony_ci	port.port.dev = &pdev->dev;
44862306a36Sopenharmony_ci	port.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE);
44962306a36Sopenharmony_ci	port.bugs |= UART_BUG_TXRACE;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	rc = sysfs_create_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
45262306a36Sopenharmony_ci	if (rc < 0)
45362306a36Sopenharmony_ci		return rc;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (of_property_read_u32(np, "clock-frequency", &clk)) {
45662306a36Sopenharmony_ci		vuart->clk = devm_clk_get(&pdev->dev, NULL);
45762306a36Sopenharmony_ci		if (IS_ERR(vuart->clk)) {
45862306a36Sopenharmony_ci			dev_warn(&pdev->dev,
45962306a36Sopenharmony_ci				"clk or clock-frequency not defined\n");
46062306a36Sopenharmony_ci			rc = PTR_ERR(vuart->clk);
46162306a36Sopenharmony_ci			goto err_sysfs_remove;
46262306a36Sopenharmony_ci		}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		rc = clk_prepare_enable(vuart->clk);
46562306a36Sopenharmony_ci		if (rc < 0)
46662306a36Sopenharmony_ci			goto err_sysfs_remove;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		clk = clk_get_rate(vuart->clk);
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* If current-speed was set, then try not to change it. */
47262306a36Sopenharmony_ci	if (of_property_read_u32(np, "current-speed", &prop) == 0)
47362306a36Sopenharmony_ci		port.port.custom_divisor = clk / (16 * prop);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* Check for shifted address mapping */
47662306a36Sopenharmony_ci	if (of_property_read_u32(np, "reg-offset", &prop) == 0)
47762306a36Sopenharmony_ci		port.port.mapbase += prop;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* Check for registers offset within the devices address range */
48062306a36Sopenharmony_ci	if (of_property_read_u32(np, "reg-shift", &prop) == 0)
48162306a36Sopenharmony_ci		port.port.regshift = prop;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	/* Check for fifo size */
48462306a36Sopenharmony_ci	if (of_property_read_u32(np, "fifo-size", &prop) == 0)
48562306a36Sopenharmony_ci		port.port.fifosize = prop;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	/* Check for a fixed line number */
48862306a36Sopenharmony_ci	rc = of_alias_get_id(np, "serial");
48962306a36Sopenharmony_ci	if (rc >= 0)
49062306a36Sopenharmony_ci		port.port.line = rc;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	port.port.irq = irq_of_parse_and_map(np, 0);
49362306a36Sopenharmony_ci	port.port.handle_irq = aspeed_vuart_handle_irq;
49462306a36Sopenharmony_ci	port.port.iotype = UPIO_MEM;
49562306a36Sopenharmony_ci	port.port.type = PORT_ASPEED_VUART;
49662306a36Sopenharmony_ci	port.port.uartclk = clk;
49762306a36Sopenharmony_ci	port.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP
49862306a36Sopenharmony_ci		| UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_NO_THRE_TEST;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	if (of_property_read_bool(np, "no-loopback-test"))
50162306a36Sopenharmony_ci		port.port.flags |= UPF_SKIP_TEST;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (port.port.fifosize)
50462306a36Sopenharmony_ci		port.capabilities = UART_CAP_FIFO;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (of_property_read_bool(np, "auto-flow-control"))
50762306a36Sopenharmony_ci		port.capabilities |= UART_CAP_AFE;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	rc = serial8250_register_8250_port(&port);
51062306a36Sopenharmony_ci	if (rc < 0)
51162306a36Sopenharmony_ci		goto err_clk_disable;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	vuart->line = rc;
51462306a36Sopenharmony_ci	vuart->port = serial8250_get_port(vuart->line);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	rc = of_parse_phandle_with_fixed_args(
51762306a36Sopenharmony_ci		np, "aspeed,sirq-polarity-sense", 2, 0,
51862306a36Sopenharmony_ci		&sirq_polarity_sense_args);
51962306a36Sopenharmony_ci	if (rc < 0) {
52062306a36Sopenharmony_ci		dev_dbg(&pdev->dev,
52162306a36Sopenharmony_ci			"aspeed,sirq-polarity-sense property not found\n");
52262306a36Sopenharmony_ci	} else {
52362306a36Sopenharmony_ci		aspeed_vuart_auto_configure_sirq_polarity(
52462306a36Sopenharmony_ci			vuart, sirq_polarity_sense_args.np,
52562306a36Sopenharmony_ci			sirq_polarity_sense_args.args[0],
52662306a36Sopenharmony_ci			BIT(sirq_polarity_sense_args.args[1]));
52762306a36Sopenharmony_ci		of_node_put(sirq_polarity_sense_args.np);
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	rc = of_property_read_u32(np, "aspeed,lpc-io-reg", &prop);
53162306a36Sopenharmony_ci	if (rc < 0)
53262306a36Sopenharmony_ci		prop = ASPEED_VUART_DEFAULT_LPC_ADDR;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	rc = aspeed_vuart_set_lpc_address(vuart, prop);
53562306a36Sopenharmony_ci	if (rc < 0) {
53662306a36Sopenharmony_ci		dev_err(&pdev->dev, "invalid value in aspeed,lpc-io-reg property\n");
53762306a36Sopenharmony_ci		goto err_clk_disable;
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", sirq, 2);
54162306a36Sopenharmony_ci	if (rc < 0) {
54262306a36Sopenharmony_ci		sirq[0] = ASPEED_VUART_DEFAULT_SIRQ;
54362306a36Sopenharmony_ci		sirq[1] = ASPEED_VUART_DEFAULT_SIRQ_POLARITY;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	rc = aspeed_vuart_set_sirq(vuart, sirq[0]);
54762306a36Sopenharmony_ci	if (rc < 0) {
54862306a36Sopenharmony_ci		dev_err(&pdev->dev, "invalid sirq number in aspeed,lpc-interrupts property\n");
54962306a36Sopenharmony_ci		goto err_clk_disable;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	sirq_polarity = aspeed_vuart_map_irq_polarity(sirq[1]);
55362306a36Sopenharmony_ci	if (sirq_polarity < 0) {
55462306a36Sopenharmony_ci		dev_err(&pdev->dev, "invalid sirq polarity in aspeed,lpc-interrupts property\n");
55562306a36Sopenharmony_ci		rc = sirq_polarity;
55662306a36Sopenharmony_ci		goto err_clk_disable;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	aspeed_vuart_set_sirq_polarity(vuart, sirq_polarity);
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	aspeed_vuart_set_enabled(vuart, true);
56262306a36Sopenharmony_ci	aspeed_vuart_set_host_tx_discard(vuart, true);
56362306a36Sopenharmony_ci	platform_set_drvdata(pdev, vuart);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cierr_clk_disable:
56862306a36Sopenharmony_ci	clk_disable_unprepare(vuart->clk);
56962306a36Sopenharmony_ci	irq_dispose_mapping(port.port.irq);
57062306a36Sopenharmony_cierr_sysfs_remove:
57162306a36Sopenharmony_ci	sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
57262306a36Sopenharmony_ci	return rc;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic int aspeed_vuart_remove(struct platform_device *pdev)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct aspeed_vuart *vuart = platform_get_drvdata(pdev);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	del_timer_sync(&vuart->unthrottle_timer);
58062306a36Sopenharmony_ci	aspeed_vuart_set_enabled(vuart, false);
58162306a36Sopenharmony_ci	serial8250_unregister_port(vuart->line);
58262306a36Sopenharmony_ci	sysfs_remove_group(&vuart->dev->kobj, &aspeed_vuart_attr_group);
58362306a36Sopenharmony_ci	clk_disable_unprepare(vuart->clk);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	return 0;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic const struct of_device_id aspeed_vuart_table[] = {
58962306a36Sopenharmony_ci	{ .compatible = "aspeed,ast2400-vuart" },
59062306a36Sopenharmony_ci	{ .compatible = "aspeed,ast2500-vuart" },
59162306a36Sopenharmony_ci	{ },
59262306a36Sopenharmony_ci};
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic struct platform_driver aspeed_vuart_driver = {
59562306a36Sopenharmony_ci	.driver = {
59662306a36Sopenharmony_ci		.name = "aspeed-vuart",
59762306a36Sopenharmony_ci		.of_match_table = aspeed_vuart_table,
59862306a36Sopenharmony_ci	},
59962306a36Sopenharmony_ci	.probe = aspeed_vuart_probe,
60062306a36Sopenharmony_ci	.remove = aspeed_vuart_remove,
60162306a36Sopenharmony_ci};
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cimodule_platform_driver(aspeed_vuart_driver);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ciMODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>");
60662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
60762306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Aspeed VUART device");
608