diff options
Diffstat (limited to 'drivers/tty')
| -rw-r--r-- | drivers/tty/serial/8250/8250.h | 1 | ||||
| -rw-r--r-- | drivers/tty/serial/8250/8250_bcm2835aux.c | 8 | ||||
| -rw-r--r-- | drivers/tty/serial/8250/8250_core.c | 15 | ||||
| -rw-r--r-- | drivers/tty/serial/8250/8250_port.c | 238 | ||||
| -rw-r--r-- | drivers/tty/serial/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/tty/serial/Makefile | 1 | ||||
| -rw-r--r-- | drivers/tty/serial/amba-pl011.c | 115 | ||||
| -rw-r--r-- | drivers/tty/serial/rpi-fw-uart.c | 561 | ||||
| -rw-r--r-- | drivers/tty/serial/sc16is7xx.c | 5 |
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); |
