// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2024, Raspberry Pi Ltd.  All rights reserved.
 */

#include <linux/console.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <soc/bcm2835/raspberrypi-firmware.h>
#include <linux/dma-mapping.h>

#define RPI_FW_UART_RX_FIFO_RD	0xb0
#define RPI_FW_UART_RX_FIFO_WR	0xb4
#define RPI_FW_UART_TX_FIFO_RD	0xb8
#define RPI_FW_UART_TX_FIFO_WR	0xbc

#define RPI_FW_UART_FIFO_SIZE		32
#define RPI_FW_UART_FIFO_SIZE_MASK	(RPI_FW_UART_FIFO_SIZE - 1)

#define RPI_FW_UART_MIN_VERSION	3

struct rpi_fw_uart_params {
	u32 start;
	u32 baud;
	u32 data_bits;
	u32 stop_bits;
	u32 gpio_rx;
	u32 gpio_tx;
	u32 flags;
	u32 fifosize;
	u32 rx_buffer;
	u32 tx_buffer;
	u32 version;
	u32 fifo_reg_base;
};

struct rpi_fw_uart {
	struct uart_driver	driver;
	struct uart_port	port;
	struct rpi_firmware	*firmware;
	struct gpio_desc	*rx_gpiod;
	struct gpio_desc	*tx_gpiod;
	unsigned int		rx_gpio;
	unsigned int		tx_gpio;
	unsigned int		baud;
	unsigned int		data_bits;
	unsigned int		stop_bits;
	unsigned char __iomem	*base;
	size_t			dma_buffer_size;

	struct hrtimer		trigger_start_rx;
	ktime_t			rx_poll_delay;
	void			*rx_buffer;
	dma_addr_t		rx_buffer_dma_addr;
	int			rx_stop;

	void			*tx_buffer;
	dma_addr_t		tx_buffer_dma_addr;
};

static unsigned int rpi_fw_uart_tx_is_full(struct uart_port *port)
{
	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
	u32 rd, wr;

	rd = readl(rfu->base + RPI_FW_UART_TX_FIFO_RD);
	wr = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR);
	return ((wr + 1) & RPI_FW_UART_FIFO_SIZE_MASK) == rd;
}

static unsigned int rpi_fw_uart_tx_is_empty(struct uart_port *port)
{
	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
	u32 rd, wr;

	if (!rfu->tx_buffer)
		return 1;

	rd = readl(rfu->base + RPI_FW_UART_TX_FIFO_RD);
	wr = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR);

	return rd == wr;
}

static unsigned int rpi_fw_uart_rx_is_empty(struct uart_port *port)
{
	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
	u32 rd, wr;

	if (!rfu->rx_buffer)
		return 1;

	rd = readl(rfu->base + RPI_FW_UART_RX_FIFO_RD);
	wr = readl(rfu->base + RPI_FW_UART_RX_FIFO_WR);

	return rd == wr;
}

static unsigned int rpi_fw_uart_tx_empty(struct uart_port *port)
{
	return rpi_fw_uart_tx_is_empty(port) ? TIOCSER_TEMT : 0;
}

static void rpi_fw_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
	/*
	 * No hardware flow control, firmware automatically configures
	 * TX to output high and RX to input low.
	 */
	dev_dbg(port->dev, "%s mctrl %u\n", __func__, mctrl);
}

static unsigned int rpi_fw_uart_get_mctrl(struct uart_port *port)
{
	/* No hardware flow control */
	return TIOCM_CTS;
}

static void rpi_fw_uart_stop(struct uart_port *port)
{
	struct rpi_fw_uart_params msg = {.start = 0};
	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);

	hrtimer_cancel(&rfu->trigger_start_rx);

	if (rpi_firmware_property(rfu->firmware,
				RPI_FIRMWARE_SET_SW_UART,
				&msg, sizeof(msg)))
		dev_warn(port->dev,
			 "Failed to shutdown rpi-fw uart. Firmware not configured?");
}

static void rpi_fw_uart_stop_tx(struct uart_port *port)
{
	/* No supported by the current firmware APIs. */
}

static void rpi_fw_uart_stop_rx(struct uart_port *port)
{
	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);

	rfu->rx_stop = 1;
}

static unsigned int rpi_fw_write(struct uart_port *port, const char *s,
				unsigned int count)
{
	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
	u8 *out = rfu->tx_buffer;
	unsigned int consumed = 0;

	while (consumed < count && !rpi_fw_uart_tx_is_full(port)) {
		u32 wp = readl(rfu->base + RPI_FW_UART_TX_FIFO_WR)
			& RPI_FW_UART_FIFO_SIZE_MASK;
		out[wp] = s[consumed++];
		wp = (wp + 1) & RPI_FW_UART_FIFO_SIZE_MASK;
		writel(wp, rfu->base + RPI_FW_UART_TX_FIFO_WR);
	}
	return consumed;
}

/* Called with port.lock taken */
static void rpi_fw_uart_start_tx(struct uart_port *port)
{
	struct tty_port *tport = &port->state->port;

	for (;;) {
		unsigned int consumed;
		unsigned char *tail;
		unsigned int count;

		count = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, port->fifosize);
		if (!count)
			break;

		consumed = rpi_fw_write(port, tail, count);
		uart_xmit_advance(port, consumed);
	}
	uart_write_wakeup(port);
}

/* Called with port.lock taken */
static void rpi_fw_uart_start_rx(struct uart_port *port)
{
	struct tty_port *tty_port = &port->state->port;
	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
	int count = 0;

	/*
	 * RX is polled, read up to a full buffer of data before trying again
	 * so that this can be interrupted if the firmware is filling the
	 * buffer too fast
	 */
	while (!rpi_fw_uart_rx_is_empty(port) && count < port->fifosize) {
		const u8 *in = rfu->rx_buffer;
		u32 rp = readl(rfu->base + RPI_FW_UART_RX_FIFO_RD)
			& RPI_FW_UART_FIFO_SIZE_MASK;

		tty_insert_flip_char(tty_port, in[rp], TTY_NORMAL);
		rp = (rp + 1) & RPI_FW_UART_FIFO_SIZE_MASK;
		writel(rp, rfu->base + RPI_FW_UART_RX_FIFO_RD);
		count++;
	}
	if (count)
		tty_flip_buffer_push(tty_port);
}

static enum hrtimer_restart rpi_fw_uart_trigger_rx(struct hrtimer *t)
{
	unsigned long flags;
	struct rpi_fw_uart *rfu = container_of(t, struct rpi_fw_uart,
					      trigger_start_rx);

	spin_lock_irqsave(&rfu->port.lock, flags);
	if (rfu->rx_stop) {
		spin_unlock_irqrestore(&rfu->port.lock, flags);
		return HRTIMER_NORESTART;
	}

	rpi_fw_uart_start_rx(&rfu->port);
	spin_unlock_irqrestore(&rfu->port.lock, flags);
	hrtimer_forward_now(t, rfu->rx_poll_delay);
	return HRTIMER_RESTART;
}

static void rpi_fw_uart_break_ctl(struct uart_port *port, int ctl)
{
	dev_dbg(port->dev, "%s ctl %d\n", __func__, ctl);
}

static int rpi_fw_uart_configure(struct uart_port *port)
{
	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);
	struct rpi_fw_uart_params msg;
	unsigned long flags;
	int rc;

	rpi_fw_uart_stop(port);

	memset(&msg, 0, sizeof(msg));
	msg.start = 1;
	msg.gpio_rx = rfu->rx_gpio;
	msg.gpio_tx = rfu->tx_gpio;
	msg.data_bits = rfu->data_bits;
	msg.stop_bits = rfu->stop_bits;
	msg.baud = rfu->baud;
	msg.fifosize = RPI_FW_UART_FIFO_SIZE;
	msg.rx_buffer = (u32) rfu->rx_buffer_dma_addr;
	msg.tx_buffer = (u32) rfu->tx_buffer_dma_addr;

	rfu->rx_poll_delay = ms_to_ktime(50);

	/*
	 * Reconfigures the firmware UART with the new settings. On the first
	 * call retrieve the addresses of the FIFO buffers. The buffers are
	 * allocated at startup and are not de-allocated.
	 * NB rpi_firmware_property can block
	 */
	rc = rpi_firmware_property(rfu->firmware,
				RPI_FIRMWARE_SET_SW_UART,
				&msg, sizeof(msg));
	if (rc)
		goto fail;

	rc = rpi_firmware_property(rfu->firmware,
			RPI_FIRMWARE_GET_SW_UART,
			&msg, sizeof(msg));
	if (rc)
		goto fail;

	dev_dbg(port->dev, "version %08x, reg addr %x\n", msg.version,
		msg.fifo_reg_base);

	dev_dbg(port->dev, "started %d baud %u data %u stop %u rx %u tx %u flags %u fifosize %u\n",
		msg.start, msg.baud, msg.data_bits, msg.stop_bits,
		msg.gpio_rx, msg.gpio_tx, msg.flags, msg.fifosize);

	if (msg.fifosize != port->fifosize) {
		dev_err(port->dev, "Expected fifo size %u actual %u",
				port->fifosize, msg.fifosize);
		rc = -EINVAL;
		goto fail;
	}

	if (!msg.start) {
		dev_err(port->dev, "Firmware service not running\n");
		rc = -EINVAL;
	}

	spin_lock_irqsave(&rfu->port.lock, flags);
	rfu->rx_stop = 0;
	hrtimer_start(&rfu->trigger_start_rx,
		      rfu->rx_poll_delay, HRTIMER_MODE_REL);
	spin_unlock_irqrestore(&rfu->port.lock, flags);
	return 0;
fail:
	dev_err(port->dev, "Failed to configure rpi-fw uart. Firmware not configured?");
	return rc;
}

static void rpi_fw_uart_free_buffers(struct uart_port *port)
{
	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);

	if (rfu->rx_buffer)
		dma_free_coherent(port->dev, rfu->dma_buffer_size,
				rfu->rx_buffer, GFP_ATOMIC);

	if (rfu->tx_buffer)
		dma_free_coherent(port->dev, rfu->dma_buffer_size,
				rfu->tx_buffer, GFP_ATOMIC);

	rfu->rx_buffer = NULL;
	rfu->tx_buffer = NULL;
	rfu->rx_buffer_dma_addr = 0;
	rfu->tx_buffer_dma_addr = 0;
}

static int rpi_fw_uart_alloc_buffers(struct uart_port *port)
{
	struct rpi_fw_uart *rfu = container_of(port, struct rpi_fw_uart, port);

	if (rfu->tx_buffer)
		return 0;

	rfu->dma_buffer_size = PAGE_ALIGN(RPI_FW_UART_FIFO_SIZE);

	rfu->rx_buffer = dma_alloc_coherent(port->dev, rfu->dma_buffer_size,
		&rfu->rx_buffer_dma_addr, GFP_ATOMIC);

	if (!rfu->rx_buffer)
		goto alloc_fail;

	rfu->tx_buffer = dma_alloc_coherent(port->dev, rfu->dma_buffer_size,
		&rfu->tx_buffer_dma_addr, GFP_ATOMIC);

	if (!rfu->tx_buffer)
		goto alloc_fail;

	dev_dbg(port->dev, "alloc-buffers %p %x %p %x\n",
		rfu->rx_buffer, (u32) rfu->rx_buffer_dma_addr,
		rfu->tx_buffer, (u32) rfu->tx_buffer_dma_addr);
	return 0;

alloc_fail:
	dev_err(port->dev, "%s uart buffer allocation failed\n", __func__);
	rpi_fw_uart_free_buffers(port);
	return -ENOMEM;
}

static int rpi_fw_uart_startup(struct uart_port *port)
{
	int rc;

	rc = rpi_fw_uart_alloc_buffers(port);
	if (rc)
		dev_err(port->dev, "Failed to start\n");
	return rc;
}

static void rpi_fw_uart_shutdown(struct uart_port *port)
{
	rpi_fw_uart_stop(port);
	rpi_fw_uart_free_buffers(port);
}

static void rpi_fw_uart_set_termios(struct uart_port *port,
				       struct ktermios *new,
				       const struct ktermios *old)
{
	struct rpi_fw_uart *rfu =
		container_of(port, struct rpi_fw_uart, port);
	rfu->baud = uart_get_baud_rate(port, new, old, 50, 115200);
	rfu->stop_bits = (new->c_cflag & CSTOPB) ? 2 : 1;

	rpi_fw_uart_configure(port);
}

static const struct uart_ops rpi_fw_uart_ops = {
	.tx_empty = rpi_fw_uart_tx_empty,
	.set_mctrl = rpi_fw_uart_set_mctrl,
	.get_mctrl = rpi_fw_uart_get_mctrl,
	.stop_rx = rpi_fw_uart_stop_rx,
	.stop_tx = rpi_fw_uart_stop_tx,
	.start_tx = rpi_fw_uart_start_tx,
	.break_ctl = rpi_fw_uart_break_ctl,
	.startup = rpi_fw_uart_startup,
	.shutdown = rpi_fw_uart_shutdown,
	.set_termios = rpi_fw_uart_set_termios,
};

static int rpi_fw_uart_get_gpio_offset(struct device *dev, const char *name)
{
	struct of_phandle_args of_args = { 0 };
	bool is_bcm28xx;

	/* This really shouldn't fail, given that we have a gpiod */
	if (of_parse_phandle_with_args(dev->of_node, name, "#gpio-cells", 0, &of_args))
		return dev_err_probe(dev, -EINVAL, "can't find gpio declaration\n");

	is_bcm28xx = of_device_is_compatible(of_args.np, "brcm,bcm2835-gpio") ||
		     of_device_is_compatible(of_args.np, "brcm,bcm2711-gpio");
	of_node_put(of_args.np);
	if (!is_bcm28xx || of_args.args_count != 2)
		return dev_err_probe(dev, -EINVAL, "not a BCM28xx gpio\n");

	return of_args.args[0];
}

static int rpi_fw_uart_probe(struct platform_device *pdev)
{
	struct device_node *firmware_node;
	struct device *dev = &pdev->dev;
	struct rpi_firmware *firmware;
	struct uart_port *port;
	struct rpi_fw_uart *rfu;
	struct rpi_fw_uart_params msg;
	int version_major;
	int err;

	dev_dbg(dev, "%s of_node %p\n", __func__, dev->of_node);

	/*
	 * We can be probed either through the an old-fashioned
	 * platform device registration or through a DT node that is a
	 * child of the firmware node. Handle both cases.
	 */
	if (dev->of_node)
		firmware_node = of_parse_phandle(dev->of_node, "firmware", 0);
	else
		firmware_node = of_find_compatible_node(NULL, NULL,
				"raspberrypi,bcm2835-firmware");
	if (!firmware_node) {
		dev_err(dev, "Missing firmware node\n");
		return -ENOENT;
	}

	firmware = devm_rpi_firmware_get(dev, firmware_node);
	of_node_put(firmware_node);
	if (!firmware)
		return -EPROBE_DEFER;

	rfu = devm_kzalloc(dev, sizeof(*rfu), GFP_KERNEL);
	if (!rfu)
		return -ENOMEM;

	rfu->firmware = firmware;

	err = rpi_firmware_property(rfu->firmware, RPI_FIRMWARE_GET_SW_UART,
			&msg, sizeof(msg));
	if (err) {
		dev_err(dev, "VC firmware does not support rpi-fw-uart\n");
		return err;
	}

	version_major = msg.version >> 16;
	if (msg.version < RPI_FW_UART_MIN_VERSION) {
		dev_err(dev, "rpi-fw-uart fw version %d is too old min version %d\n",
				version_major, RPI_FW_UART_MIN_VERSION);
		return -EINVAL;
	}

	rfu->rx_gpiod = devm_gpiod_get(dev, "rx", GPIOD_IN);
	if (IS_ERR(rfu->rx_gpiod))
		return PTR_ERR(rfu->rx_gpiod);

	rfu->tx_gpiod = devm_gpiod_get(dev, "tx", GPIOD_OUT_HIGH);
	if (IS_ERR(rfu->tx_gpiod))
		return PTR_ERR(rfu->tx_gpiod);

	rfu->rx_gpio = rpi_fw_uart_get_gpio_offset(dev, "rx-gpios");
	if (rfu->rx_gpio < 0)
		return rfu->rx_gpio;
	rfu->tx_gpio = rpi_fw_uart_get_gpio_offset(dev, "tx-gpios");
	if (rfu->tx_gpio < 0)
		return rfu->tx_gpio;

	rfu->base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(rfu->base))
		return PTR_ERR(rfu->base);

	/* setup the driver */
	rfu->driver.owner = THIS_MODULE;
	rfu->driver.driver_name = "ttyRFU";
	rfu->driver.dev_name = "ttyRFU";
	rfu->driver.nr = 1;
	rfu->data_bits = 8;

	/* RX is polled */
	hrtimer_init(&rfu->trigger_start_rx, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	rfu->trigger_start_rx.function = rpi_fw_uart_trigger_rx;

	err = uart_register_driver(&rfu->driver);
	if (err) {
		dev_err(dev, "failed to register UART driver: %d\n",
			err);
		return err;
	}

	/* setup the port */
	port = &rfu->port;
	spin_lock_init(&port->lock);
	port->dev = &pdev->dev;
	port->type = PORT_RPI_FW;
	port->ops = &rpi_fw_uart_ops;
	port->fifosize = RPI_FW_UART_FIFO_SIZE;
	port->iotype = UPIO_MEM;
	port->flags = UPF_BOOT_AUTOCONF;
	port->private_data = rfu;

	err = uart_add_one_port(&rfu->driver, port);
	if (err) {
		dev_err(dev, "failed to add UART port: %d\n", err);
		goto unregister_uart;
	}
	platform_set_drvdata(pdev, rfu);

	dev_info(dev, "version %d.%d gpios tx %u rx %u\n",
			msg.version >> 16, msg.version & 0xffff,
			rfu->tx_gpio, rfu->rx_gpio);
	return 0;

unregister_uart:
	uart_unregister_driver(&rfu->driver);

	return err;
}

static void rpi_fw_uart_remove(struct platform_device *pdev)
{
	struct rpi_fw_uart *rfu = platform_get_drvdata(pdev);

	uart_remove_one_port(&rfu->driver, &rfu->port);
	uart_unregister_driver(&rfu->driver);
}

static const struct of_device_id rpi_fw_match[] = {
	{ .compatible = "raspberrypi,firmware-uart" },
	{ }
};
MODULE_DEVICE_TABLE(of, rpi_fw_match);

static struct platform_driver rpi_fw_driver = {
	.driver = {
		.name = "rpi_fw-uart",
		.of_match_table = rpi_fw_match,
	},
	.probe = rpi_fw_uart_probe,
	.remove = rpi_fw_uart_remove,
};
module_platform_driver(rpi_fw_driver);

MODULE_AUTHOR("Tim Gover <tim.gover@rasberrypi.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Raspberry Pi Firmware Software UART driver");
