aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/8250/8250.h1
-rw-r--r--drivers/tty/serial/8250/8250_bcm2835aux.c8
-rw-r--r--drivers/tty/serial/8250/8250_core.c15
-rw-r--r--drivers/tty/serial/8250/8250_port.c238
-rw-r--r--drivers/tty/serial/Kconfig11
-rw-r--r--drivers/tty/serial/Makefile1
-rw-r--r--drivers/tty/serial/amba-pl011.c115
-rw-r--r--drivers/tty/serial/rpi-fw-uart.c561
-rw-r--r--drivers/tty/serial/sc16is7xx.c5
9 files changed, 853 insertions, 102 deletions
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index e99f5193d8f1..3dd0b67edb6d 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -92,6 +92,7 @@ struct serial8250_config {
#define UART_BUG_NOMSR BIT(2) /* UART has buggy MSR status bits (Au1x00) */
#define UART_BUG_THRE BIT(3) /* UART has buggy THRE reassertion */
#define UART_BUG_TXRACE BIT(5) /* UART Tx fails to set remote DR */
+#define UART_BUG_NOMSI BIT(6) /* UART has no modem status interrupt */
/* Module parameters */
#define UART_NR CONFIG_SERIAL_8250_NR_UARTS
diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c
index 0609582a62f7..ece629bdd700 100644
--- a/drivers/tty/serial/8250/8250_bcm2835aux.c
+++ b/drivers/tty/serial/8250/8250_bcm2835aux.c
@@ -101,6 +101,7 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev)
up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SKIP_TEST | UPF_IOREMAP;
up.port.rs485_config = serial8250_em485_config;
up.port.rs485_supported = serial8250_em485_supported;
+ up.bugs |= UART_BUG_NOMSI;
up.rs485_start_tx = bcm2835aux_rs485_start_tx;
up.rs485_stop_tx = bcm2835aux_rs485_stop_tx;
@@ -156,6 +157,13 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev)
*/
up.port.uartclk *= 2;
+ /* The clock is only queried at probe time, which means we get one shot
+ * at this. A zero clock is never going to work and is almost certainly
+ * due to a parent not being ready, so prefer to defer.
+ */
+ if (!up.port.uartclk)
+ return -EPROBE_DEFER;
+
/* register the port */
ret = serial8250_register_8250_port(&up);
if (ret < 0) {
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index bfa421ab3253..fa0720a6bd11 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -210,6 +210,18 @@ static void serial8250_timeout(struct timer_list *t)
mod_timer(&up->timer, jiffies + uart_poll_timeout(&up->port));
}
+static void serial8250_cts_poll_timeout(struct timer_list *t)
+{
+ struct uart_8250_port *up = timer_container_of(up, t, timer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&up->port.lock, flags);
+ serial8250_modem_status(up);
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ if (up->port.hw_stopped)
+ mod_timer(&up->timer, jiffies + 1);
+}
+
static void serial8250_backup_timeout(struct timer_list *t)
{
struct uart_8250_port *up = timer_container_of(up, t, timer);
@@ -273,6 +285,9 @@ static void univ8250_setup_timer(struct uart_8250_port *up)
uart_poll_timeout(port) + HZ / 5);
}
+ if (up->bugs & UART_BUG_NOMSI)
+ up->timer.function = serial8250_cts_poll_timeout;
+
/*
* If the "interrupt" for this port doesn't correspond with any
* hardware interrupt, we use a timer-based system. The original
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 719faf92aa8a..7a62883aad99 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -357,7 +357,8 @@ static void mem_serial_out(struct uart_port *p, unsigned int offset, u32 value)
writeb(value, p->membase + offset);
}
-static void mem16_serial_out(struct uart_port *p, unsigned int offset, u32 value)
+static void mem16_serial_out(struct uart_port *p, unsigned int offset,
+ u32 value)
{
offset = offset << p->regshift;
writew(value, p->membase + offset);
@@ -369,7 +370,8 @@ static u32 mem16_serial_in(struct uart_port *p, unsigned int offset)
return readw(p->membase + offset);
}
-static void mem32_serial_out(struct uart_port *p, unsigned int offset, u32 value)
+static void mem32_serial_out(struct uart_port *p, unsigned int offset,
+ u32 value)
{
offset = offset << p->regshift;
writel(value, p->membase + offset);
@@ -381,7 +383,8 @@ static u32 mem32_serial_in(struct uart_port *p, unsigned int offset)
return readl(p->membase + offset);
}
-static void mem32be_serial_out(struct uart_port *p, unsigned int offset, u32 value)
+static void mem32be_serial_out(struct uart_port *p, unsigned int offset,
+ u32 value)
{
offset = offset << p->regshift;
iowrite32be(value, p->membase + offset);
@@ -468,8 +471,7 @@ static void set_io_from_upio(struct uart_port *p)
p->handle_irq = serial8250_default_handle_irq;
}
-static void
-serial_port_out_sync(struct uart_port *p, int offset, int value)
+static void serial_port_out_sync(struct uart_port *p, int offset, int value)
{
switch (p->iotype) {
case UPIO_MEM:
@@ -478,7 +480,7 @@ serial_port_out_sync(struct uart_port *p, int offset, int value)
case UPIO_MEM32BE:
case UPIO_AU:
p->serial_out(p, offset, value);
- p->serial_in(p, UART_LCR); /* safe, no side-effects */
+ p->serial_in(p, UART_LCR); /* safe, no side-effects */
break;
default:
p->serial_out(p, offset, value);
@@ -492,8 +494,9 @@ static void serial8250_clear_fifos(struct uart_8250_port *p)
{
if (p->capabilities & UART_CAP_FIFO) {
serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO);
- serial_out(p, UART_FCR, UART_FCR_ENABLE_FIFO |
- UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ serial_out(p, UART_FCR,
+ UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT);
serial_out(p, UART_FCR, 0);
}
}
@@ -557,9 +560,11 @@ static int serial8250_em485_init(struct uart_8250_port *p)
if (!p->em485)
return -ENOMEM;
- hrtimer_setup(&p->em485->stop_tx_timer, &serial8250_em485_handle_stop_tx, CLOCK_MONOTONIC,
+ hrtimer_setup(&p->em485->stop_tx_timer,
+ &serial8250_em485_handle_stop_tx, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
- hrtimer_setup(&p->em485->start_tx_timer, &serial8250_em485_handle_start_tx, CLOCK_MONOTONIC,
+ hrtimer_setup(&p->em485->start_tx_timer,
+ &serial8250_em485_handle_start_tx, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
p->em485->port = p;
p->em485->active_timer = NULL;
@@ -599,8 +604,9 @@ void serial8250_em485_destroy(struct uart_8250_port *p)
EXPORT_SYMBOL_GPL(serial8250_em485_destroy);
struct serial_rs485 serial8250_em485_supported = {
- .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND |
- SER_RS485_TERMINATE_BUS | SER_RS485_RX_DURING_TX,
+ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
+ SER_RS485_RTS_AFTER_SEND | SER_RS485_TERMINATE_BUS |
+ SER_RS485_RX_DURING_TX,
.delay_rts_before_send = 1,
.delay_rts_after_send = 1,
};
@@ -728,8 +734,9 @@ static int size_fifo(struct uart_8250_port *up)
serial_out(up, UART_LCR, 0);
old_fcr = serial_in(up, UART_FCR);
old_mcr = serial8250_in_MCR(up);
- serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
- UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ serial_out(up, UART_FCR,
+ UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT);
serial8250_out_MCR(up, UART_MCR_LOOP);
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
old_dl = serial_dl_read(up);
@@ -737,9 +744,9 @@ static int size_fifo(struct uart_8250_port *up)
serial_out(up, UART_LCR, UART_LCR_WLEN8);
for (count = 0; count < 256; count++)
serial_out(up, UART_TX, count);
- mdelay(20);/* FIXME - schedule_timeout */
- for (count = 0; (serial_in(up, UART_LSR) & UART_LSR_DR) &&
- (count < 256); count++)
+ mdelay(20); /* FIXME - schedule_timeout */
+ for (count = 0;
+ (serial_in(up, UART_LSR) & UART_LSR_DR) && (count < 256); count++)
serial_in(up, UART_RX);
serial_out(up, UART_FCR, old_fcr);
serial8250_out_MCR(up, old_mcr);
@@ -930,9 +937,10 @@ static void autoconfig_16550a(struct uart_8250_port *up)
up->capabilities |= UART_CAP_EFR | UART_CAP_SLEEP;
} else {
serial_out(up, UART_LCR, 0);
- serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
- UART_FCR7_64BYTE);
- status1 = serial_in(up, UART_IIR) & UART_IIR_FIFO_ENABLED_16750;
+ serial_out(up, UART_FCR,
+ UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE);
+ status1 = serial_in(up, UART_IIR) &
+ UART_IIR_FIFO_ENABLED_16750;
serial_out(up, UART_FCR, 0);
serial_out(up, UART_LCR, 0);
@@ -986,7 +994,7 @@ static void autoconfig_16550a(struct uart_8250_port *up)
serial_out(up, UART_LCR, 0);
- up->port.uartclk = 921600*16;
+ up->port.uartclk = 921600 * 16;
up->port.type = PORT_NS16550A;
up->capabilities |= UART_NATSEMI;
return;
@@ -1137,7 +1145,8 @@ static void autoconfig(struct uart_8250_port *up)
* that conflicts with COM 1-4 --- we hope!
*/
if (!(port->flags & UPF_SKIP_TEST)) {
- serial8250_out_MCR(up, UART_MCR_LOOP | UART_MCR_OUT2 | UART_MCR_RTS);
+ serial8250_out_MCR(up, UART_MCR_LOOP | UART_MCR_OUT2 |
+ UART_MCR_RTS);
status1 = serial_in(up, UART_MSR) & UART_MSR_STATUS_BITS;
serial8250_out_MCR(up, save_mcr);
if (status1 != (UART_MSR_DCD | UART_MSR_CTS)) {
@@ -1241,7 +1250,7 @@ static void autoconfig_irq(struct uart_8250_port *up)
serial8250_out_MCR(up, UART_MCR_DTR | UART_MCR_RTS);
} else {
serial8250_out_MCR(up,
- UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
+ UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
}
/* Synchronize UART_IER access against the console. */
scoped_guard(uart_port_lock_irq, port)
@@ -1313,8 +1322,8 @@ EXPORT_SYMBOL_GPL(serial8250_em485_stop_tx);
static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t)
{
- struct uart_8250_em485 *em485 = container_of(t, struct uart_8250_em485,
- stop_tx_timer);
+ struct uart_8250_em485 *em485 =
+ container_of(t, struct uart_8250_em485, stop_tx_timer);
struct uart_8250_port *p = em485->port;
guard(serial8250_rpm)(p);
@@ -1349,7 +1358,8 @@ static void __stop_tx_rs485(struct uart_8250_port *p, u64 stop_delay)
*/
if (stop_delay > 0) {
em485->active_timer = &em485->stop_tx_timer;
- hrtimer_start(&em485->stop_tx_timer, ns_to_ktime(stop_delay), HRTIMER_MODE_REL);
+ hrtimer_start(&em485->stop_tx_timer, ns_to_ktime(stop_delay),
+ HRTIMER_MODE_REL);
} else {
p->rs485_stop_tx(p, true);
em485->active_timer = NULL;
@@ -1385,7 +1395,8 @@ static inline void __stop_tx(struct uart_8250_port *p)
* rather than after it is fully sent.
* Roughly estimate 1 extra bit here with / 7.
*/
- stop_delay = p->port.frame_time + DIV_ROUND_UP(p->port.frame_time, 7);
+ stop_delay = p->port.frame_time +
+ DIV_ROUND_UP(p->port.frame_time, 7);
}
__stop_tx_rs485(p, stop_delay);
@@ -1409,6 +1420,10 @@ static void serial8250_stop_tx(struct uart_port *port)
up->acr |= UART_ACR_TXDIS;
serial_icr_write(up, UART_ACR, up->acr);
}
+ serial8250_rpm_put(up);
+
+ if (port->hw_stopped && (up->bugs & UART_BUG_NOMSI))
+ mod_timer(&up->timer, jiffies + 1);
}
static inline void __start_tx(struct uart_port *port)
@@ -1500,8 +1515,8 @@ static bool start_tx_rs485(struct uart_port *port)
static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t)
{
- struct uart_8250_em485 *em485 = container_of(t, struct uart_8250_em485,
- start_tx_timer);
+ struct uart_8250_em485 *em485 =
+ container_of(t, struct uart_8250_em485, start_tx_timer);
struct uart_8250_port *p = em485->port;
guard(uart_port_lock_irqsave)(&p->port);
@@ -1522,6 +1537,9 @@ static void serial8250_start_tx(struct uart_port *port)
/* Port locked to synchronize UART_IER access against the console. */
lockdep_assert_held_once(&port->lock);
+ if (up->bugs & UART_BUG_NOMSI)
+ timer_delete(&up->timer);
+
if (!port->x_char && kfifo_is_empty(&port->state->port.xmit_fifo))
return;
@@ -1749,6 +1767,9 @@ unsigned int serial8250_modem_status(struct uart_8250_port *up)
uart_handle_cts_change(port, status & UART_MSR_CTS);
wake_up_interruptible(&port->state->port.delta_msr_wait);
+ } else if (up->bugs & UART_BUG_NOMSI && port->hw_stopped &&
+ status & UART_MSR_CTS) {
+ uart_handle_cts_change(port, status & UART_MSR_CTS);
}
return status;
@@ -1875,7 +1896,8 @@ static unsigned int serial8250_tx_empty(struct uart_port *port)
guard(serial8250_rpm)(up);
guard(uart_port_lock_irqsave)(port);
- if (!serial8250_tx_dma_running(up) && uart_lsr_tx_empty(serial_lsr_in(up)))
+ if (!serial8250_tx_dma_running(up) &&
+ uart_lsr_tx_empty(serial_lsr_in(up)))
return TIOCSER_TEMT;
return 0;
@@ -1887,8 +1909,7 @@ unsigned int serial8250_do_get_mctrl(struct uart_port *port)
unsigned int status;
unsigned int val;
- scoped_guard(serial8250_rpm, up)
- status = serial8250_modem_status(up);
+ scoped_guard(serial8250_rpm, up) status = serial8250_modem_status(up);
val = serial8250_MSR_to_TIOCM(status);
if (up->gpios)
@@ -2011,9 +2032,7 @@ static int serial8250_get_poll_char(struct uart_port *port)
return serial_port_in(port, UART_RX);
}
-
-static void serial8250_put_poll_char(struct uart_port *port,
- unsigned char c)
+static void serial8250_put_poll_char(struct uart_port *port, unsigned char c)
{
unsigned int ier;
struct uart_8250_port *up = up_to_u8250p(port);
@@ -2078,7 +2097,8 @@ static void serial8250_startup_special(struct uart_port *port)
*
* Synchronize UART_IER access against the console.
*/
- scoped_guard(uart_port_lock_irqsave, port) {
+ scoped_guard(uart_port_lock_irqsave, port)
+ {
serial_port_out(port, UART_IER, 0);
serial_port_out(port, UART_DA830_PWREMU_MGMT, 0);
}
@@ -2087,8 +2107,8 @@ static void serial8250_startup_special(struct uart_port *port)
/* Enable Tx, Rx and free run mode */
serial_port_out(port, UART_DA830_PWREMU_MGMT,
UART_DA830_PWREMU_MGMT_UTRST |
- UART_DA830_PWREMU_MGMT_URRST |
- UART_DA830_PWREMU_MGMT_FREE);
+ UART_DA830_PWREMU_MGMT_URRST |
+ UART_DA830_PWREMU_MGMT_FREE);
break;
case PORT_RSA:
rsa_enable(up);
@@ -2107,7 +2127,8 @@ static void serial8250_set_TRG_levels(struct uart_port *port)
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
- fctr = serial_in(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX);
+ fctr = serial_in(up, UART_FCTR) &
+ ~(UART_FCTR_RX | UART_FCTR_TX);
fctr |= UART_FCTR_TRGD;
serial_port_out(port, UART_FCTR, fctr | UART_FCTR_RX);
serial_port_out(port, UART_TRG, UART_TRG_96);
@@ -2126,11 +2147,13 @@ static void serial8250_set_TRG_levels(struct uart_port *port)
/* Bounds checking of TX threshold (valid 0 to fifosize-2) */
if (up->tx_loadsz < 2 || up->tx_loadsz > port->fifosize) {
- dev_err(port->dev, "TX FIFO Threshold errors, skipping\n");
+ dev_err(port->dev,
+ "TX FIFO Threshold errors, skipping\n");
return;
}
serial_port_out(port, UART_ALTR_AFR, UART_ALTR_EN_TXFIFO_LW);
- serial_port_out(port, UART_ALTR_TX_LOW, port->fifosize - up->tx_loadsz);
+ serial_port_out(port, UART_ALTR_TX_LOW,
+ port->fifosize - up->tx_loadsz);
port->handle_irq = serial8250_tx_threshold_handle_irq;
break;
}
@@ -2158,7 +2181,8 @@ static void serial8250_THRE_test(struct uart_port *port)
*
* Synchronize UART_IER access against the console.
*/
- scoped_guard(uart_port_lock_irqsave, port) {
+ scoped_guard(uart_port_lock_irqsave, port)
+ {
wait_for_xmitr(up, UART_LSR_THRE);
serial_port_out_sync(port, UART_IER, UART_IER_THRI);
udelay(1); /* allow THRE to set */
@@ -2221,7 +2245,8 @@ static void serial8250_iir_txen_test(struct uart_port *port)
if (lsr_temt && iir_noint) {
if (!(up->bugs & UART_BUG_TXEN)) {
up->bugs |= UART_BUG_TXEN;
- dev_dbg(port->dev, "enabling bad tx status workarounds\n");
+ dev_dbg(port->dev,
+ "enabling bad tx status workarounds\n");
}
return;
}
@@ -2357,7 +2382,8 @@ void serial8250_do_shutdown(struct uart_port *port)
*
* Synchronize UART_IER access against the console.
*/
- scoped_guard(uart_port_lock_irqsave, port) {
+ scoped_guard(uart_port_lock_irqsave, port)
+ {
up->ier = 0;
serial_port_out(port, UART_IER, 0);
}
@@ -2367,7 +2393,8 @@ void serial8250_do_shutdown(struct uart_port *port)
if (up->dma)
serial8250_release_dma(up);
- scoped_guard(uart_port_lock_irqsave, port) {
+ scoped_guard(uart_port_lock_irqsave, port)
+ {
if (port->flags & UPF_FOURPORT) {
/* reset interrupts on the AST Fourport board */
inb((port->iobase & 0xfe0) | 0x1f);
@@ -2414,7 +2441,8 @@ static void serial8250_flush_buffer(struct uart_port *port)
serial8250_tx_dma_flush(up);
}
-static unsigned int serial8250_do_get_divisor(struct uart_port *port, unsigned int baud)
+static unsigned int serial8250_do_get_divisor(struct uart_port *port,
+ unsigned int baud)
{
upf_t magic_multiplier = port->flags & UPF_MAGIC_MULTIPLIER;
struct uart_8250_port *up = up_to_u8250p(port);
@@ -2478,7 +2506,8 @@ static unsigned int serial8250_get_divisor(struct uart_port *port,
return serial8250_do_get_divisor(port, baud);
}
-static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, tcflag_t c_cflag)
+static unsigned char serial8250_compute_lcr(struct uart_8250_port *up,
+ tcflag_t c_cflag)
{
u8 lcr = UART_LCR_WLEN(tty_get_char_size(c_cflag));
@@ -2569,7 +2598,8 @@ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk)
{
struct tty_port *tport = &port->state->port;
- scoped_guard(tty_port_tty, tport) {
+ scoped_guard(tty_port_tty, tport)
+ {
struct tty_struct *tty = scoped_tty();
guard(rwsem_write)(&tty->termios_rwsem);
@@ -2592,7 +2622,8 @@ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk)
}
EXPORT_SYMBOL_GPL(serial8250_update_uartclk);
-static void serial8250_set_mini(struct uart_port *port, struct ktermios *termios)
+static void serial8250_set_mini(struct uart_port *port,
+ struct ktermios *termios)
{
struct uart_8250_port *up = up_to_u8250p(port);
@@ -2608,7 +2639,8 @@ static void serial8250_set_mini(struct uart_port *port, struct ktermios *termios
}
}
-static void serial8250_set_trigger_for_slow_speed(struct uart_port *port, struct ktermios *termios,
+static void serial8250_set_trigger_for_slow_speed(struct uart_port *port,
+ struct ktermios *termios,
unsigned int baud)
{
struct uart_8250_port *up = up_to_u8250p(port);
@@ -2642,7 +2674,8 @@ static void serial8250_set_afe(struct uart_port *port, struct ktermios *termios)
up->mcr |= UART_MCR_AFE;
}
-static void serial8250_set_errors_and_ignores(struct uart_port *port, struct ktermios *termios)
+static void serial8250_set_errors_and_ignores(struct uart_port *port,
+ struct ktermios *termios)
{
/*
* Specify which conditions may be considered for error handling and the ignoring of
@@ -2680,7 +2713,8 @@ static void serial8250_set_ier(struct uart_port *port, struct ktermios *termios)
/* CTS flow control flag and modem status interrupts */
up->ier &= ~UART_IER_MSI;
- if (!(up->bugs & UART_BUG_NOMSR) && UART_ENABLE_MS(&up->port, termios->c_cflag))
+ if (!(up->bugs & UART_BUG_NOMSR) &&
+ UART_ENABLE_MS(&up->port, termios->c_cflag))
up->ier |= UART_IER_MSI;
if (up->capabilities & UART_CAP_UUE)
up->ier |= UART_IER_UUE;
@@ -2738,9 +2772,8 @@ static void serial8250_set_fcr(struct uart_port *port, struct ktermios *termios)
serial_port_out(port, UART_FCR, up->fcr);
}
-void
-serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
- const struct ktermios *old)
+void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
+ const struct ktermios *old)
{
struct uart_8250_port *up = up_to_u8250p(port);
unsigned int baud, quot, frac = 0;
@@ -2756,7 +2789,8 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
*
* Synchronize UART_IER access against the console.
*/
- scoped_guard(serial8250_rpm, up) {
+ scoped_guard(serial8250_rpm, up)
+ {
guard(uart_port_lock_irqsave)(port);
up->lcr = lcr;
@@ -2777,9 +2811,9 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
}
EXPORT_SYMBOL(serial8250_do_set_termios);
-static void
-serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
- const struct ktermios *old)
+static void serial8250_set_termios(struct uart_port *port,
+ struct ktermios *termios,
+ const struct ktermios *old)
{
if (port->set_termios)
port->set_termios(port, termios, old);
@@ -2803,8 +2837,8 @@ void serial8250_do_set_ldisc(struct uart_port *port, struct ktermios *termios)
}
EXPORT_SYMBOL_GPL(serial8250_do_set_ldisc);
-static void
-serial8250_set_ldisc(struct uart_port *port, struct ktermios *termios)
+static void serial8250_set_ldisc(struct uart_port *port,
+ struct ktermios *termios)
{
if (port->set_ldisc)
port->set_ldisc(port, termios);
@@ -2821,9 +2855,8 @@ void serial8250_do_pm(struct uart_port *port, unsigned int state,
}
EXPORT_SYMBOL(serial8250_do_pm);
-static void
-serial8250_pm(struct uart_port *port, unsigned int state,
- unsigned int oldstate)
+static void serial8250_pm(struct uart_port *port, unsigned int state,
+ unsigned int oldstate)
{
if (port->pm)
port->pm(port, state, oldstate);
@@ -2979,7 +3012,7 @@ static int do_serial8250_get_rxtrig(struct tty_port *port)
}
static ssize_t rx_trig_bytes_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
struct tty_port *port = dev_get_drvdata(dev);
int rxtrig_bytes;
@@ -3024,7 +3057,8 @@ static int do_serial8250_set_rxtrig(struct tty_port *port, unsigned char bytes)
}
static ssize_t rx_trig_bytes_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct tty_port *port = dev_get_drvdata(dev);
unsigned char bytes;
@@ -3047,8 +3081,7 @@ static ssize_t rx_trig_bytes_store(struct device *dev,
static DEVICE_ATTR_RW(rx_trig_bytes);
static struct attribute *serial8250_dev_attrs[] = {
- &dev_attr_rx_trig_bytes.attr,
- NULL
+ &dev_attr_rx_trig_bytes.attr, NULL
};
static struct attribute_group serial8250_dev_attr_group = {
@@ -3096,8 +3129,8 @@ static void serial8250_config_port(struct uart_port *port, int flags)
up->fcr = uart_config[up->port.type].fcr;
}
-static int
-serial8250_verify_port(struct uart_port *port, struct serial_struct *ser)
+static int serial8250_verify_port(struct uart_port *port,
+ struct serial_struct *ser)
{
if (ser->irq >= irq_get_nr_irqs() || ser->irq < 0 ||
ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
@@ -3117,27 +3150,27 @@ static const char *serial8250_type(struct uart_port *port)
}
static const struct uart_ops serial8250_pops = {
- .tx_empty = serial8250_tx_empty,
- .set_mctrl = serial8250_set_mctrl,
- .get_mctrl = serial8250_get_mctrl,
- .stop_tx = serial8250_stop_tx,
- .start_tx = serial8250_start_tx,
- .throttle = serial8250_throttle,
- .unthrottle = serial8250_unthrottle,
- .stop_rx = serial8250_stop_rx,
- .enable_ms = serial8250_enable_ms,
- .break_ctl = serial8250_break_ctl,
- .startup = serial8250_startup,
- .shutdown = serial8250_shutdown,
- .flush_buffer = serial8250_flush_buffer,
- .set_termios = serial8250_set_termios,
- .set_ldisc = serial8250_set_ldisc,
- .pm = serial8250_pm,
- .type = serial8250_type,
- .release_port = serial8250_release_port,
- .request_port = serial8250_request_port,
- .config_port = serial8250_config_port,
- .verify_port = serial8250_verify_port,
+ .tx_empty = serial8250_tx_empty,
+ .set_mctrl = serial8250_set_mctrl,
+ .get_mctrl = serial8250_get_mctrl,
+ .stop_tx = serial8250_stop_tx,
+ .start_tx = serial8250_start_tx,
+ .throttle = serial8250_throttle,
+ .unthrottle = serial8250_unthrottle,
+ .stop_rx = serial8250_stop_rx,
+ .enable_ms = serial8250_enable_ms,
+ .break_ctl = serial8250_break_ctl,
+ .startup = serial8250_startup,
+ .shutdown = serial8250_shutdown,
+ .flush_buffer = serial8250_flush_buffer,
+ .set_termios = serial8250_set_termios,
+ .set_ldisc = serial8250_set_ldisc,
+ .pm = serial8250_pm,
+ .type = serial8250_type,
+ .release_port = serial8250_release_port,
+ .request_port = serial8250_request_port,
+ .config_port = serial8250_config_port,
+ .verify_port = serial8250_verify_port,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = serial8250_get_poll_char,
.poll_put_char = serial8250_put_poll_char,
@@ -3192,7 +3225,8 @@ static void serial8250_console_putchar(struct uart_port *port, unsigned char ch)
serial_port_out(port, UART_TX, ch);
}
-static void serial8250_console_wait_putchar(struct uart_port *port, unsigned char ch)
+static void serial8250_console_wait_putchar(struct uart_port *port,
+ unsigned char ch)
{
struct uart_8250_port *up = up_to_u8250p(port);
@@ -3319,29 +3353,29 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
}
use_fifo = (up->capabilities & UART_CAP_FIFO) &&
- /*
+ /*
* BCM283x requires to check the fifo
* after each byte.
*/
- !(up->capabilities & UART_CAP_MINI) &&
- /*
+ !(up->capabilities & UART_CAP_MINI) &&
+ /*
* tx_loadsz contains the transmit fifo size
*/
- up->tx_loadsz > 1 &&
- (up->fcr & UART_FCR_ENABLE_FIFO) &&
- port->state &&
- test_bit(TTY_PORT_INITIALIZED, &port->state->port.iflags) &&
- /*
+ up->tx_loadsz > 1 && (up->fcr & UART_FCR_ENABLE_FIFO) &&
+ port->state &&
+ test_bit(TTY_PORT_INITIALIZED, &port->state->port.iflags) &&
+ /*
* After we put a data in the fifo, the controller will send
* it regardless of the CTS state. Therefore, only use fifo
* if we don't use control flow.
*/
- !(up->port.flags & UPF_CONS_FLOW);
+ !(up->port.flags & UPF_CONS_FLOW);
if (likely(use_fifo))
serial8250_console_fifo_write(up, s, count);
else
- uart_console_write(port, s, count, serial8250_console_wait_putchar);
+ uart_console_write(port, s, count,
+ serial8250_console_wait_putchar);
/*
* Finally, wait for transmitter to become empty
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 282116765e64..84b485b38b7b 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1619,6 +1619,17 @@ config SERIAL_ESP32_ACM
snippet may be used:
earlycon=esp32s3acm,mmio32,0x60038000
+config SERIAL_RPI_FW
+ tristate "Raspberry Pi Firmware software UART support"
+ depends on RASPBERRYPI_FIRMWARE || COMPILE_TEST
+ select SERIAL_CORE
+ help
+ This selects the Raspberry Pi firmware UART. This is a bit-bashed
+ implementation running on the Raspbery Pi VPU core.
+ This is not supported on Raspberry Pi 5 or newer platforms.
+
+ If unsure, say N.
+
endmenu
config SERIAL_MCTRL_GPIO
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index a2ccbc508ec5..f77c5fdcf6b8 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o
obj-$(CONFIG_SERIAL_QE) += ucc_uart.o
obj-$(CONFIG_SERIAL_RDA) += rda-uart.o
obj-$(CONFIG_SERIAL_RP2) += rp2.o
+obj-$(CONFIG_SERIAL_RPI_FW) += rpi-fw-uart.o
obj-$(CONFIG_SERIAL_RSCI) += rsci.o
obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
obj-$(CONFIG_SERIAL_SAMSUNG) += samsung_tty.o
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 7f17d288c807..711aace90483 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -152,6 +152,20 @@ static const struct vendor_data vendor_sbsa = {
.fixed_options = true,
};
+static struct vendor_data vendor_arm_axi = {
+ .reg_offset = pl011_std_offsets,
+ .ifls = UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8,
+ .fr_busy = UART01x_FR_BUSY,
+ .fr_dsr = UART01x_FR_DSR,
+ .fr_cts = UART01x_FR_CTS,
+ .fr_ri = UART011_FR_RI,
+ .oversampling = false,
+ .dma_threshold = false,
+ .cts_event_workaround = false,
+ .always_enabled = false,
+ .fixed_options = false,
+};
+
#ifdef CONFIG_ACPI_SPCR_TABLE
static const struct vendor_data vendor_qdt_qdf2400_e44 = {
.reg_offset = pl011_std_offsets,
@@ -461,6 +475,7 @@ static void pl011_dma_probe(struct uart_amba_port *uap)
.src_addr = uap->port.mapbase +
pl011_reg_to_offset(uap, REG_DR),
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.direction = DMA_DEV_TO_MEM,
.src_maxburst = uap->fifosize >> 2,
.device_fc = false,
@@ -480,6 +495,12 @@ static void pl011_dma_probe(struct uart_amba_port *uap)
"RX DMA disabled - no residue processing\n");
return;
}
+ /*
+ * DMA controllers with smaller burst capabilities than 1/4
+ * the FIFO depth will leave more bytes than expected in the
+ * RX FIFO if mismatched.
+ */
+ rx_conf.src_maxburst = min(caps.max_burst, rx_conf.src_maxburst);
}
dmaengine_slave_config(chan, &rx_conf);
uap->dmarx.chan = chan;
@@ -1495,6 +1516,7 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c,
return false; /* unable to transmit character */
pl011_write(c, uap, REG_DR);
+ mb();
uap->port.icount.tx++;
return true;
@@ -1527,6 +1549,10 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
if (likely(from_irq) && count-- == 0)
break;
+ if (likely(from_irq) && count == 0 &&
+ pl011_read(uap, REG_FR) & UART01x_FR_TXFF)
+ break;
+
if (!kfifo_peek(&tport->xmit_fifo, &c))
break;
@@ -2894,6 +2920,11 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
if (IS_ERR(uap->clk))
return PTR_ERR(uap->clk);
+ if (of_property_read_bool(dev->dev.of_node, "cts-event-workaround")) {
+ vendor->cts_event_workaround = true;
+ dev_info(&dev->dev, "cts_event_workaround enabled\n");
+ }
+
uap->reg_offset = vendor->reg_offset;
uap->vendor = vendor;
uap->fifosize = vendor->get_fifosize(dev);
@@ -3070,6 +3101,87 @@ static struct platform_driver arm_sbsa_uart_platform_driver = {
},
};
+static int pl011_axi_probe(struct platform_device *pdev)
+{
+ struct uart_amba_port *uap;
+ struct vendor_data *vendor = &vendor_arm_axi;
+ struct resource *r;
+ unsigned int periphid;
+ int portnr, ret, irq;
+
+ portnr = pl011_find_free_port();
+ if (portnr < 0)
+ return portnr;
+
+ uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port),
+ GFP_KERNEL);
+ if (!uap)
+ return -ENOMEM;
+
+ uap->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(uap->clk))
+ return PTR_ERR(uap->clk);
+
+ if (of_property_read_bool(pdev->dev.of_node, "cts-event-workaround")) {
+ vendor->cts_event_workaround = true;
+ dev_info(&pdev->dev, "cts_event_workaround enabled\n");
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ periphid = 0x00241011; /* A safe default */
+ of_property_read_u32(pdev->dev.of_node, "arm,primecell-periphid",
+ &periphid);
+
+ uap->reg_offset = vendor->reg_offset;
+ uap->vendor = vendor;
+ uap->fifosize = (AMBA_REV_BITS(periphid) < 3) ? 16 : 32;
+ uap->port.iotype = vendor->access_32b ? UPIO_MEM32 : UPIO_MEM;
+ uap->port.irq = irq;
+ uap->port.ops = &amba_pl011_pops;
+ uap->port.rs485_config = pl011_rs485_config;
+ uap->port.rs485_supported = pl011_rs485_supported;
+
+ snprintf(uap->type, sizeof(uap->type), "PL011 AXI");
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ ret = pl011_setup_port(&pdev->dev, uap, r, portnr);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, uap);
+
+ return pl011_register_port(uap);
+}
+
+static void pl011_axi_remove(struct platform_device *pdev)
+{
+ struct uart_amba_port *uap = platform_get_drvdata(pdev);
+
+ uart_remove_one_port(&amba_reg, &uap->port);
+ pl011_unregister_port(uap);
+}
+
+static const struct of_device_id pl011_axi_of_match[] = {
+ { .compatible = "arm,pl011-axi" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pl011_axi_of_match);
+
+static struct platform_driver pl011_axi_platform_driver = {
+ .probe = pl011_axi_probe,
+ .remove = pl011_axi_remove,
+ .driver = {
+ .name = "pl011-axi",
+ .pm = &pl011_dev_pm_ops,
+ .of_match_table = of_match_ptr(pl011_axi_of_match),
+ .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011),
+ },
+};
+
static const struct amba_id pl011_ids[] = {
{
.id = 0x00041011,
@@ -3103,12 +3215,15 @@ static int __init pl011_init(void)
if (platform_driver_register(&arm_sbsa_uart_platform_driver))
pr_warn("could not register SBSA UART platform driver\n");
+ if (platform_driver_register(&pl011_axi_platform_driver))
+ pr_warn("could not register PL011 AXI platform driver\n");
return amba_driver_register(&pl011_driver);
}
static void __exit pl011_exit(void)
{
platform_driver_unregister(&arm_sbsa_uart_platform_driver);
+ platform_driver_unregister(&pl011_axi_platform_driver);
amba_driver_unregister(&pl011_driver);
}
diff --git a/drivers/tty/serial/rpi-fw-uart.c b/drivers/tty/serial/rpi-fw-uart.c
new file mode 100644
index 000000000000..075f68ae81ac
--- /dev/null
+++ b/drivers/tty/serial/rpi-fw-uart.c
@@ -0,0 +1,561 @@
+// 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_setup(&rfu->trigger_start_rx, rpi_fw_uart_trigger_rx, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+
+ 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 <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Raspberry Pi Firmware Software UART driver");
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index c7435595dce1..f6ccb1dd216b 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -817,6 +817,8 @@ static bool sc16is7xx_port_irq(struct sc16is7xx_port *s, int portno)
if (rxlen)
sc16is7xx_handle_rx(port, rxlen, iir);
+ else
+ rc = false;
break;
/* CTSRTS interrupt comes only when CTS goes inactive */
case SC16IS7XX_IIR_CTSRTS_SRC:
@@ -1205,6 +1207,9 @@ static int sc16is7xx_startup(struct uart_port *port)
SC16IS7XX_IER_MSI_BIT;
sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val);
+ /* Initialize the Modem Control signals to current status */
+ one->old_mctrl = sc16is7xx_get_hwmctrl(port);
+
/* Enable modem status polling */
uart_port_lock_irqsave(port, &flags);
sc16is7xx_enable_ms(port);