// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/platform_device.h>
#include <linux/serial_core.h>
#include <linux/tty_flip.h>
#include <linux/of.h>
#include <linux/gpio.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <hwregs/ser_defs.h>
#include "serial_mctrl_gpio.h"
#define DRV_NAME "etraxfs-uart"
#define UART_NR CONFIG_ETRAX_SERIAL_PORTS
#define MODIFY_REG(instance, reg, var) \
do { \
if (REG_RD_INT(ser, instance, reg) != \
REG_TYPE_CONV(int, reg_ser_##reg, var)) \
REG_WR(ser, instance, reg, var); \
} while (0)
struct uart_cris_port {
struct uart_port port;
int initialized;
int irq;
void __iomem *regi_ser;
struct mctrl_gpios *gpios;
int write_ongoing;
};
static struct uart_driver etraxfs_uart_driver;
static struct uart_port *console_port;
static int console_baud = 115200;
static struct uart_cris_port *etraxfs_uart_ports[UART_NR];
static void cris_serial_port_init(struct uart_port *port, int line);
static void etraxfs_uart_stop_rx(struct uart_port *port);
static inline void etraxfs_uart_start_tx_bottom(struct uart_port *port);
#ifdef CONFIG_SERIAL_ETRAXFS_CONSOLE
static void
cris_console_write(struct console *co, const char *s, unsigned int count)
{
struct uart_cris_port *up;
int i;
reg_ser_r_stat_din stat;
reg_ser_rw_tr_dma_en tr_dma_en, old;
up = etraxfs_uart_ports[co->index];
if (!up)
return;
/* Switch to manual mode. */
tr_dma_en = old = REG_RD(ser, up->regi_ser, rw_tr_dma_en);
if (tr_dma_en.en == regk_ser_yes) {
tr_dma_en.en = regk_ser_no;
REG_WR(ser, up->regi_ser, rw_tr_dma_en, tr_dma_en);
}
/* Send data. */
for (i = 0; i < count; i++) {
/* LF -> CRLF */
if (s[i] == '\n') {
do {
stat = REG_RD(ser, up->regi_ser, r_stat_din);
} while (!stat.tr_rdy);
REG_WR_INT(ser, up->regi_ser, rw_dout, '\r');
}
/* Wait until transmitter is ready and send. */
do {
stat = REG_RD(ser, up->regi_ser, r_stat_din);
} while (!stat.tr_rdy);
REG_WR_INT(ser, up->regi_ser, rw_dout, s[i]);
}
/* Restore mode. */
if (tr_dma_en.en != old.en)
REG_WR(ser, up->regi_ser, rw_tr_dma_en, old);
}
static int __init
cris_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = 115200;
int bits = 8;
int parity = 'n';
int flow = 'n';
if (co->index < 0 || co->index >= UART_NR)
co->index = 0;
port = &etraxfs_uart_ports[co->index]->port;
console_port = port;
co->flags |= CON_CONSDEV;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
console_baud = baud;
cris_serial_port_init(port, co->index);
uart_set_options(port, co, baud, parity, bits, flow);
return 0;
}
static struct console cris_console = {
.name = "ttyS",
.write = cris_console_write,
.device = uart_console_device,
.setup = cris_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &etraxfs_uart_driver,
};
#endif /* CONFIG_SERIAL_ETRAXFS_CONSOLE */
static struct uart_driver etraxfs_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "serial",
.dev_name = "ttyS",
.major = TTY_MAJOR,
.minor = 64,
.nr = UART_NR,
#ifdef CONFIG_SERIAL_ETRAXFS_CONSOLE
.cons = &cris_console,
#endif /* CONFIG_SERIAL_ETRAXFS_CONSOLE */
};
static inline int crisv32_serial_get_rts(struct uart_cris_port *up)
{
void __iomem *regi_ser = up->regi_ser;
/*
* Return what the user has controlled rts to or
* what the pin is? (if auto_rts is used it differs during tx)
*/
reg_ser_r_stat_din rstat = REG_RD(ser, regi_ser, r