From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754561AbaIKL5a (ORCPT ); Thu, 11 Sep 2014 07:57:30 -0400 Received: from mailout32.mail01.mtsvc.net ([216.70.64.70]:52816 "EHLO n23.mail01.mtsvc.net" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753524AbaIKL5Z (ORCPT ); Thu, 11 Sep 2014 07:57:25 -0400 Message-ID: <54118E1B.5070706@hurleysoftware.com> Date: Thu, 11 Sep 2014 07:57:15 -0400 From: Peter Hurley User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.0 MIME-Version: 1.0 To: Sebastian Andrzej Siewior , linux-serial@vger.kernel.org CC: linux-kernel@vger.kernel.org, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org, tony@atomide.com, balbi@ti.com, gregkh@linuxfoundation.org Subject: Re: [PATCH 06/16] tty: serial: Add 8250-core based omap driver References: <1410377411-26656-1-git-send-email-bigeasy@linutronix.de> <1410377411-26656-7-git-send-email-bigeasy@linutronix.de> In-Reply-To: <1410377411-26656-7-git-send-email-bigeasy@linutronix.de> Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Authenticated-User: 990527 peter@hurleysoftware.com X-MT-ID: 8FA290C2A27252AACF65DBC4A42F3CE3735FB2A4 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Hi Sebastian, Nice work. Minor comments within. On 09/10/2014 03:30 PM, Sebastian Andrzej Siewior wrote: > This patch provides a 8250-core based UART driver for the internal OMAP > UART. The long term goal is to provide the same functionality as the > current OMAP uart driver and DMA support. > I tried to merge omap-serial code together with the 8250-core code. > There should should be hardly a noticable difference. The trigger levels > are different compared to omap-serial: > - omap serial > TX: Interrupt comes after TX FIFO has room for 16 bytes. > TX of 4096 bytes in one go results in 256 interrupts > > RX: Interrupt comes after there is on byte in the FIFO. > RX of 4096 bytes results in 4096 interrupts. > > - this driver > TX: Interrupt comes once the TX FIFO is empty. > TX of 4096 bytes results in 65 interrupts. That means there will > be gaps on the line while the driver reloads the FIFO. > > RX: Interrupt comes once there are 48 bytes in the FIFO or less over > "longer" time frame. We have > 1 / 11520 * 10^3 * 16 => 1.38… ms > 1.38ms to react and purge the FIFO on 115200,8N1. Since the other > driver fired after each byte it had ~5.47ms time to react. This > _may_ cause problems if one relies on no missing bytes and has no > flow control. On the other hand we get only 85 interrupts for the > same amount of data. After this is merged, it may be worth investigating how to use Yoshihiro's newly-added 8250-based tunable RX trigger interface for omap. > It has been only tested as console UART on am335x-evm, dra7-evm and > beagle bone. I also did some longer raw-transfers to meassure the load. > > The device name is ttyS based instead of ttyO. If a ttyO based node name > is required please ask udev for it. If both driver are activated (this > and omap-serial) then this serial driver will take control over the > device due to the link order > > v8…v9: > - less on a file seems to hang the am335x after a while. I > believe I introduce this bug a while ago since I can reproduce > this prior to v8. Fixed by redoing the omap8250_restore_regs() > v7…v8: > - redo the register write. There is now one function for that > which is used from set_termios() and runtime-resume. > - drop PORT_OMAP_16750 and move the setup to the omap file. We > have our own set termios function anyway (Heikki Krogerus) > - use MEM instead of MEM32. TRM of AM/DM37x says that 32bit > access on THR might result in data abort. We only need 32bit > access in the errata function which is before we use 8250's > read function so it doesn't matter. > v4…v7: > - change trigger levels after some tests with raw transfers. > v3…v4: > - drop RS485 support > - wire up ->throttle / ->unthrottle > v2…v3: > - wire up startup & shutdown for wakeup-irq handling. > - RS485 handling (well the core does). > > v1…v2: > - added runtime PM. Could somebody could please double check > this? > - added omap_8250_set_termios() > > Reviewed-by: Tony Lindgren > Tested-by: Tony Lindgren > Signed-off-by: Sebastian Andrzej Siewior > --- > drivers/tty/serial/8250/8250_omap.c | 911 ++++++++++++++++++++++++++++++++++++ > drivers/tty/serial/8250/Kconfig | 9 + > drivers/tty/serial/8250/Makefile | 1 + > 3 files changed, 921 insertions(+) > create mode 100644 drivers/tty/serial/8250/8250_omap.c > > diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c > new file mode 100644 > index 000000000000..2a187b00ed0a > --- /dev/null > +++ b/drivers/tty/serial/8250/8250_omap.c > @@ -0,0 +1,911 @@ > +/* > + * 8250-core based driver for the OMAP internal UART > + * > + * Copyright (C) 2014 Sebastian Andrzej Siewior + * based on omap-serial.c, Copyright (C) 2010 Texas Instruments. or something like that, since this is (partly) based on omap-serial.c > + * > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "8250.h" > + > +#define DEFAULT_CLK_SPEED 48000000 > + > +#define UART_ERRATA_i202_MDR1_ACCESS (1 << 0) > +#define OMAP_UART_WER_HAS_TX_WAKEUP (1 << 1) > + > +#define OMAP_UART_FCR_RX_TRIG 6 > +#define OMAP_UART_FCR_TX_TRIG 4 > + > +/* SCR register bitmasks */ > +#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK (1 << 7) > +#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK (1 << 6) > +#define OMAP_UART_SCR_TX_EMPTY (1 << 3) > +#define OMAP_UART_SCR_DMAMODE_MASK (3 << 1) > +#define OMAP_UART_SCR_DMAMODE_1 (1 << 1) > +#define OMAP_UART_SCR_DMAMODE_CTL (1 << 0) > + > +/* MVR register bitmasks */ > +#define OMAP_UART_MVR_SCHEME_SHIFT 30 > +#define OMAP_UART_LEGACY_MVR_MAJ_MASK 0xf0 > +#define OMAP_UART_LEGACY_MVR_MAJ_SHIFT 4 > +#define OMAP_UART_LEGACY_MVR_MIN_MASK 0x0f > +#define OMAP_UART_MVR_MAJ_MASK 0x700 > +#define OMAP_UART_MVR_MAJ_SHIFT 8 > +#define OMAP_UART_MVR_MIN_MASK 0x3f > + > +#define UART_TI752_TLR_TX 0 > +#define UART_TI752_TLR_RX 4 > + > +#define TRIGGER_TLR_MASK(x) ((x & 0x3c) >> 2) > +#define TRIGGER_FCR_MASK(x) (x & 3) > + > +/* Enable XON/XOFF flow control on output */ > +#define OMAP_UART_SW_TX 0x08 > +/* Enable XON/XOFF flow control on input */ > +#define OMAP_UART_SW_RX 0x02 > + > +#define OMAP_UART_WER_MOD_WKUP 0x7f > +#define OMAP_UART_TX_WAKEUP_EN (1 << 7) > + > +#define TX_TRIGGER 1 > +#define RX_TRIGGER 48 > + > +#define OMAP_UART_TCR_RESTORE(x) ((x / 4) << 4) > +#define OMAP_UART_TCR_HALT(x) ((x / 4) << 0) > + > +#define UART_BUILD_REVISION(x, y) (((x) << 8) | (y)) > + > +#define OMAP_UART_REV_46 0x0406 > +#define OMAP_UART_REV_52 0x0502 > +#define OMAP_UART_REV_63 0x0603 > + > +struct omap8250_priv { > + int line; > + u32 habit; > + u32 mdr1; > + u32 efr; > + u32 quot; > + u32 scr; > + u32 wer; > + u32 xon; > + u32 xoff; > + > + bool is_suspending; > + int wakeirq; > + int wakeups_enabled; > + u32 latency; > + u32 calc_latency; > + struct pm_qos_request pm_qos_request; > + struct work_struct qos_work; > + struct uart_8250_dma omap8250_dma; > + bool dma_active; > +}; > + > +static u32 uart_read(struct uart_8250_port *up, u32 reg) > +{ > + return readl(up->port.membase + (reg << up->port.regshift)); > +} > + > +/* > + * Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460) > + * The access to uart register after MDR1 Access > + * causes UART to corrupt data. > + * > + * Need a delay = > + * 5 L4 clock cycles + 5 UART functional clock cycle (@48MHz = ~0.2uS) > + * give 10 times as much > + */ > +static void omap_8250_mdr1_errataset(struct uart_8250_port *up, u8 mdr1) > +{ > + u8 timeout = 255; > + > + serial_out(up, UART_OMAP_MDR1, mdr1); > + udelay(2); > + serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_XMIT | > + UART_FCR_CLEAR_RCVR); > + /* > + * Wait for FIFO to empty: when empty, RX_FIFO_E bit is 0 and > + * TX_FIFO_E bit is 1. > + */ > + while (UART_LSR_THRE != (serial_in(up, UART_LSR) & > + (UART_LSR_THRE | UART_LSR_DR))) { > + timeout--; > + if (!timeout) { > + /* Should *never* happen. we warn and carry on */ > + dev_crit(up->port.dev, "Errata i202: timedout %x\n", > + serial_in(up, UART_LSR)); > + break; > + } > + udelay(1); > + } > +} > + > +static void omap_8250_get_divisor(struct uart_port *port, unsigned int baud, > + struct omap8250_priv *priv) > +{ > + unsigned int uartclk = port->uartclk; > + unsigned int div_13, div_16; > + unsigned int abs_d13, abs_d16; > + > + /* > + * Old custom speed handling. > + */ > + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) { > + priv->quot = port->custom_divisor & 0xffff; > + /* > + * I assume that nobody is using this. But hey, if somebody > + * would like to specify the divisor _and_ the mode then the > + * driver is ready and waiting for it. > + */ > + if (port->custom_divisor & (1 << 16)) > + priv->mdr1 = UART_OMAP_MDR1_13X_MODE; > + else > + priv->mdr1 = UART_OMAP_MDR1_16X_MODE; > + return; > + } > + div_13 = DIV_ROUND_CLOSEST(uartclk, 13 * baud); > + div_16 = DIV_ROUND_CLOSEST(uartclk, 16 * baud); > + > + abs_d13 = abs(baud - port->uartclk / 13 / div_13); > + abs_d16 = abs(baud - port->uartclk / 16 / div_16); > + > + if (abs_d13 >= abs_d16) { > + priv->mdr1 = UART_OMAP_MDR1_16X_MODE; > + priv->quot = div_16; > + } else { > + priv->mdr1 = UART_OMAP_MDR1_13X_MODE; > + priv->quot = div_13; > + } > +} > + > +static void omap8250_update_scr(struct uart_8250_port *up, > + struct omap8250_priv *priv) > +{ > + /* > + * The manual recommends not to enable the DMA mode selector in the SCR > + * (instead of the FCR) register _and_ selecting the DMA mode as one > + * register write because this may lead to malfunction. > + */ > + if (priv->scr & OMAP_UART_SCR_DMAMODE_MASK) > + serial_out(up, UART_OMAP_SCR, > + priv->scr & ~OMAP_UART_SCR_DMAMODE_MASK); > + serial_out(up, UART_OMAP_SCR, priv->scr); > +} > + > +static void omap8250_restore_regs(struct uart_8250_port *up) > +{ > + struct omap8250_priv *priv = up->port.private_data; > + > + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); > + serial_out(up, UART_DLL, 0); > + serial_out(up, UART_DLM, 0); > + > + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); > + serial_out(up, UART_EFR, UART_EFR_ECB); > + > + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); > + serial_out(up, UART_MCR, UART_MCR_TCRTLR); > + serial_out(up, UART_FCR, up->fcr); > + > + omap8250_update_scr(up, priv); > + > + /* Protocol, Baud Rate, and Interrupt Settings */ > + /* need mode A for FCR */ > + if (priv->habit & UART_ERRATA_i202_MDR1_ACCESS) > + omap_8250_mdr1_errataset(up, UART_OMAP_MDR1_DISABLE); > + else > + serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_DISABLE); > + > + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); > + > + serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_RESTORE(16) | > + OMAP_UART_TCR_HALT(52)); > + serial_out(up, UART_TI752_TLR, > + TRIGGER_TLR_MASK(TX_TRIGGER) << UART_TI752_TLR_TX | > + TRIGGER_TLR_MASK(RX_TRIGGER) << UART_TI752_TLR_RX); > + > + serial_out(up, UART_LCR, 0); > + > + /* drop TCR + TLR access, we setup XON/XOFF later */ > + serial_out(up, UART_MCR, up->mcr); > + serial_out(up, UART_IER, 0); > + > + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); > + serial_dl_write(up, priv->quot); > + > + serial_out(up, UART_EFR, priv->efr); > + > + serial_out(up, UART_LCR, up->lcr); > + /* need mode A for FCR */ > + if (priv->habit & UART_ERRATA_i202_MDR1_ACCESS) > + omap_8250_mdr1_errataset(up, priv->mdr1); > + else > + serial_out(up, UART_OMAP_MDR1, priv->mdr1); > + > + /* Configure flow control */ > + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); > + serial_out(up, UART_XON1, priv->xon); > + serial_out(up, UART_XOFF1, priv->xoff); > + > + serial_out(up, UART_LCR, up->lcr); > + up->port.ops->set_mctrl(&up->port, up->port.mctrl); > +} > +/* > + * OMAP can use "CLK / (16 or 13) / div" for baud rate. And then we have have > + * some differences in how we want to handle flow control. > + */ > +static void omap_8250_set_termios(struct uart_port *port, > + struct ktermios *termios, struct ktermios *old) > +{ > + struct uart_8250_port *up = > + container_of(port, struct uart_8250_port, port); > + struct omap8250_priv *priv = up->port.private_data; > + unsigned char cval = 0; > + unsigned long flags = 0; > + unsigned int baud; > + > + switch (termios->c_cflag & CSIZE) { > + case CS5: > + cval = UART_LCR_WLEN5; > + break; > + case CS6: > + cval = UART_LCR_WLEN6; > + break; > + case CS7: > + cval = UART_LCR_WLEN7; > + break; > + default: > + case CS8: > + cval = UART_LCR_WLEN8; > + break; > + } > + > + if (termios->c_cflag & CSTOPB) > + cval |= UART_LCR_STOP; > + if (termios->c_cflag & PARENB) > + cval |= UART_LCR_PARITY; > + if (!(termios->c_cflag & PARODD)) > + cval |= UART_LCR_EPAR; > + if (termios->c_cflag & CMSPAR) > + cval |= UART_LCR_SPAR; > + > + /* > + * Ask the core to calculate the divisor for us. > + */ > + baud = uart_get_baud_rate(port, termios, old, > + port->uartclk / 16 / 0xffff, > + port->uartclk / 13); > + omap_8250_get_divisor(port, baud, priv); > + > + /* > + * Ok, we're now changing the port state. Do it with > + * interrupts disabled. > + */ > + pm_runtime_get_sync(port->dev); > + spin_lock_irqsave(&port->lock, flags); ^^^ spin_lock_irq(&port->lock); The serial core calls the ->set_termios() method with interrupts enabled. > + > + /* > + * Update the per-port timeout. > + */ > + uart_update_timeout(port, termios->c_cflag, baud); > + > + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; > + if (termios->c_iflag & INPCK) > + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; > + if (termios->c_iflag & (BRKINT | PARMRK)) ^ IGNBRK | Otherwise, the read_status_mask will mask out the BI condition, so uart_insert_char() will send '\0' byte as TTY_NORMAL. The 8250 and omap RX path differed so the omap driver didn't need this change, whereas the 8250 driver does. > + up->port.read_status_mask |= UART_LSR_BI; > + > + /* > + * Characters to ignore > + */ > + up->port.ignore_status_mask = 0; > + if (termios->c_iflag & IGNPAR) > + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; > + if (termios->c_iflag & IGNBRK) { > + up->port.ignore_status_mask |= UART_LSR_BI; > + /* > + * If we're ignoring parity and break indicators, > + * ignore overruns too (for real raw support). > + */ > + if (termios->c_iflag & IGNPAR) > + up->port.ignore_status_mask |= UART_LSR_OE; > + } > + > + /* > + * ignore all characters if CREAD is not set > + */ > + if ((termios->c_cflag & CREAD) == 0) > + up->port.ignore_status_mask |= UART_LSR_DR; > + > + /* > + * Modem status interrupts > + */ > + up->ier &= ~UART_IER_MSI; > + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) > + up->ier |= UART_IER_MSI; > + > + up->lcr = cval; > + /* Up to here it was mostly serial8250_do_set_termios() */ > + > + /* > + * We enable TRIG_GRANU for RX and TX and additionaly we set > + * SCR_TX_EMPTY bit. The result is the following: > + * - RX_TRIGGER amount of bytes in the FIFO will cause an interrupt. > + * - less than RX_TRIGGER number of bytes will also cause an interrupt > + * once the UART decides that there no new bytes arriving. > + * - Once THRE is enabled, the interrupt will be fired once the FIFO is > + * empty - the trigger level is ignored here. > + * > + * Once DMA is enabled: > + * - UART will assert the TX DMA line once there is room for TX_TRIGGER > + * bytes in the TX FIFO. On each assert the DMA engine will move > + * TX_TRIGGER bytes into the FIFO. > + * - UART will assert the RX DMA line once there are RX_TRIGGER bytes in > + * the FIFO and move RX_TRIGGER bytes. > + * This is because treshold and trigger values are the same. > + */ > + up->fcr = UART_FCR_ENABLE_FIFO; > + up->fcr |= TRIGGER_FCR_MASK(TX_TRIGGER) << OMAP_UART_FCR_TX_TRIG; > + up->fcr |= TRIGGER_FCR_MASK(RX_TRIGGER) << OMAP_UART_FCR_RX_TRIG; > + > + priv->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK | OMAP_UART_SCR_TX_EMPTY | > + OMAP_UART_SCR_TX_TRIG_GRANU1_MASK; > + > + priv->xon = termios->c_cc[VSTART]; > + priv->xoff = termios->c_cc[VSTOP]; > + > + priv->efr = 0; > + if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { > + /* Enable AUTORTS and AUTOCTS */ > + priv->efr |= UART_EFR_CTS | UART_EFR_RTS; > + > + /* Ensure MCR RTS is asserted */ > + up->mcr |= UART_MCR_RTS; > + } > + > + if (up->port.flags & UPF_SOFT_FLOW) { I'm aware that this is basically from the omap driver but can someone clear up if omap hardware can actually do UPF_HARD_FLOW and UPF_SOFT_FLOW simultaneously? The datasheets that I've looked at say no. Regards, Peter Hurley > + /* > + * IXON Flag: > + * Enable XON/XOFF flow control on input. > + * Receiver compares XON1, XOFF1. > + */ > + if (termios->c_iflag & IXON) > + priv->efr |= OMAP_UART_SW_RX; > + > + /* > + * IXOFF Flag: > + * Enable XON/XOFF flow control on output. > + * Transmit XON1, XOFF1 > + */ > + if (termios->c_iflag & IXOFF) > + priv->efr |= OMAP_UART_SW_TX; > + > + /* > + * IXANY Flag: > + * Enable any character to restart output. > + * Operation resumes after receiving any > + * character after recognition of the XOFF character > + */ > + if (termios->c_iflag & IXANY) > + up->mcr |= UART_MCR_XONANY; > + else > + up->mcr &= ~UART_MCR_XONANY; > + } > + omap8250_restore_regs(up); > + > + spin_unlock_irqrestore(&up->port.lock, flags); > + pm_runtime_mark_last_busy(port->dev); > + pm_runtime_put_autosuspend(port->dev); > + > + /* calculate wakeup latency constraint */ > + priv->calc_latency = USEC_PER_SEC * 64 * 8 / baud; > + priv->latency = priv->calc_latency; > + > + schedule_work(&priv->qos_work); > + > + /* Don't rewrite B0 */ > + if (tty_termios_baud_rate(termios)) > + tty_termios_encode_baud_rate(termios, baud, baud); > +} > + > +/* same as 8250 except that we may have extra flow bits set in EFR */ > +static void omap_8250_pm(struct uart_port *port, unsigned int state, > + unsigned int oldstate) > +{ > + struct uart_8250_port *up = > + container_of(port, struct uart_8250_port, port); > + struct omap8250_priv *priv = up->port.private_data; > + > + pm_runtime_get_sync(port->dev); > + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); > + serial_out(up, UART_EFR, priv->efr | UART_EFR_ECB); > + serial_out(up, UART_LCR, 0); > + > + serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0); > + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); > + serial_out(up, UART_EFR, priv->efr); > + serial_out(up, UART_LCR, 0); > + > + pm_runtime_mark_last_busy(port->dev); > + pm_runtime_put_autosuspend(port->dev); > +} > + > +static void omap_serial_fill_features_erratas(struct uart_8250_port *up, > + struct omap8250_priv *priv) > +{ > + u32 mvr, scheme; > + u16 revision, major, minor; > + > + mvr = uart_read(up, UART_OMAP_MVER); > + > + /* Check revision register scheme */ > + scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT; > + > + switch (scheme) { > + case 0: /* Legacy Scheme: OMAP2/3 */ > + /* MINOR_REV[0:4], MAJOR_REV[4:7] */ > + major = (mvr & OMAP_UART_LEGACY_MVR_MAJ_MASK) >> > + OMAP_UART_LEGACY_MVR_MAJ_SHIFT; > + minor = (mvr & OMAP_UART_LEGACY_MVR_MIN_MASK); > + break; > + case 1: > + /* New Scheme: OMAP4+ */ > + /* MINOR_REV[0:5], MAJOR_REV[8:10] */ > + major = (mvr & OMAP_UART_MVR_MAJ_MASK) >> > + OMAP_UART_MVR_MAJ_SHIFT; > + minor = (mvr & OMAP_UART_MVR_MIN_MASK); > + break; > + default: > + dev_warn(up->port.dev, > + "Unknown revision, defaulting to highest\n"); > + /* highest possible revision */ > + major = 0xff; > + minor = 0xff; > + } > + /* normalize revision for the driver */ > + revision = UART_BUILD_REVISION(major, minor); > + > + switch (revision) { > + case OMAP_UART_REV_46: > + priv->habit = UART_ERRATA_i202_MDR1_ACCESS; > + break; > + case OMAP_UART_REV_52: > + priv->habit = UART_ERRATA_i202_MDR1_ACCESS | > + OMAP_UART_WER_HAS_TX_WAKEUP; > + break; > + case OMAP_UART_REV_63: > + priv->habit = UART_ERRATA_i202_MDR1_ACCESS | > + OMAP_UART_WER_HAS_TX_WAKEUP; > + break; > + default: > + break; > + } > +} > + > +static void omap8250_uart_qos_work(struct work_struct *work) > +{ > + struct omap8250_priv *priv; > + > + priv = container_of(work, struct omap8250_priv, qos_work); > + pm_qos_update_request(&priv->pm_qos_request, priv->latency); > +} > + > +static irqreturn_t omap_wake_irq(int irq, void *dev_id) > +{ > + struct uart_port *port = dev_id; > + int ret; > + > + ret = port->handle_irq(port); > + if (ret) > + return IRQ_HANDLED; > + return IRQ_NONE; > +} > + > +static int omap_8250_startup(struct uart_port *port) > +{ > + struct uart_8250_port *up = > + container_of(port, struct uart_8250_port, port); > + struct omap8250_priv *priv = port->private_data; > + > + int ret; > + > + if (priv->wakeirq) { > + ret = request_irq(priv->wakeirq, omap_wake_irq, > + port->irqflags, "wakeup irq", port); > + if (ret) > + return ret; > + disable_irq(priv->wakeirq); > + } > + > + pm_runtime_get_sync(port->dev); > + > + ret = serial8250_do_startup(port); > + if (ret) > + goto err; > + > +#ifdef CONFIG_PM_RUNTIME > + up->capabilities |= UART_CAP_RPM; > +#endif > + > + /* Enable module level wake up */ > + priv->wer = OMAP_UART_WER_MOD_WKUP; > + if (priv->habit & OMAP_UART_WER_HAS_TX_WAKEUP) > + priv->wer |= OMAP_UART_TX_WAKEUP_EN; > + serial_out(up, UART_OMAP_WER, priv->wer); > + > + pm_runtime_mark_last_busy(port->dev); > + pm_runtime_put_autosuspend(port->dev); > + return 0; > +err: > + pm_runtime_mark_last_busy(port->dev); > + pm_runtime_put_autosuspend(port->dev); > + if (priv->wakeirq) > + free_irq(priv->wakeirq, port); > + return ret; > +} > + > +static void omap_8250_shutdown(struct uart_port *port) > +{ > + struct uart_8250_port *up = > + container_of(port, struct uart_8250_port, port); > + struct omap8250_priv *priv = port->private_data; > + > + flush_work(&priv->qos_work); > + > + pm_runtime_get_sync(port->dev); > + > + serial_out(up, UART_OMAP_WER, 0); > + serial8250_do_shutdown(port); > + > + pm_runtime_mark_last_busy(port->dev); > + pm_runtime_put_autosuspend(port->dev); > + > + if (priv->wakeirq) > + free_irq(priv->wakeirq, port); > +} > + > +static void omap_8250_throttle(struct uart_port *port) > +{ > + unsigned long flags; > + struct uart_8250_port *up = > + container_of(port, struct uart_8250_port, port); > + > + pm_runtime_get_sync(port->dev); > + > + spin_lock_irqsave(&port->lock, flags); > + up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); > + serial_out(up, UART_IER, up->ier); > + spin_unlock_irqrestore(&port->lock, flags); > + > + pm_runtime_mark_last_busy(port->dev); > + pm_runtime_put_autosuspend(port->dev); > +} > + > +static void omap_8250_unthrottle(struct uart_port *port) > +{ > + unsigned long flags; > + struct uart_8250_port *up = > + container_of(port, struct uart_8250_port, port); > + > + pm_runtime_get_sync(port->dev); > + > + spin_lock_irqsave(&port->lock, flags); > + up->ier |= UART_IER_RLSI | UART_IER_RDI; > + serial_out(up, UART_IER, up->ier); > + spin_unlock_irqrestore(&port->lock, flags); > + > + pm_runtime_mark_last_busy(port->dev); > + pm_runtime_put_autosuspend(port->dev); > +} > + > +static int omap8250_probe(struct platform_device *pdev) > +{ > + struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > + struct omap8250_priv *priv; > + struct uart_8250_port up; > + int ret; > + void __iomem *membase; > + > + if (!regs || !irq) { > + dev_err(&pdev->dev, "missing registers or irq\n"); > + return -EINVAL; > + } > + > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + membase = devm_ioremap_nocache(&pdev->dev, regs->start, > + resource_size(regs)); > + if (!membase) > + return -ENODEV; > + > + memset(&up, 0, sizeof(up)); > + up.port.dev = &pdev->dev; > + up.port.mapbase = regs->start; > + up.port.membase = membase; > + up.port.irq = irq->start; > + /* > + * It claims to be 16C750 compatible however it is a little different. > + * It has EFR and has no FCR7_64byte bit. The AFE (which it claims to > + * have) is enabled via EFR instead of MCR. The type is set here 8250 > + * just to get things going. UNKNOWN does not work for a few reasons and > + * we don't need our own type since we don't use 8250's set_termios() > + * and our "bugs" are handeld via the bug member. > + */ > + up.port.type = PORT_8250; > + up.port.iotype = UPIO_MEM; > + up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SOFT_FLOW | > + UPF_HARD_FLOW; > + up.port.private_data = priv; > + > + up.port.regshift = 2; > + up.port.fifosize = 64; > + up.tx_loadsz = 64; > + up.capabilities = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP; > +#ifdef CONFIG_PM_RUNTIME > + /* > + * PM_RUNTIME is mostly transparent. However to do it right we need to a > + * TX empty interrupt before we can put the device to auto idle. So if > + * PM_RUNTIME is not enabled we don't add that flag and can spare that > + * one extra interrupt in the TX path. > + */ > + up.capabilities |= UART_CAP_RPM; > +#endif > + up.port.set_termios = omap_8250_set_termios; > + up.port.pm = omap_8250_pm; > + up.port.startup = omap_8250_startup; > + up.port.shutdown = omap_8250_shutdown; > + up.port.throttle = omap_8250_throttle; > + up.port.unthrottle = omap_8250_unthrottle; > + > + if (pdev->dev.of_node) { > + up.port.line = of_alias_get_id(pdev->dev.of_node, "serial"); > + of_property_read_u32(pdev->dev.of_node, "clock-frequency", > + &up.port.uartclk); > + priv->wakeirq = irq_of_parse_and_map(pdev->dev.of_node, 1); > + } else { > + up.port.line = pdev->id; > + } > + > + if (up.port.line < 0) { > + dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n", > + up.port.line); > + return -ENODEV; > + } > + if (!up.port.uartclk) { > + up.port.uartclk = DEFAULT_CLK_SPEED; > + dev_warn(&pdev->dev, > + "No clock speed specified: using default: %d\n", > + DEFAULT_CLK_SPEED); > + } > + > + priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; > + priv->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; > + pm_qos_add_request(&priv->pm_qos_request, > + PM_QOS_CPU_DMA_LATENCY, priv->latency); > + INIT_WORK(&priv->qos_work, omap8250_uart_qos_work); > + > + device_init_wakeup(&pdev->dev, true); > + pm_runtime_use_autosuspend(&pdev->dev); > + pm_runtime_set_autosuspend_delay(&pdev->dev, -1); > + > + pm_runtime_irq_safe(&pdev->dev); > + pm_runtime_enable(&pdev->dev); > + > + pm_runtime_get_sync(&pdev->dev); > + > + omap_serial_fill_features_erratas(&up, priv); > + ret = serial8250_register_8250_port(&up); > + if (ret < 0) { > + dev_err(&pdev->dev, "unable to register 8250 port\n"); > + goto err; > + } > + priv->line = ret; > + platform_set_drvdata(pdev, priv); > + pm_runtime_mark_last_busy(&pdev->dev); > + pm_runtime_put_autosuspend(&pdev->dev); > + return 0; > +err: > + pm_runtime_put(&pdev->dev); > + pm_runtime_disable(&pdev->dev); > + return ret; > +} > + > +static int omap8250_remove(struct platform_device *pdev) > +{ > + struct omap8250_priv *priv = platform_get_drvdata(pdev); > + > + pm_runtime_put_sync(&pdev->dev); > + pm_runtime_disable(&pdev->dev); > + serial8250_unregister_port(priv->line); > + pm_qos_remove_request(&priv->pm_qos_request); > + device_init_wakeup(&pdev->dev, false); > + return 0; > +} > + > +#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME) > + > +static inline void omap8250_enable_wakeirq(struct omap8250_priv *priv, > + bool enable) > +{ > + if (!priv->wakeirq) > + return; > + > + if (enable) > + enable_irq(priv->wakeirq); > + else > + disable_irq_nosync(priv->wakeirq); > +} > + > +static void omap8250_enable_wakeup(struct omap8250_priv *priv, > + bool enable) > +{ > + if (enable == priv->wakeups_enabled) > + return; > + > + omap8250_enable_wakeirq(priv, enable); > + priv->wakeups_enabled = enable; > +} > +#endif > + > +#ifdef CONFIG_PM_SLEEP > +static int omap8250_prepare(struct device *dev) > +{ > + struct omap8250_priv *priv = dev_get_drvdata(dev); > + > + if (!priv) > + return 0; > + priv->is_suspending = true; > + return 0; > +} > + > +static void omap8250_complete(struct device *dev) > +{ > + struct omap8250_priv *priv = dev_get_drvdata(dev); > + > + if (!priv) > + return; > + priv->is_suspending = false; > +} > + > +static int omap8250_suspend(struct device *dev) > +{ > + struct omap8250_priv *priv = dev_get_drvdata(dev); > + > + serial8250_suspend_port(priv->line); > + flush_work(&priv->qos_work); > + > + if (device_may_wakeup(dev)) > + omap8250_enable_wakeup(priv, true); > + else > + omap8250_enable_wakeup(priv, false); > + return 0; > +} > + > +static int omap8250_resume(struct device *dev) > +{ > + struct omap8250_priv *priv = dev_get_drvdata(dev); > + > + if (device_may_wakeup(dev)) > + omap8250_enable_wakeup(priv, false); > + > + serial8250_resume_port(priv->line); > + return 0; > +} > +#else > +#define omap8250_prepare NULL > +#define omap8250_complete NULL > +#endif > + > +#ifdef CONFIG_PM_RUNTIME > +static int omap8250_lost_context(struct uart_8250_port *up) > +{ > + u32 val; > + > + val = serial_in(up, UART_OMAP_MDR1); > + /* > + * If we lose context, then MDR1 is set to its reset value which is > + * UART_OMAP_MDR1_DISABLE. After set_termios() we set it either to 13x > + * or 16x but never to disable again. > + */ > + if (val == UART_OMAP_MDR1_DISABLE) > + return 1; > + return 0; > +} > + > +static int omap8250_runtime_suspend(struct device *dev) > +{ > + struct omap8250_priv *priv = dev_get_drvdata(dev); > + struct uart_8250_port *up; > + > + up = serial8250_get_port(priv->line); > + /* > + * When using 'no_console_suspend', the console UART must not be > + * suspended. Since driver suspend is managed by runtime suspend, > + * preventing runtime suspend (by returning error) will keep device > + * active during suspend. > + */ > + if (priv->is_suspending && !console_suspend_enabled) { > + if (uart_console(&up->port)) > + return -EBUSY; > + } > + > + omap8250_enable_wakeup(priv, true); > + > + priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; > + schedule_work(&priv->qos_work); > + > + return 0; > +} > + > +static int omap8250_runtime_resume(struct device *dev) > +{ > + struct omap8250_priv *priv = dev_get_drvdata(dev); > + struct uart_8250_port *up; > + int loss_cntx; > + > + /* In case runtime-pm tries this before we are setup */ > + if (!priv) > + return 0; > + > + up = serial8250_get_port(priv->line); > + omap8250_enable_wakeup(priv, false); > + loss_cntx = omap8250_lost_context(up); > + > + if (loss_cntx) > + omap8250_restore_regs(up); > + > + priv->latency = priv->calc_latency; > + schedule_work(&priv->qos_work); > + return 0; > +} > +#endif > + > +static const struct dev_pm_ops omap8250_dev_pm_ops = { > + SET_SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume) > + SET_RUNTIME_PM_OPS(omap8250_runtime_suspend, > + omap8250_runtime_resume, NULL) > + .prepare = omap8250_prepare, > + .complete = omap8250_complete, > +}; > + > +static const struct of_device_id omap8250_dt_ids[] = { > + { .compatible = "ti,omap2-uart" }, > + { .compatible = "ti,omap3-uart" }, > + { .compatible = "ti,omap4-uart" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, omap8250_dt_ids); > + > +static struct platform_driver omap8250_platform_driver = { > + .driver = { > + .name = "omap8250", > + .pm = &omap8250_dev_pm_ops, > + .of_match_table = omap8250_dt_ids, > + .owner = THIS_MODULE, > + }, > + .probe = omap8250_probe, > + .remove = omap8250_remove, > +}; > +module_platform_driver(omap8250_platform_driver); > + > +MODULE_AUTHOR("Sebastian Andrzej Siewior"); > +MODULE_DESCRIPTION("OMAP 8250 Driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig > index 21eca79224e4..bb1b7119ecf9 100644 > --- a/drivers/tty/serial/8250/Kconfig > +++ b/drivers/tty/serial/8250/Kconfig > @@ -299,6 +299,15 @@ config SERIAL_8250_RT288X > serial port, say Y to this option. The driver can handle up to 2 serial > ports. If unsure, say N. > > +config SERIAL_8250_OMAP > + tristate "Support for OMAP internal UART (8250 based driver)" > + depends on SERIAL_8250 && ARCH_OMAP2PLUS > + help > + If you have a machine based on an Texas Instruments OMAP CPU you > + can enable its onboard serial ports by enabling this option. > + > + This driver is in early stage and uses ttyS instead of ttyO. > + > config SERIAL_8250_FINTEK > tristate "Support for Fintek F81216A LPC to 4 UART" > depends on SERIAL_8250 && PNP > diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile > index 5256b894e46a..31e7cdc6865c 100644 > --- a/drivers/tty/serial/8250/Makefile > +++ b/drivers/tty/serial/8250/Makefile > @@ -20,5 +20,6 @@ obj-$(CONFIG_SERIAL_8250_HUB6) += 8250_hub6.o > obj-$(CONFIG_SERIAL_8250_FSL) += 8250_fsl.o > obj-$(CONFIG_SERIAL_8250_DW) += 8250_dw.o > obj-$(CONFIG_SERIAL_8250_EM) += 8250_em.o > +obj-$(CONFIG_SERIAL_8250_OMAP) += 8250_omap.o > obj-$(CONFIG_SERIAL_8250_FINTEK) += 8250_fintek.o > obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o >