linux-stable/drivers/tty/serial/msm_serial.c
Stephan Gerhold 173ebdedcd serial: msm: Use OPP table for DVFS support
Parse the OPP table from the device tree and use dev_pm_opp_set_rate()
instead of clk_set_rate() to allow making performance state votes
specified in the OPP table (e.g. for power domains and interconnects).

Without an OPP table in the device tree this will behave just as before
this patch.

Signed-off-by: Stephan Gerhold <stephan.gerhold@kernkonzept.com>
Link: https://lore.kernel.org/r/20231128-serial-msm-dvfs-v1-2-4f290d20a4be@kernkonzept.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-12-08 12:02:37 +01:00

1935 lines
47 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Driver for msm7k serial device and console
*
* Copyright (C) 2007 Google, Inc.
* Author: Robert Love <rlove@google.com>
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/atomic.h>
#include <linux/dma/qcom_adm.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/wait.h>
#define MSM_UART_MR1 0x0000
#define MSM_UART_MR1_AUTO_RFR_LEVEL0 0x3F
#define MSM_UART_MR1_AUTO_RFR_LEVEL1 0x3FF00
#define MSM_UART_DM_MR1_AUTO_RFR_LEVEL1 0xFFFFFF00
#define MSM_UART_MR1_RX_RDY_CTL BIT(7)
#define MSM_UART_MR1_CTS_CTL BIT(6)
#define MSM_UART_MR2 0x0004
#define MSM_UART_MR2_ERROR_MODE BIT(6)
#define MSM_UART_MR2_BITS_PER_CHAR 0x30
#define MSM_UART_MR2_BITS_PER_CHAR_5 (0x0 << 4)
#define MSM_UART_MR2_BITS_PER_CHAR_6 (0x1 << 4)
#define MSM_UART_MR2_BITS_PER_CHAR_7 (0x2 << 4)
#define MSM_UART_MR2_BITS_PER_CHAR_8 (0x3 << 4)
#define MSM_UART_MR2_STOP_BIT_LEN_ONE (0x1 << 2)
#define MSM_UART_MR2_STOP_BIT_LEN_TWO (0x3 << 2)
#define MSM_UART_MR2_PARITY_MODE_NONE 0x0
#define MSM_UART_MR2_PARITY_MODE_ODD 0x1
#define MSM_UART_MR2_PARITY_MODE_EVEN 0x2
#define MSM_UART_MR2_PARITY_MODE_SPACE 0x3
#define MSM_UART_MR2_PARITY_MODE 0x3
#define MSM_UART_CSR 0x0008
#define MSM_UART_TF 0x000C
#define UARTDM_TF 0x0070
#define MSM_UART_CR 0x0010
#define MSM_UART_CR_CMD_NULL (0 << 4)
#define MSM_UART_CR_CMD_RESET_RX (1 << 4)
#define MSM_UART_CR_CMD_RESET_TX (2 << 4)
#define MSM_UART_CR_CMD_RESET_ERR (3 << 4)
#define MSM_UART_CR_CMD_RESET_BREAK_INT (4 << 4)
#define MSM_UART_CR_CMD_START_BREAK (5 << 4)
#define MSM_UART_CR_CMD_STOP_BREAK (6 << 4)
#define MSM_UART_CR_CMD_RESET_CTS (7 << 4)
#define MSM_UART_CR_CMD_RESET_STALE_INT (8 << 4)
#define MSM_UART_CR_CMD_PACKET_MODE (9 << 4)
#define MSM_UART_CR_CMD_MODE_RESET (12 << 4)
#define MSM_UART_CR_CMD_SET_RFR (13 << 4)
#define MSM_UART_CR_CMD_RESET_RFR (14 << 4)
#define MSM_UART_CR_CMD_PROTECTION_EN (16 << 4)
#define MSM_UART_CR_CMD_STALE_EVENT_DISABLE (6 << 8)
#define MSM_UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4)
#define MSM_UART_CR_CMD_FORCE_STALE (4 << 8)
#define MSM_UART_CR_CMD_RESET_TX_READY (3 << 8)
#define MSM_UART_CR_TX_DISABLE BIT(3)
#define MSM_UART_CR_TX_ENABLE BIT(2)
#define MSM_UART_CR_RX_DISABLE BIT(1)
#define MSM_UART_CR_RX_ENABLE BIT(0)
#define MSM_UART_CR_CMD_RESET_RXBREAK_START ((1 << 11) | (2 << 4))
#define MSM_UART_IMR 0x0014
#define MSM_UART_IMR_TXLEV BIT(0)
#define MSM_UART_IMR_RXSTALE BIT(3)
#define MSM_UART_IMR_RXLEV BIT(4)
#define MSM_UART_IMR_DELTA_CTS BIT(5)
#define MSM_UART_IMR_CURRENT_CTS BIT(6)
#define MSM_UART_IMR_RXBREAK_START BIT(10)
#define MSM_UART_IPR_RXSTALE_LAST 0x20
#define MSM_UART_IPR_STALE_LSB 0x1F
#define MSM_UART_IPR_STALE_TIMEOUT_MSB 0x3FF80
#define MSM_UART_DM_IPR_STALE_TIMEOUT_MSB 0xFFFFFF80
#define MSM_UART_IPR 0x0018
#define MSM_UART_TFWR 0x001C
#define MSM_UART_RFWR 0x0020
#define MSM_UART_HCR 0x0024
#define MSM_UART_MREG 0x0028
#define MSM_UART_NREG 0x002C
#define MSM_UART_DREG 0x0030
#define MSM_UART_MNDREG 0x0034
#define MSM_UART_IRDA 0x0038
#define MSM_UART_MISR_MODE 0x0040
#define MSM_UART_MISR_RESET 0x0044
#define MSM_UART_MISR_EXPORT 0x0048
#define MSM_UART_MISR_VAL 0x004C
#define MSM_UART_TEST_CTRL 0x0050
#define MSM_UART_SR 0x0008
#define MSM_UART_SR_HUNT_CHAR BIT(7)
#define MSM_UART_SR_RX_BREAK BIT(6)
#define MSM_UART_SR_PAR_FRAME_ERR BIT(5)
#define MSM_UART_SR_OVERRUN BIT(4)
#define MSM_UART_SR_TX_EMPTY BIT(3)
#define MSM_UART_SR_TX_READY BIT(2)
#define MSM_UART_SR_RX_FULL BIT(1)
#define MSM_UART_SR_RX_READY BIT(0)
#define MSM_UART_RF 0x000C
#define UARTDM_RF 0x0070
#define MSM_UART_MISR 0x0010
#define MSM_UART_ISR 0x0014
#define MSM_UART_ISR_TX_READY BIT(7)
#define UARTDM_RXFS 0x50
#define UARTDM_RXFS_BUF_SHIFT 0x7
#define UARTDM_RXFS_BUF_MASK 0x7
#define UARTDM_DMEN 0x3C
#define UARTDM_DMEN_RX_SC_ENABLE BIT(5)
#define UARTDM_DMEN_TX_SC_ENABLE BIT(4)
#define UARTDM_DMEN_TX_BAM_ENABLE BIT(2) /* UARTDM_1P4 */
#define UARTDM_DMEN_TX_DM_ENABLE BIT(0) /* < UARTDM_1P4 */
#define UARTDM_DMEN_RX_BAM_ENABLE BIT(3) /* UARTDM_1P4 */
#define UARTDM_DMEN_RX_DM_ENABLE BIT(1) /* < UARTDM_1P4 */
#define UARTDM_DMRX 0x34
#define UARTDM_NCF_TX 0x40
#define UARTDM_RX_TOTAL_SNAP 0x38
#define UARTDM_BURST_SIZE 16 /* in bytes */
#define UARTDM_TX_AIGN(x) ((x) & ~0x3) /* valid for > 1p3 */
#define UARTDM_TX_MAX 256 /* in bytes, valid for <= 1p3 */
#define UARTDM_RX_SIZE (UART_XMIT_SIZE / 4)
enum {
UARTDM_1P1 = 1,
UARTDM_1P2,
UARTDM_1P3,
UARTDM_1P4,
};
struct msm_dma {
struct dma_chan *chan;
enum dma_data_direction dir;
dma_addr_t phys;
unsigned char *virt;
dma_cookie_t cookie;
u32 enable_bit;
unsigned int count;
struct dma_async_tx_descriptor *desc;
};
struct msm_port {
struct uart_port uart;
char name[16];
struct clk *clk;
struct clk *pclk;
unsigned int imr;
int is_uartdm;
unsigned int old_snap_state;
bool break_detected;
struct msm_dma tx_dma;
struct msm_dma rx_dma;
};
static inline struct msm_port *to_msm_port(struct uart_port *up)
{
return container_of(up, struct msm_port, uart);
}
static
void msm_write(struct uart_port *port, unsigned int val, unsigned int off)
{
writel_relaxed(val, port->membase + off);
}
static
unsigned int msm_read(struct uart_port *port, unsigned int off)
{
return readl_relaxed(port->membase + off);
}
/*
* Setup the MND registers to use the TCXO clock.
*/
static void msm_serial_set_mnd_regs_tcxo(struct uart_port *port)
{
msm_write(port, 0x06, MSM_UART_MREG);
msm_write(port, 0xF1, MSM_UART_NREG);
msm_write(port, 0x0F, MSM_UART_DREG);
msm_write(port, 0x1A, MSM_UART_MNDREG);
port->uartclk = 1843200;
}
/*
* Setup the MND registers to use the TCXO clock divided by 4.
*/
static void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port)
{
msm_write(port, 0x18, MSM_UART_MREG);
msm_write(port, 0xF6, MSM_UART_NREG);
msm_write(port, 0x0F, MSM_UART_DREG);
msm_write(port, 0x0A, MSM_UART_MNDREG);
port->uartclk = 1843200;
}
static void msm_serial_set_mnd_regs(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
/*
* These registers don't exist so we change the clk input rate
* on uartdm hardware instead
*/
if (msm_port->is_uartdm)
return;
if (port->uartclk == 19200000)
msm_serial_set_mnd_regs_tcxo(port);
else if (port->uartclk == 4800000)
msm_serial_set_mnd_regs_tcxoby4(port);
}
static void msm_handle_tx(struct uart_port *port);
static void msm_start_rx_dma(struct msm_port *msm_port);
static void msm_stop_dma(struct uart_port *port, struct msm_dma *dma)
{
struct device *dev = port->dev;
unsigned int mapped;
u32 val;
mapped = dma->count;
dma->count = 0;
dmaengine_terminate_all(dma->chan);
/*
* DMA Stall happens if enqueue and flush command happens concurrently.
* For example before changing the baud rate/protocol configuration and
* sending flush command to ADM, disable the channel of UARTDM.
* Note: should not reset the receiver here immediately as it is not
* suggested to do disable/reset or reset/disable at the same time.
*/
val = msm_read(port, UARTDM_DMEN);
val &= ~dma->enable_bit;
msm_write(port, val, UARTDM_DMEN);
if (mapped)
dma_unmap_single(dev, dma->phys, mapped, dma->dir);
}
static void msm_release_dma(struct msm_port *msm_port)
{
struct msm_dma *dma;
dma = &msm_port->tx_dma;
if (dma->chan) {
msm_stop_dma(&msm_port->uart, dma);
dma_release_channel(dma->chan);
}
memset(dma, 0, sizeof(*dma));
dma = &msm_port->rx_dma;
if (dma->chan) {
msm_stop_dma(&msm_port->uart, dma);
dma_release_channel(dma->chan);
kfree(dma->virt);
}
memset(dma, 0, sizeof(*dma));
}
static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base)
{
struct device *dev = msm_port->uart.dev;
struct dma_slave_config conf;
struct qcom_adm_peripheral_config periph_conf = {};
struct msm_dma *dma;
u32 crci = 0;
int ret;
dma = &msm_port->tx_dma;
/* allocate DMA resources, if available */
dma->chan = dma_request_chan(dev, "tx");
if (IS_ERR(dma->chan))
goto no_tx;
of_property_read_u32(dev->of_node, "qcom,tx-crci", &crci);
memset(&conf, 0, sizeof(conf));
conf.direction = DMA_MEM_TO_DEV;
conf.device_fc = true;
conf.dst_addr = base + UARTDM_TF;
conf.dst_maxburst = UARTDM_BURST_SIZE;
if (crci) {
conf.peripheral_config = &periph_conf;
conf.peripheral_size = sizeof(periph_conf);
periph_conf.crci = crci;
}
ret = dmaengine_slave_config(dma->chan, &conf);
if (ret)
goto rel_tx;
dma->dir = DMA_TO_DEVICE;
if (msm_port->is_uartdm < UARTDM_1P4)
dma->enable_bit = UARTDM_DMEN_TX_DM_ENABLE;
else
dma->enable_bit = UARTDM_DMEN_TX_BAM_ENABLE;
return;
rel_tx:
dma_release_channel(dma->chan);
no_tx:
memset(dma, 0, sizeof(*dma));
}
static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base)
{
struct device *dev = msm_port->uart.dev;
struct dma_slave_config conf;
struct qcom_adm_peripheral_config periph_conf = {};
struct msm_dma *dma;
u32 crci = 0;
int ret;
dma = &msm_port->rx_dma;
/* allocate DMA resources, if available */
dma->chan = dma_request_chan(dev, "rx");
if (IS_ERR(dma->chan))
goto no_rx;
of_property_read_u32(dev->of_node, "qcom,rx-crci", &crci);
dma->virt = kzalloc(UARTDM_RX_SIZE, GFP_KERNEL);
if (!dma->virt)
goto rel_rx;
memset(&conf, 0, sizeof(conf));
conf.direction = DMA_DEV_TO_MEM;
conf.device_fc = true;
conf.src_addr = base + UARTDM_RF;
conf.src_maxburst = UARTDM_BURST_SIZE;
if (crci) {
conf.peripheral_config = &periph_conf;
conf.peripheral_size = sizeof(periph_conf);
periph_conf.crci = crci;
}
ret = dmaengine_slave_config(dma->chan, &conf);
if (ret)
goto err;
dma->dir = DMA_FROM_DEVICE;
if (msm_port->is_uartdm < UARTDM_1P4)
dma->enable_bit = UARTDM_DMEN_RX_DM_ENABLE;
else
dma->enable_bit = UARTDM_DMEN_RX_BAM_ENABLE;
return;
err:
kfree(dma->virt);
rel_rx:
dma_release_channel(dma->chan);
no_rx:
memset(dma, 0, sizeof(*dma));
}
static inline void msm_wait_for_xmitr(struct uart_port *port)
{
unsigned int timeout = 500000;
while (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_EMPTY)) {
if (msm_read(port, MSM_UART_ISR) & MSM_UART_ISR_TX_READY)
break;
udelay(1);
if (!timeout--)
break;
}
msm_write(port, MSM_UART_CR_CMD_RESET_TX_READY, MSM_UART_CR);
}
static void msm_stop_tx(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
msm_port->imr &= ~MSM_UART_IMR_TXLEV;
msm_write(port, msm_port->imr, MSM_UART_IMR);
}
static void msm_start_tx(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
struct msm_dma *dma = &msm_port->tx_dma;
/* Already started in DMA mode */
if (dma->count)
return;
msm_port->imr |= MSM_UART_IMR_TXLEV;
msm_write(port, msm_port->imr, MSM_UART_IMR);
}
static void msm_reset_dm_count(struct uart_port *port, int count)
{
msm_wait_for_xmitr(port);
msm_write(port, count, UARTDM_NCF_TX);
msm_read(port, UARTDM_NCF_TX);
}
static void msm_complete_tx_dma(void *args)
{
struct msm_port *msm_port = args;
struct uart_port *port = &msm_port->uart;
struct circ_buf *xmit = &port->state->xmit;
struct msm_dma *dma = &msm_port->tx_dma;
struct dma_tx_state state;
unsigned long flags;
unsigned int count;
u32 val;
uart_port_lock_irqsave(port, &flags);
/* Already stopped */
if (!dma->count)
goto done;
dmaengine_tx_status(dma->chan, dma->cookie, &state);
dma_unmap_single(port->dev, dma->phys, dma->count, dma->dir);
val = msm_read(port, UARTDM_DMEN);
val &= ~dma->enable_bit;
msm_write(port, val, UARTDM_DMEN);
if (msm_port->is_uartdm > UARTDM_1P3) {
msm_write(port, MSM_UART_CR_CMD_RESET_TX, MSM_UART_CR);
msm_write(port, MSM_UART_CR_TX_ENABLE, MSM_UART_CR);
}
count = dma->count - state.residue;
uart_xmit_advance(port, count);
dma->count = 0;
/* Restore "Tx FIFO below watermark" interrupt */
msm_port->imr |= MSM_UART_IMR_TXLEV;
msm_write(port, msm_port->imr, MSM_UART_IMR);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
msm_handle_tx(port);
done:
uart_port_unlock_irqrestore(port, flags);
}
static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
{
struct circ_buf *xmit = &msm_port->uart.state->xmit;
struct uart_port *port = &msm_port->uart;
struct msm_dma *dma = &msm_port->tx_dma;
void *cpu_addr;
int ret;
u32 val;
cpu_addr = &xmit->buf[xmit->tail];
dma->phys = dma_map_single(port->dev, cpu_addr, count, dma->dir);
ret = dma_mapping_error(port->dev, dma->phys);
if (ret)
return ret;
dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys,
count, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT |
DMA_PREP_FENCE);
if (!dma->desc) {
ret = -EIO;
goto unmap;
}
dma->desc->callback = msm_complete_tx_dma;
dma->desc->callback_param = msm_port;
dma->cookie = dmaengine_submit(dma->desc);
ret = dma_submit_error(dma->cookie);
if (ret)
goto unmap;
/*
* Using DMA complete for Tx FIFO reload, no need for
* "Tx FIFO below watermark" one, disable it
*/
msm_port->imr &= ~MSM_UART_IMR_TXLEV;
msm_write(port, msm_port->imr, MSM_UART_IMR);
dma->count = count;
val = msm_read(port, UARTDM_DMEN);
val |= dma->enable_bit;
if (msm_port->is_uartdm < UARTDM_1P4)
msm_write(port, val, UARTDM_DMEN);
msm_reset_dm_count(port, count);
if (msm_port->is_uartdm > UARTDM_1P3)
msm_write(port, val, UARTDM_DMEN);
dma_async_issue_pending(dma->chan);
return 0;
unmap:
dma_unmap_single(port->dev, dma->phys, count, dma->dir);
return ret;
}
static void msm_complete_rx_dma(void *args)
{
struct msm_port *msm_port = args;
struct uart_port *port = &msm_port->uart;
struct tty_port *tport = &port->state->port;
struct msm_dma *dma = &msm_port->rx_dma;
int count = 0, i, sysrq;
unsigned long flags;
u32 val;
uart_port_lock_irqsave(port, &flags);
/* Already stopped */
if (!dma->count)
goto done;
val = msm_read(port, UARTDM_DMEN);
val &= ~dma->enable_bit;
msm_write(port, val, UARTDM_DMEN);
if (msm_read(port, MSM_UART_SR) & MSM_UART_SR_OVERRUN) {
port->icount.overrun++;
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
msm_write(port, MSM_UART_CR_CMD_RESET_ERR, MSM_UART_CR);
}
count = msm_read(port, UARTDM_RX_TOTAL_SNAP);
port->icount.rx += count;
dma->count = 0;
dma_unmap_single(port->dev, dma->phys, UARTDM_RX_SIZE, dma->dir);
for (i = 0; i < count; i++) {
char flag = TTY_NORMAL;
if (msm_port->break_detected && dma->virt[i] == 0) {
port->icount.brk++;
flag = TTY_BREAK;
msm_port->break_detected = false;
if (uart_handle_break(port))
continue;
}
if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK))
flag = TTY_NORMAL;
uart_port_unlock_irqrestore(port, flags);
sysrq = uart_handle_sysrq_char(port, dma->virt[i]);
uart_port_lock_irqsave(port, &flags);
if (!sysrq)
tty_insert_flip_char(tport, dma->virt[i], flag);
}
msm_start_rx_dma(msm_port);
done:
uart_port_unlock_irqrestore(port, flags);
if (count)
tty_flip_buffer_push(tport);
}
static void msm_start_rx_dma(struct msm_port *msm_port)
{
struct msm_dma *dma = &msm_port->rx_dma;
struct uart_port *uart = &msm_port->uart;
u32 val;
int ret;
if (IS_ENABLED(CONFIG_CONSOLE_POLL))
return;
if (!dma->chan)
return;
dma->phys = dma_map_single(uart->dev, dma->virt,
UARTDM_RX_SIZE, dma->dir);
ret = dma_mapping_error(uart->dev, dma->phys);
if (ret)
goto sw_mode;
dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys,
UARTDM_RX_SIZE, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
if (!dma->desc)
goto unmap;
dma->desc->callback = msm_complete_rx_dma;
dma->desc->callback_param = msm_port;
dma->cookie = dmaengine_submit(dma->desc);
ret = dma_submit_error(dma->cookie);
if (ret)
goto unmap;
/*
* Using DMA for FIFO off-load, no need for "Rx FIFO over
* watermark" or "stale" interrupts, disable them
*/
msm_port->imr &= ~(MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE);
/*
* Well, when DMA is ADM3 engine(implied by <= UARTDM v1.3),
* we need RXSTALE to flush input DMA fifo to memory
*/
if (msm_port->is_uartdm < UARTDM_1P4)
msm_port->imr |= MSM_UART_IMR_RXSTALE;
msm_write(uart, msm_port->imr, MSM_UART_IMR);
dma->count = UARTDM_RX_SIZE;
dma_async_issue_pending(dma->chan);
msm_write(uart, MSM_UART_CR_CMD_RESET_STALE_INT, MSM_UART_CR);
msm_write(uart, MSM_UART_CR_CMD_STALE_EVENT_ENABLE, MSM_UART_CR);
val = msm_read(uart, UARTDM_DMEN);
val |= dma->enable_bit;
if (msm_port->is_uartdm < UARTDM_1P4)
msm_write(uart, val, UARTDM_DMEN);
msm_write(uart, UARTDM_RX_SIZE, UARTDM_DMRX);
if (msm_port->is_uartdm > UARTDM_1P3)
msm_write(uart, val, UARTDM_DMEN);
return;
unmap:
dma_unmap_single(uart->dev, dma->phys, UARTDM_RX_SIZE, dma->dir);
sw_mode:
/*
* Switch from DMA to SW/FIFO mode. After clearing Rx BAM (UARTDM_DMEN),
* receiver must be reset.
*/
msm_write(uart, MSM_UART_CR_CMD_RESET_RX, MSM_UART_CR);
msm_write(uart, MSM_UART_CR_RX_ENABLE, MSM_UART_CR);
msm_write(uart, MSM_UART_CR_CMD_RESET_STALE_INT, MSM_UART_CR);
msm_write(uart, 0xFFFFFF, UARTDM_DMRX);
msm_write(uart, MSM_UART_CR_CMD_STALE_EVENT_ENABLE, MSM_UART_CR);
/* Re-enable RX interrupts */
msm_port->imr |= MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE;
msm_write(uart, msm_port->imr, MSM_UART_IMR);
}
static void msm_stop_rx(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
struct msm_dma *dma = &msm_port->rx_dma;
msm_port->imr &= ~(MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE);
msm_write(port, msm_port->imr, MSM_UART_IMR);
if (dma->chan)
msm_stop_dma(port, dma);
}
static void msm_enable_ms(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
msm_port->imr |= MSM_UART_IMR_DELTA_CTS;
msm_write(port, msm_port->imr, MSM_UART_IMR);
}
static void msm_handle_rx_dm(struct uart_port *port, unsigned int misr)
__must_hold(&port->lock)
{
struct tty_port *tport = &port->state->port;
unsigned int sr;
int count = 0;
struct msm_port *msm_port = to_msm_port(port);
if ((msm_read(port, MSM_UART_SR) & MSM_UART_SR_OVERRUN)) {
port->icount.overrun++;
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
msm_write(port, MSM_UART_CR_CMD_RESET_ERR, MSM_UART_CR);
}
if (misr & MSM_UART_IMR_RXSTALE) {
count = msm_read(port, UARTDM_RX_TOTAL_SNAP) -
msm_port->old_snap_state;
msm_port->old_snap_state = 0;
} else {
count = 4 * (msm_read(port, MSM_UART_RFWR));
msm_port->old_snap_state += count;
}
/* TODO: Precise error reporting */
port->icount.rx += count;
while (count > 0) {
unsigned char buf[4];
int sysrq, r_count, i;
sr = msm_read(port, MSM_UART_SR);
if ((sr & MSM_UART_SR_RX_READY) == 0) {
msm_port->old_snap_state -= count;
break;
}
ioread32_rep(port->membase + UARTDM_RF, buf, 1);
r_count = min_t(int, count, sizeof(buf));
for (i = 0; i < r_count; i++) {
char flag = TTY_NORMAL;
if (msm_port->break_detected && buf[i] == 0) {
port->icount.brk++;
flag = TTY_BREAK;
msm_port->break_detected = false;
if (uart_handle_break(port))
continue;
}
if (!(port->read_status_mask & MSM_UART_SR_RX_BREAK))
flag = TTY_NORMAL;
uart_port_unlock(port);
sysrq = uart_handle_sysrq_char(port, buf[i]);
uart_port_lock(port);
if (!sysrq)
tty_insert_flip_char(tport, buf[i], flag);
}
count -= r_count;
}
tty_flip_buffer_push(tport);
if (misr & (MSM_UART_IMR_RXSTALE))
msm_write(port, MSM_UART_CR_CMD_RESET_STALE_INT, MSM_UART_CR);
msm_write(port, 0xFFFFFF, UARTDM_DMRX);
msm_write(port, MSM_UART_CR_CMD_STALE_EVENT_ENABLE, MSM_UART_CR);
/* Try to use DMA */
msm_start_rx_dma(msm_port);
}
static void msm_handle_rx(struct uart_port *port)
__must_hold(&port->lock)
{
struct tty_port *tport = &port->state->port;
unsigned int sr;
/*
* Handle overrun. My understanding of the hardware is that overrun
* is not tied to the RX buffer, so we handle the case out of band.
*/
if ((msm_read(port, MSM_UART_SR) & MSM_UART_SR_OVERRUN)) {
port->icount.overrun++;
tty_insert_flip_char(tport, 0, TTY_OVERRUN);
msm_write(port, MSM_UART_CR_CMD_RESET_ERR, MSM_UART_CR);
}
/* and now the main RX loop */
while ((sr = msm_read(port, MSM_UART_SR)) & MSM_UART_SR_RX_READY) {
unsigned int c;
char flag = TTY_NORMAL;
int sysrq;
c = msm_read(port, MSM_UART_RF);
if (sr & MSM_UART_SR_RX_BREAK) {
port->icount.brk++;
if (uart_handle_break(port))
continue;
} else if (sr & MSM_UART_SR_PAR_FRAME_ERR) {
port->icount.frame++;
} else {
port->icount.rx++;
}
/* Mask conditions we're ignoring. */
sr &= port->read_status_mask;
if (sr & MSM_UART_SR_RX_BREAK)
flag = TTY_BREAK;
else if (sr & MSM_UART_SR_PAR_FRAME_ERR)
flag = TTY_FRAME;
uart_port_unlock(port);
sysrq = uart_handle_sysrq_char(port, c);
uart_port_lock(port);
if (!sysrq)
tty_insert_flip_char(tport, c, flag);
}
tty_flip_buffer_push(tport);
}
static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count)
{
struct circ_buf *xmit = &port->state->xmit;
struct msm_port *msm_port = to_msm_port(port);
unsigned int num_chars;
unsigned int tf_pointer = 0;
void __iomem *tf;
if (msm_port->is_uartdm)
tf = port->membase + UARTDM_TF;
else
tf = port->membase + MSM_UART_TF;
if (tx_count && msm_port->is_uartdm)
msm_reset_dm_count(port, tx_count);
while (tf_pointer < tx_count) {
int i;
char buf[4] = { 0 };
if (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_READY))
break;
if (msm_port->is_uartdm)
num_chars = min(tx_count - tf_pointer,
(unsigned int)sizeof(buf));
else
num_chars = 1;
for (i = 0; i < num_chars; i++)
buf[i] = xmit->buf[xmit->tail + i];
iowrite32_rep(tf, buf, 1);
uart_xmit_advance(port, num_chars);
tf_pointer += num_chars;
}
/* disable tx interrupts if nothing more to send */
if (uart_circ_empty(xmit))
msm_stop_tx(port);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
}
static void msm_handle_tx(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
struct circ_buf *xmit = &msm_port->uart.state->xmit;
struct msm_dma *dma = &msm_port->tx_dma;
unsigned int pio_count, dma_count, dma_min;
char buf[4] = { 0 };
void __iomem *tf;
int err = 0;
if (port->x_char) {
if (msm_port->is_uartdm)
tf = port->membase + UARTDM_TF;
else
tf = port->membase + MSM_UART_TF;
buf[0] = port->x_char;
if (msm_port->is_uartdm)
msm_reset_dm_count(port, 1);
iowrite32_rep(tf, buf, 1);
port->icount.tx++;
port->x_char = 0;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
msm_stop_tx(port);
return;
}
pio_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
dma_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
dma_min = 1; /* Always DMA */
if (msm_port->is_uartdm > UARTDM_1P3) {
dma_count = UARTDM_TX_AIGN(dma_count);
dma_min = UARTDM_BURST_SIZE;
} else {
if (dma_count > UARTDM_TX_MAX)
dma_count = UARTDM_TX_MAX;
}
if (pio_count > port->fifosize)
pio_count = port->fifosize;
if (!dma->chan || dma_count < dma_min)
msm_handle_tx_pio(port, pio_count);
else
err = msm_handle_tx_dma(msm_port, dma_count);
if (err) /* fall back to PIO mode */
msm_handle_tx_pio(port, pio_count);
}
static void msm_handle_delta_cts(struct uart_port *port)
{
msm_write(port, MSM_UART_CR_CMD_RESET_CTS, MSM_UART_CR);
port->icount.cts++;
wake_up_interruptible(&port->state->port.delta_msr_wait);
}
static irqreturn_t msm_uart_irq(int irq, void *dev_id)
{
struct uart_port *port = dev_id;
struct msm_port *msm_port = to_msm_port(port);
struct msm_dma *dma = &msm_port->rx_dma;
unsigned long flags;
unsigned int misr;
u32 val;
uart_port_lock_irqsave(port, &flags);
misr = msm_read(port, MSM_UART_MISR);
msm_write(port, 0, MSM_UART_IMR); /* disable interrupt */
if (misr & MSM_UART_IMR_RXBREAK_START) {
msm_port->break_detected = true;
msm_write(port, MSM_UART_CR_CMD_RESET_RXBREAK_START, MSM_UART_CR);
}
if (misr & (MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE)) {
if (dma->count) {
val = MSM_UART_CR_CMD_STALE_EVENT_DISABLE;
msm_write(port, val, MSM_UART_CR);
val = MSM_UART_CR_CMD_RESET_STALE_INT;
msm_write(port, val, MSM_UART_CR);
/*
* Flush DMA input fifo to memory, this will also
* trigger DMA RX completion
*/
dmaengine_terminate_all(dma->chan);
} else if (msm_port->is_uartdm) {
msm_handle_rx_dm(port, misr);
} else {
msm_handle_rx(port);
}
}
if (misr & MSM_UART_IMR_TXLEV)
msm_handle_tx(port);
if (misr & MSM_UART_IMR_DELTA_CTS)
msm_handle_delta_cts(port);
msm_write(port, msm_port->imr, MSM_UART_IMR); /* restore interrupt */
uart_port_unlock_irqrestore(port, flags);
return IRQ_HANDLED;
}
static unsigned int msm_tx_empty(struct uart_port *port)
{
return (msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0;
}
static unsigned int msm_get_mctrl(struct uart_port *port)
{
return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS;
}
static void msm_reset(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
unsigned int mr;
/* reset everything */
msm_write(port, MSM_UART_CR_CMD_RESET_RX, MSM_UART_CR);
msm_write(port, MSM_UART_CR_CMD_RESET_TX, MSM_UART_CR);
msm_write(port, MSM_UART_CR_CMD_RESET_ERR, MSM_UART_CR);
msm_write(port, MSM_UART_CR_CMD_RESET_BREAK_INT, MSM_UART_CR);
msm_write(port, MSM_UART_CR_CMD_RESET_CTS, MSM_UART_CR);
msm_write(port, MSM_UART_CR_CMD_RESET_RFR, MSM_UART_CR);
mr = msm_read(port, MSM_UART_MR1);
mr &= ~MSM_UART_MR1_RX_RDY_CTL;
msm_write(port, mr, MSM_UART_MR1);
/* Disable DM modes */
if (msm_port->is_uartdm)
msm_write(port, 0, UARTDM_DMEN);
}
static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
unsigned int mr;
mr = msm_read(port, MSM_UART_MR1);
if (!(mctrl & TIOCM_RTS)) {
mr &= ~MSM_UART_MR1_RX_RDY_CTL;
msm_write(port, mr, MSM_UART_MR1);
msm_write(port, MSM_UART_CR_CMD_RESET_RFR, MSM_UART_CR);
} else {
mr |= MSM_UART_MR1_RX_RDY_CTL;
msm_write(port, mr, MSM_UART_MR1);
}
}
static void msm_break_ctl(struct uart_port *port, int break_ctl)
{
if (break_ctl)
msm_write(port, MSM_UART_CR_CMD_START_BREAK, MSM_UART_CR);
else
msm_write(port, MSM_UART_CR_CMD_STOP_BREAK, MSM_UART_CR);
}
struct msm_baud_map {
u16 divisor;
u8 code;
u8 rxstale;
};
static const struct msm_baud_map *
msm_find_best_baud(struct uart_port *port, unsigned int baud,
unsigned long *rate)
{
struct msm_port *msm_port = to_msm_port(port);
unsigned int divisor, result;
unsigned long target, old, best_rate = 0, diff, best_diff = ULONG_MAX;
const struct msm_baud_map *entry, *end, *best;
static const struct msm_baud_map table[] = {
{ 1, 0xff, 31 },
{ 2, 0xee, 16 },
{ 3, 0xdd, 8 },
{ 4, 0xcc, 6 },
{ 6, 0xbb, 6 },
{ 8, 0xaa, 6 },
{ 12, 0x99, 6 },
{ 16, 0x88, 1 },
{ 24, 0x77, 1 },
{ 32, 0x66, 1 },
{ 48, 0x55, 1 },
{ 96, 0x44, 1 },
{ 192, 0x33, 1 },
{ 384, 0x22, 1 },
{ 768, 0x11, 1 },
{ 1536, 0x00, 1 },
};
best = table; /* Default to smallest divider */
target = clk_round_rate(msm_port->clk, 16 * baud);
divisor = DIV_ROUND_CLOSEST(target, 16 * baud);
end = table + ARRAY_SIZE(table);
entry = table;
while (entry < end) {
if (entry->divisor <= divisor) {
result = target / entry->divisor / 16;
diff = abs(result - baud);
/* Keep track of best entry */
if (diff < best_diff) {
best_diff = diff;
best = entry;
best_rate = target;
}
if (result == baud)
break;
} else if (entry->divisor > divisor) {
old = target;
target = clk_round_rate(msm_port->clk, old + 1);
/*
* The rate didn't get any faster so we can't do
* better at dividing it down
*/
if (target == old)
break;
/* Start the divisor search over at this new rate */
entry = table;
divisor = DIV_ROUND_CLOSEST(target, 16 * baud);
continue;
}
entry++;
}
*rate = best_rate;
return best;
}
static int msm_set_baud_rate(struct uart_port *port, unsigned int baud,
unsigned long *saved_flags)
__must_hold(&port->lock)
{
unsigned int rxstale, watermark, mask;
struct msm_port *msm_port = to_msm_port(port);
const struct msm_baud_map *entry;
unsigned long flags, rate;
flags = *saved_flags;
uart_port_unlock_irqrestore(port, flags);
entry = msm_find_best_baud(port, baud, &rate);
dev_pm_opp_set_rate(port->dev, rate);
baud = rate / 16 / entry->divisor;
uart_port_lock_irqsave(port, &flags);
*saved_flags = flags;
port->uartclk = rate;
msm_write(port, entry->code, MSM_UART_CSR);
/* RX stale watermark */
rxstale = entry->rxstale;
watermark = MSM_UART_IPR_STALE_LSB & rxstale;
if (msm_port->is_uartdm) {
mask = MSM_UART_DM_IPR_STALE_TIMEOUT_MSB;
} else {
watermark |= MSM_UART_IPR_RXSTALE_LAST;
mask = MSM_UART_IPR_STALE_TIMEOUT_MSB;
}
watermark |= mask & (rxstale << 2);
msm_write(port, watermark, MSM_UART_IPR);
/* set RX watermark */
watermark = (port->fifosize * 3) / 4;
msm_write(port, watermark, MSM_UART_RFWR);
/* set TX watermark */
msm_write(port, 10, MSM_UART_TFWR);
msm_write(port, MSM_UART_CR_CMD_PROTECTION_EN, MSM_UART_CR);
msm_reset(port);
/* Enable RX and TX */
msm_write(port, MSM_UART_CR_TX_ENABLE | MSM_UART_CR_RX_ENABLE, MSM_UART_CR);
/* turn on RX and CTS interrupts */
msm_port->imr = MSM_UART_IMR_RXLEV | MSM_UART_IMR_RXSTALE |
MSM_UART_IMR_CURRENT_CTS | MSM_UART_IMR_RXBREAK_START;
msm_write(port, msm_port->imr, MSM_UART_IMR);
if (msm_port->is_uartdm) {
msm_write(port, MSM_UART_CR_CMD_RESET_STALE_INT, MSM_UART_CR);
msm_write(port, 0xFFFFFF, UARTDM_DMRX);
msm_write(port, MSM_UART_CR_CMD_STALE_EVENT_ENABLE, MSM_UART_CR);
}
return baud;
}
static void msm_init_clock(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
dev_pm_opp_set_rate(port->dev, port->uartclk);
clk_prepare_enable(msm_port->clk);
clk_prepare_enable(msm_port->pclk);
msm_serial_set_mnd_regs(port);
}
static int msm_startup(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
unsigned int data, rfr_level, mask;
int ret;
snprintf(msm_port->name, sizeof(msm_port->name),
"msm_serial%d", port->line);
msm_init_clock(port);
if (likely(port->fifosize > 12))
rfr_level = port->fifosize - 12;
else
rfr_level = port->fifosize;
/* set automatic RFR level */
data = msm_read(port, MSM_UART_MR1);
if (msm_port->is_uartdm)
mask = MSM_UART_DM_MR1_AUTO_RFR_LEVEL1;
else
mask = MSM_UART_MR1_AUTO_RFR_LEVEL1;
data &= ~mask;
data &= ~MSM_UART_MR1_AUTO_RFR_LEVEL0;
data |= mask & (rfr_level << 2);
data |= MSM_UART_MR1_AUTO_RFR_LEVEL0 & rfr_level;
msm_write(port, data, MSM_UART_MR1);
if (msm_port->is_uartdm) {
msm_request_tx_dma(msm_port, msm_port->uart.mapbase);
msm_request_rx_dma(msm_port, msm_port->uart.mapbase);
}
ret = request_irq(port->irq, msm_uart_irq, IRQF_TRIGGER_HIGH,
msm_port->name, port);
if (unlikely(ret))
goto err_irq;
return 0;
err_irq:
if (msm_port->is_uartdm)
msm_release_dma(msm_port);
clk_disable_unprepare(msm_port->pclk);
clk_disable_unprepare(msm_port->clk);
dev_pm_opp_set_rate(port->dev, 0);
return ret;
}
static void msm_shutdown(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
msm_port->imr = 0;
msm_write(port, 0, MSM_UART_IMR); /* disable interrupts */
if (msm_port->is_uartdm)
msm_release_dma(msm_port);
clk_disable_unprepare(msm_port->clk);
dev_pm_opp_set_rate(port->dev, 0);
free_irq(port->irq, port);
}
static void msm_set_termios(struct uart_port *port, struct ktermios *termios,
const struct ktermios *old)
{
struct msm_port *msm_port = to_msm_port(port);
struct msm_dma *dma = &msm_port->rx_dma;
unsigned long flags;
unsigned int baud, mr;
uart_port_lock_irqsave(port, &flags);
if (dma->chan) /* Terminate if any */
msm_stop_dma(port, dma);
/* calculate and set baud rate */
baud = uart_get_baud_rate(port, termios, old, 300, 4000000);
baud = msm_set_baud_rate(port, baud, &flags);
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
/* calculate parity */
mr = msm_read(port, MSM_UART_MR2);
mr &= ~MSM_UART_MR2_PARITY_MODE;
if (termios->c_cflag & PARENB) {
if (termios->c_cflag & PARODD)
mr |= MSM_UART_MR2_PARITY_MODE_ODD;
else if (termios->c_cflag & CMSPAR)
mr |= MSM_UART_MR2_PARITY_MODE_SPACE;
else
mr |= MSM_UART_MR2_PARITY_MODE_EVEN;
}
/* calculate bits per char */
mr &= ~MSM_UART_MR2_BITS_PER_CHAR;
switch (termios->c_cflag & CSIZE) {
case CS5:
mr |= MSM_UART_MR2_BITS_PER_CHAR_5;
break;
case CS6:
mr |= MSM_UART_MR2_BITS_PER_CHAR_6;
break;
case CS7:
mr |= MSM_UART_MR2_BITS_PER_CHAR_7;
break;
case CS8:
default:
mr |= MSM_UART_MR2_BITS_PER_CHAR_8;
break;
}
/* calculate stop bits */
mr &= ~(MSM_UART_MR2_STOP_BIT_LEN_ONE | MSM_UART_MR2_STOP_BIT_LEN_TWO);
if (termios->c_cflag & CSTOPB)
mr |= MSM_UART_MR2_STOP_BIT_LEN_TWO;
else
mr |= MSM_UART_MR2_STOP_BIT_LEN_ONE;
/* set parity, bits per char, and stop bit */
msm_write(port, mr, MSM_UART_MR2);
/* calculate and set hardware flow control */
mr = msm_read(port, MSM_UART_MR1);
mr &= ~(MSM_UART_MR1_CTS_CTL | MSM_UART_MR1_RX_RDY_CTL);
if (termios->c_cflag & CRTSCTS) {
mr |= MSM_UART_MR1_CTS_CTL;
mr |= MSM_UART_MR1_RX_RDY_CTL;
}
msm_write(port, mr, MSM_UART_MR1);
/* Configure status bits to ignore based on termio flags. */
port->read_status_mask = 0;
if (termios->c_iflag & INPCK)
port->read_status_mask |= MSM_UART_SR_PAR_FRAME_ERR;
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= MSM_UART_SR_RX_BREAK;
uart_update_timeout(port, termios->c_cflag, baud);
/* Try to use DMA */
msm_start_rx_dma(msm_port);
uart_port_unlock_irqrestore(port, flags);
}
static const char *msm_type(struct uart_port *port)
{
return "MSM";
}
static void msm_release_port(struct uart_port *port)
{
struct platform_device *pdev = to_platform_device(port->dev);
struct resource *uart_resource;
resource_size_t size;
uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!uart_resource))
return;
size = resource_size(uart_resource);
release_mem_region(port->mapbase, size);
iounmap(port->membase);
port->membase = NULL;
}
static int msm_request_port(struct uart_port *port)
{
struct platform_device *pdev = to_platform_device(port->dev);
struct resource *uart_resource;
resource_size_t size;
int ret;
uart_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!uart_resource))
return -ENXIO;
size = resource_size(uart_resource);
if (!request_mem_region(port->mapbase, size, "msm_serial"))
return -EBUSY;
port->membase = ioremap(port->mapbase, size);
if (!port->membase) {
ret = -EBUSY;
goto fail_release_port;
}
return 0;
fail_release_port:
release_mem_region(port->mapbase, size);
return ret;
}
static void msm_config_port(struct uart_port *port, int flags)
{
int ret;
if (flags & UART_CONFIG_TYPE) {
port->type = PORT_MSM;
ret = msm_request_port(port);
if (ret)
return;
}
}
static int msm_verify_port(struct uart_port *port, struct serial_struct *ser)
{
if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_MSM))
return -EINVAL;
if (unlikely(port->irq != ser->irq))
return -EINVAL;
return 0;
}
static void msm_power(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
struct msm_port *msm_port = to_msm_port(port);
switch (state) {
case 0:
dev_pm_opp_set_rate(port->dev, port->uartclk);
clk_prepare_enable(msm_port->clk);
clk_prepare_enable(msm_port->pclk);
break;
case 3:
clk_disable_unprepare(msm_port->clk);
dev_pm_opp_set_rate(port->dev, 0);
clk_disable_unprepare(msm_port->pclk);
break;
default:
pr_err("msm_serial: Unknown PM state %d\n", state);
}
}
#ifdef CONFIG_CONSOLE_POLL
static int msm_poll_get_char_single(struct uart_port *port)
{
struct msm_port *msm_port = to_msm_port(port);
unsigned int rf_reg = msm_port->is_uartdm ? UARTDM_RF : MSM_UART_RF;
if (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_RX_READY))
return NO_POLL_CHAR;
return msm_read(port, rf_reg) & 0xff;
}
static int msm_poll_get_char_dm(struct uart_port *port)
{
int c;
static u32 slop;
static int count;
unsigned char *sp = (unsigned char *)&slop;
/* Check if a previous read had more than one char */
if (count) {
c = sp[sizeof(slop) - count];
count--;
/* Or if FIFO is empty */
} else if (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_RX_READY)) {
/*
* If RX packing buffer has less than a word, force stale to
* push contents into RX FIFO
*/
count = msm_read(port, UARTDM_RXFS);
count = (count >> UARTDM_RXFS_BUF_SHIFT) & UARTDM_RXFS_BUF_MASK;
if (count) {
msm_write(port, MSM_UART_CR_CMD_FORCE_STALE, MSM_UART_CR);
slop = msm_read(port, UARTDM_RF);
c = sp[0];
count--;
msm_write(port, MSM_UART_CR_CMD_RESET_STALE_INT, MSM_UART_CR);
msm_write(port, 0xFFFFFF, UARTDM_DMRX);
msm_write(port, MSM_UART_CR_CMD_STALE_EVENT_ENABLE, MSM_UART_CR);
} else {
c = NO_POLL_CHAR;
}
/* FIFO has a word */
} else {
slop = msm_read(port, UARTDM_RF);
c = sp[0];
count = sizeof(slop) - 1;
}
return c;
}
static int msm_poll_get_char(struct uart_port *port)
{
u32 imr;
int c;
struct msm_port *msm_port = to_msm_port(port);
/* Disable all interrupts */
imr = msm_read(port, MSM_UART_IMR);
msm_write(port, 0, MSM_UART_IMR);
if (msm_port->is_uartdm)
c = msm_poll_get_char_dm(port);
else
c = msm_poll_get_char_single(port);
/* Enable interrupts */
msm_write(port, imr, MSM_UART_IMR);
return c;
}
static void msm_poll_put_char(struct uart_port *port, unsigned char c)
{
u32 imr;
struct msm_port *msm_port = to_msm_port(port);
/* Disable all interrupts */
imr = msm_read(port, MSM_UART_IMR);
msm_write(port, 0, MSM_UART_IMR);
if (msm_port->is_uartdm)
msm_reset_dm_count(port, 1);
/* Wait until FIFO is empty */
while (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_READY))
cpu_relax();
/* Write a character */
msm_write(port, c, msm_port->is_uartdm ? UARTDM_TF : MSM_UART_TF);
/* Wait until FIFO is empty */
while (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_READY))
cpu_relax();
/* Enable interrupts */
msm_write(port, imr, MSM_UART_IMR);
}
#endif
static const struct uart_ops msm_uart_pops = {
.tx_empty = msm_tx_empty,
.set_mctrl = msm_set_mctrl,
.get_mctrl = msm_get_mctrl,
.stop_tx = msm_stop_tx,
.start_tx = msm_start_tx,
.stop_rx = msm_stop_rx,
.enable_ms = msm_enable_ms,
.break_ctl = msm_break_ctl,
.startup = msm_startup,
.shutdown = msm_shutdown,
.set_termios = msm_set_termios,
.type = msm_type,
.release_port = msm_release_port,
.request_port = msm_request_port,
.config_port = msm_config_port,
.verify_port = msm_verify_port,
.pm = msm_power,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = msm_poll_get_char,
.poll_put_char = msm_poll_put_char,
#endif
};
static struct msm_port msm_uart_ports[] = {
{
.uart = {
.iotype = UPIO_MEM,
.ops = &msm_uart_pops,
.flags = UPF_BOOT_AUTOCONF,
.fifosize = 64,
.line = 0,
},
},
{
.uart = {
.iotype = UPIO_MEM,
.ops = &msm_uart_pops,
.flags = UPF_BOOT_AUTOCONF,
.fifosize = 64,
.line = 1,
},
},
{
.uart = {
.iotype = UPIO_MEM,
.ops = &msm_uart_pops,
.flags = UPF_BOOT_AUTOCONF,
.fifosize = 64,
.line = 2,
},
},
};
#define MSM_UART_NR ARRAY_SIZE(msm_uart_ports)
static inline struct uart_port *msm_get_port_from_line(unsigned int line)
{
return &msm_uart_ports[line].uart;
}
#ifdef CONFIG_SERIAL_MSM_CONSOLE
static void __msm_console_write(struct uart_port *port, const char *s,
unsigned int count, bool is_uartdm)
{
unsigned long flags;
int i;
int num_newlines = 0;
bool replaced = false;
void __iomem *tf;
int locked = 1;
if (is_uartdm)
tf = port->membase + UARTDM_TF;
else
tf = port->membase + MSM_UART_TF;
/* Account for newlines that will get a carriage return added */
for (i = 0; i < count; i++)
if (s[i] == '\n')
num_newlines++;
count += num_newlines;
local_irq_save(flags);
if (port->sysrq)
locked = 0;
else if (oops_in_progress)
locked = uart_port_trylock(port);
else
uart_port_lock(port);
if (is_uartdm)
msm_reset_dm_count(port, count);
i = 0;
while (i < count) {
int j;
unsigned int num_chars;
char buf[4] = { 0 };
if (is_uartdm)
num_chars = min(count - i, (unsigned int)sizeof(buf));
else
num_chars = 1;
for (j = 0; j < num_chars; j++) {
char c = *s;
if (c == '\n' && !replaced) {
buf[j] = '\r';
j++;
replaced = true;
}
if (j < num_chars) {
buf[j] = c;
s++;
replaced = false;
}
}
while (!(msm_read(port, MSM_UART_SR) & MSM_UART_SR_TX_READY))
cpu_relax();
iowrite32_rep(tf, buf, 1);
i += num_chars;
}
if (locked)
uart_port_unlock(port);
local_irq_restore(flags);
}
static void msm_console_write(struct console *co, const char *s,
unsigned int count)
{
struct uart_port *port;
struct msm_port *msm_port;
BUG_ON(co->index < 0 || co->index >= MSM_UART_NR);
port = msm_get_port_from_line(co->index);
msm_port = to_msm_port(port);
__msm_console_write(port, s, count, msm_port->is_uartdm);
}
static int msm_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = 115200;
int bits = 8;
int parity = 'n';
int flow = 'n';
if (unlikely(co->index >= MSM_UART_NR || co->index < 0))
return -ENXIO;
port = msm_get_port_from_line(co->index);
if (unlikely(!port->membase))
return -ENXIO;
msm_init_clock(port);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
pr_info("msm_serial: console setup on port #%d\n", port->line);
return uart_set_options(port, co, baud, parity, bits, flow);
}
static void
msm_serial_early_write(struct console *con, const char *s, unsigned n)
{
struct earlycon_device *dev = con->data;
__msm_console_write(&dev->port, s, n, false);
}
static int __init
msm_serial_early_console_setup(struct earlycon_device *device, const char *opt)
{
if (!device->port.membase)
return -ENODEV;
device->con->write = msm_serial_early_write;
return 0;
}
OF_EARLYCON_DECLARE(msm_serial, "qcom,msm-uart",
msm_serial_early_console_setup);
static void
msm_serial_early_write_dm(struct console *con, const char *s, unsigned n)
{
struct earlycon_device *dev = con->data;
__msm_console_write(&dev->port, s, n, true);
}
static int __init
msm_serial_early_console_setup_dm(struct earlycon_device *device,
const char *opt)
{
if (!device->port.membase)
return -ENODEV;
device->con->write = msm_serial_early_write_dm;
return 0;
}
OF_EARLYCON_DECLARE(msm_serial_dm, "qcom,msm-uartdm",
msm_serial_early_console_setup_dm);
static struct uart_driver msm_uart_driver;
static struct console msm_console = {
.name = "ttyMSM",
.write = msm_console_write,
.device = uart_console_device,
.setup = msm_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &msm_uart_driver,
};
#define MSM_CONSOLE (&msm_console)
#else
#define MSM_CONSOLE NULL
#endif
static struct uart_driver msm_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "msm_serial",
.dev_name = "ttyMSM",
.nr = MSM_UART_NR,
.cons = MSM_CONSOLE,
};
static atomic_t msm_uart_next_id = ATOMIC_INIT(0);
static const struct of_device_id msm_uartdm_table[] = {
{ .compatible = "qcom,msm-uartdm-v1.1", .data = (void *)UARTDM_1P1 },
{ .compatible = "qcom,msm-uartdm-v1.2", .data = (void *)UARTDM_1P2 },
{ .compatible = "qcom,msm-uartdm-v1.3", .data = (void *)UARTDM_1P3 },
{ .compatible = "qcom,msm-uartdm-v1.4", .data = (void *)UARTDM_1P4 },
{ }
};
static int msm_serial_probe(struct platform_device *pdev)
{
struct msm_port *msm_port;
struct resource *resource;
struct uart_port *port;
const struct of_device_id *id;
int irq, line, ret;
if (pdev->dev.of_node)
line = of_alias_get_id(pdev->dev.of_node, "serial");
else
line = pdev->id;
if (line < 0)
line = atomic_inc_return(&msm_uart_next_id) - 1;
if (unlikely(line < 0 || line >= MSM_UART_NR))
return -ENXIO;
dev_info(&pdev->dev, "msm_serial: detected port #%d\n", line);
port = msm_get_port_from_line(line);
port->dev = &pdev->dev;
msm_port = to_msm_port(port);
id = of_match_device(msm_uartdm_table, &pdev->dev);
if (id)
msm_port->is_uartdm = (unsigned long)id->data;
else
msm_port->is_uartdm = 0;
msm_port->clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(msm_port->clk))
return PTR_ERR(msm_port->clk);
if (msm_port->is_uartdm) {
msm_port->pclk = devm_clk_get(&pdev->dev, "iface");
if (IS_ERR(msm_port->pclk))
return PTR_ERR(msm_port->pclk);
}
ret = devm_pm_opp_set_clkname(&pdev->dev, "core");
if (ret)
return ret;
/* OPP table is optional */
ret = devm_pm_opp_of_add_table(&pdev->dev);
if (ret && ret != -ENODEV)
return dev_err_probe(&pdev->dev, ret, "invalid OPP table\n");
port->uartclk = clk_get_rate(msm_port->clk);
dev_info(&pdev->dev, "uartclk = %d\n", port->uartclk);
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!resource))
return -ENXIO;
port->mapbase = resource->start;
irq = platform_get_irq(pdev, 0);
if (unlikely(irq < 0))
return -ENXIO;
port->irq = irq;
port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_MSM_CONSOLE);
platform_set_drvdata(pdev, port);
return uart_add_one_port(&msm_uart_driver, port);
}
static void msm_serial_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
uart_remove_one_port(&msm_uart_driver, port);
}
static const struct of_device_id msm_match_table[] = {
{ .compatible = "qcom,msm-uart" },
{ .compatible = "qcom,msm-uartdm" },
{}
};
MODULE_DEVICE_TABLE(of, msm_match_table);
static int __maybe_unused msm_serial_suspend(struct device *dev)
{
struct msm_port *port = dev_get_drvdata(dev);
uart_suspend_port(&msm_uart_driver, &port->uart);
return 0;
}
static int __maybe_unused msm_serial_resume(struct device *dev)
{
struct msm_port *port = dev_get_drvdata(dev);
uart_resume_port(&msm_uart_driver, &port->uart);
return 0;
}
static const struct dev_pm_ops msm_serial_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(msm_serial_suspend, msm_serial_resume)
};
static struct platform_driver msm_platform_driver = {
.remove_new = msm_serial_remove,
.probe = msm_serial_probe,
.driver = {
.name = "msm_serial",
.pm = &msm_serial_dev_pm_ops,
.of_match_table = msm_match_table,
},
};
static int __init msm_serial_init(void)
{
int ret;
ret = uart_register_driver(&msm_uart_driver);
if (unlikely(ret))
return ret;
ret = platform_driver_register(&msm_platform_driver);
if (unlikely(ret))
uart_unregister_driver(&msm_uart_driver);
pr_info("msm_serial: driver initialized\n");
return ret;
}
static void __exit msm_serial_exit(void)
{
platform_driver_unregister(&msm_platform_driver);
uart_unregister_driver(&msm_uart_driver);
}
module_init(msm_serial_init);
module_exit(msm_serial_exit);
MODULE_AUTHOR("Robert Love <rlove@google.com>");
MODULE_DESCRIPTION("Driver for msm7x serial device");
MODULE_LICENSE("GPL");