#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
+#include <linux/pm_runtime.h>
#ifdef CONFIG_SPARC
#include <linux/sunserialcore.h>
#endif
}
EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos);
+static void serial8250_rpm_get(struct uart_8250_port *p)
+{
+ if (!(p->capabilities & UART_CAP_RPM))
+ return;
+ pm_runtime_get_sync(p->port.dev);
+}
+
+static void serial8250_rpm_put(struct uart_8250_port *p)
+{
+ if (!(p->capabilities & UART_CAP_RPM))
+ return;
+ pm_runtime_mark_last_busy(p->port.dev);
+ pm_runtime_put_autosuspend(p->port.dev);
+}
+
+/*
+ * This two wrapper ensure, that enable_runtime_pm_tx() can be called more than
+ * once and disable_runtime_pm_tx() will still disable RPM because the fifo is
+ * empty and the HW can idle again.
+ */
+static void serial8250_rpm_get_tx(struct uart_8250_port *p)
+{
+ unsigned char rpm_active;
+
+ if (!(p->capabilities & UART_CAP_RPM))
+ return;
+
+ rpm_active = xchg(&p->rpm_tx_active, 1);
+ if (rpm_active)
+ return;
+ pm_runtime_get_sync(p->port.dev);
+}
+
+static void serial8250_rpm_put_tx(struct uart_8250_port *p)
+{
+ unsigned char rpm_active;
+
+ if (!(p->capabilities & UART_CAP_RPM))
+ return;
+
+ rpm_active = xchg(&p->rpm_tx_active, 0);
+ if (!rpm_active)
+ return;
+ pm_runtime_mark_last_busy(p->port.dev);
+ pm_runtime_put_autosuspend(p->port.dev);
+}
+
/*
* IER sleep support. UARTs which have EFRs need the "extended
* capability" bit enabled. Note that on XR16C850s, we need to
* offset but the UART channel may only write to the corresponding
* bit.
*/
+ serial8250_rpm_get(p);
if ((p->port.type == PORT_XR17V35X) ||
(p->port.type == PORT_XR17D15X)) {
serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0);
- return;
+ goto out;
}
if (p->capabilities & UART_CAP_SLEEP) {
serial_out(p, UART_LCR, 0);
}
}
+out:
+ serial8250_rpm_put(p);
}
#ifdef CONFIG_SERIAL_8250_RSA
if (p->ier & UART_IER_THRI) {
p->ier &= ~UART_IER_THRI;
serial_out(p, UART_IER, p->ier);
+ serial8250_rpm_put_tx(p);
}
}
{
struct uart_8250_port *up = up_to_u8250p(port);
+ serial8250_rpm_get(up);
__stop_tx(up);
/*
up->acr |= UART_ACR_TXDIS;
serial_icr_write(up, UART_ACR, up->acr);
}
+ serial8250_rpm_put(up);
}
static void serial8250_start_tx(struct uart_port *port)
{
struct uart_8250_port *up = up_to_u8250p(port);
+ serial8250_rpm_get_tx(up);
if (up->dma && !serial8250_tx_dma(up)) {
return;
} else if (!(up->ier & UART_IER_THRI)) {
{
struct uart_8250_port *up = up_to_u8250p(port);
+ serial8250_rpm_get(up);
+
up->ier &= ~UART_IER_RLSI;
up->port.read_status_mask &= ~UART_LSR_DR;
serial_port_out(port, UART_IER, up->ier);
+
+ serial8250_rpm_put(up);
}
static void serial8250_enable_ms(struct uart_port *port)
return;
up->ier |= UART_IER_MSI;
+
+ serial8250_rpm_get(up);
serial_port_out(port, UART_IER, up->ier);
+ serial8250_rpm_put(up);
}
/*
DEBUG_INTR("THRE...");
- if (uart_circ_empty(xmit))
+ /*
+ * With RPM enabled, we have to wait once the FIFO is empty before the
+ * HW can go idle. So we get here once again with empty FIFO and disable
+ * the interrupt and RPM in __stop_tx()
+ */
+ if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM))
__stop_tx(up);
}
EXPORT_SYMBOL_GPL(serial8250_tx_chars);
static int serial8250_default_handle_irq(struct uart_port *port)
{
- unsigned int iir = serial_port_in(port, UART_IIR);
+ struct uart_8250_port *up = up_to_u8250p(port);
+ unsigned int iir;
+ int ret;
+
+ serial8250_rpm_get(up);
- return serial8250_handle_irq(port, iir);
+ iir = serial_port_in(port, UART_IIR);
+ ret = serial8250_handle_irq(port, iir);
+
+ serial8250_rpm_put(up);
+ return ret;
}
/*
unsigned long flags;
unsigned int lsr;
+ serial8250_rpm_get(up);
+
spin_lock_irqsave(&port->lock, flags);
lsr = serial_port_in(port, UART_LSR);
up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
spin_unlock_irqrestore(&port->lock, flags);
+ serial8250_rpm_put(up);
+
return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0;
}
unsigned int status;
unsigned int ret;
+ serial8250_rpm_get(up);
status = serial8250_modem_status(up);
+ serial8250_rpm_put(up);
ret = 0;
if (status & UART_MSR_DCD)
struct uart_8250_port *up = up_to_u8250p(port);
unsigned long flags;
+ serial8250_rpm_get(up);
spin_lock_irqsave(&port->lock, flags);
if (break_state == -1)
up->lcr |= UART_LCR_SBC;
up->lcr &= ~UART_LCR_SBC;
serial_port_out(port, UART_LCR, up->lcr);
spin_unlock_irqrestore(&port->lock, flags);
+ serial8250_rpm_put(up);
}
/*
static int serial8250_get_poll_char(struct uart_port *port)
{
- unsigned char lsr = serial_port_in(port, UART_LSR);
+ struct uart_8250_port *up = up_to_u8250p(port);
+ unsigned char lsr;
+ int status;
+
+ serial8250_rpm_get(up);
- if (!(lsr & UART_LSR_DR))
- return NO_POLL_CHAR;
+ lsr = serial_port_in(port, UART_LSR);
- return serial_port_in(port, UART_RX);
+ if (!(lsr & UART_LSR_DR)) {
+ status = NO_POLL_CHAR;
+ goto out;
+ }
+
+ status = serial_port_in(port, UART_RX);
+out:
+ serial8250_rpm_put(up);
+ return status;
}
unsigned int ier;
struct uart_8250_port *up = up_to_u8250p(port);
+ serial8250_rpm_get(up);
/*
* First save the IER then disable the interrupts
*/
*/
wait_for_xmitr(up, BOTH_EMPTY);
serial_port_out(port, UART_IER, ier);
+ serial8250_rpm_put(up);
}
#endif /* CONFIG_CONSOLE_POLL */
if (port->iotype != up->cur_iotype)
set_io_from_upio(port);
+ serial8250_rpm_get(up);
if (port->type == PORT_16C950) {
/* Wake up and initialize UART */
up->acr = 0;
*/
enable_rsa(up);
#endif
-
/*
* Clear the FIFO buffers and disable them.
* (they will be reenabled in set_termios())
(serial_port_in(port, UART_LSR) == 0xff)) {
printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n",
serial_index(port));
- return -ENODEV;
+ retval = -ENODEV;
+ goto out;
}
/*
} else {
retval = serial_link_irq_chain(up);
if (retval)
- return retval;
+ goto out;
}
/*
outb_p(0x80, icp);
inb_p(icp);
}
-
- return 0;
+ retval = 0;
+out:
+ serial8250_rpm_put(up);
+ return retval;
}
EXPORT_SYMBOL_GPL(serial8250_do_startup);
struct uart_8250_port *up = up_to_u8250p(port);
unsigned long flags;
+ serial8250_rpm_get(up);
/*
* Disable interrupts from this port
*/
* the IRQ chain.
*/
serial_port_in(port, UART_RX);
+ serial8250_rpm_put(up);
del_timer_sync(&up->timer);
up->timer.function = serial8250_timeout;
* Ok, we're now changing the port state. Do it with
* interrupts disabled.
*/
+ serial8250_rpm_get(up);
spin_lock_irqsave(&port->lock, flags);
/*
}
serial8250_set_mctrl(port, port->mctrl);
spin_unlock_irqrestore(&port->lock, flags);
+ serial8250_rpm_put(up);
+
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
touch_nmi_watchdog();
+ serial8250_rpm_get(up);
+
if (port->sysrq || oops_in_progress)
locked = spin_trylock_irqsave(&port->lock, flags);
else
if (locked)
spin_unlock_irqrestore(&port->lock, flags);
+ serial8250_rpm_put(up);
}
static int __init serial8250_console_setup(struct console *co, char *options)