--- zzzz-none-000/linux-4.4.271/drivers/tty/serial/msm_serial.c 2021-06-03 06:22:09.000000000 +0000 +++ hawkeye-5590-750/linux-4.4.271/drivers/tty/serial/msm_serial.c 2023-04-19 10:22:29.000000000 +0000 @@ -16,36 +16,150 @@ */ #if defined(CONFIG_SERIAL_MSM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -# define SUPPORT_SYSRQ +#define SUPPORT_SYSRQ #endif +#include #include #include #include -#include #include #include #include -#include +#include #include #include #include #include #include -#include #include #include #include #include #include #include +#include -#include "msm_serial.h" +#define UART_MR1 0x0000 -#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) +#define UART_MR1_AUTO_RFR_LEVEL0 0x3F +#define UART_MR1_AUTO_RFR_LEVEL1 0x3FF00 +#define UART_DM_MR1_AUTO_RFR_LEVEL1 0xFFFFFF00 +#define UART_MR1_RX_RDY_CTL BIT(7) +#define UART_MR1_CTS_CTL BIT(6) + +#define UART_MR2 0x0004 +#define UART_MR2_ERROR_MODE BIT(6) +#define UART_MR2_BITS_PER_CHAR 0x30 +#define UART_MR2_BITS_PER_CHAR_5 (0x0 << 4) +#define UART_MR2_BITS_PER_CHAR_6 (0x1 << 4) +#define UART_MR2_BITS_PER_CHAR_7 (0x2 << 4) +#define UART_MR2_BITS_PER_CHAR_8 (0x3 << 4) +#define UART_MR2_STOP_BIT_LEN_ONE (0x1 << 2) +#define UART_MR2_STOP_BIT_LEN_TWO (0x3 << 2) +#define UART_MR2_PARITY_MODE_NONE 0x0 +#define UART_MR2_PARITY_MODE_ODD 0x1 +#define UART_MR2_PARITY_MODE_EVEN 0x2 +#define UART_MR2_PARITY_MODE_SPACE 0x3 +#define UART_MR2_PARITY_MODE 0x3 + +#define UART_CSR 0x0008 + +#define UART_TF 0x000C +#define UARTDM_TF 0x0070 + +#define UART_CR 0x0010 +#define UART_CR_CMD_NULL (0 << 4) +#define UART_CR_CMD_RESET_RX (1 << 4) +#define UART_CR_CMD_RESET_TX (2 << 4) +#define UART_CR_CMD_RESET_ERR (3 << 4) +#define UART_CR_CMD_RESET_BREAK_INT (4 << 4) +#define UART_CR_CMD_START_BREAK (5 << 4) +#define UART_CR_CMD_STOP_BREAK (6 << 4) +#define UART_CR_CMD_RESET_CTS (7 << 4) +#define UART_CR_CMD_RESET_STALE_INT (8 << 4) +#define UART_CR_CMD_PACKET_MODE (9 << 4) +#define UART_CR_CMD_MODE_RESET (12 << 4) +#define UART_CR_CMD_SET_RFR (13 << 4) +#define UART_CR_CMD_RESET_RFR (14 << 4) +#define UART_CR_CMD_PROTECTION_EN (16 << 4) +#define UART_CR_CMD_STALE_EVENT_DISABLE (6 << 8) +#define UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4) +#define UART_CR_CMD_FORCE_STALE (4 << 8) +#define UART_CR_CMD_RESET_TX_READY (3 << 8) +#define UART_CR_TX_DISABLE BIT(3) +#define UART_CR_TX_ENABLE BIT(2) +#define UART_CR_RX_DISABLE BIT(1) +#define UART_CR_RX_ENABLE BIT(0) +#define UART_CR_CMD_RESET_RXBREAK_START ((1 << 11) | (2 << 4)) + +#define UART_IMR 0x0014 +#define UART_IMR_TXLEV BIT(0) +#define UART_IMR_RXSTALE BIT(3) +#define UART_IMR_RXLEV BIT(4) +#define UART_IMR_DELTA_CTS BIT(5) +#define UART_IMR_CURRENT_CTS BIT(6) +#define UART_IMR_RXBREAK_START BIT(10) + +#define UART_IPR_RXSTALE_LAST 0x20 +#define UART_IPR_STALE_LSB 0x1F +#define UART_IPR_STALE_TIMEOUT_MSB 0x3FF80 +#define UART_DM_IPR_STALE_TIMEOUT_MSB 0xFFFFFF80 + +#define UART_IPR 0x0018 +#define UART_TFWR 0x001C +#define UART_RFWR 0x0020 +#define UART_HCR 0x0024 + +#define UART_MREG 0x0028 +#define UART_NREG 0x002C +#define UART_DREG 0x0030 +#define UART_MNDREG 0x0034 +#define UART_IRDA 0x0038 +#define UART_MISR_MODE 0x0040 +#define UART_MISR_RESET 0x0044 +#define UART_MISR_EXPORT 0x0048 +#define UART_MISR_VAL 0x004C +#define UART_TEST_CTRL 0x0050 + +#define UART_SR 0x0008 +#define UART_SR_HUNT_CHAR BIT(7) +#define UART_SR_RX_BREAK BIT(6) +#define UART_SR_PAR_FRAME_ERR BIT(5) +#define UART_SR_OVERRUN BIT(4) +#define UART_SR_TX_EMPTY BIT(3) +#define UART_SR_TX_READY BIT(2) +#define UART_SR_RX_FULL BIT(1) +#define UART_SR_RX_READY BIT(0) + +#define UART_RF 0x000C +#define UARTDM_RF 0x0070 +#define UART_MISR 0x0010 +#define UART_ISR 0x0014 +#define 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, @@ -55,33 +169,106 @@ }; struct msm_dma { - struct dma_chan *chan; + 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; + 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; + 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; + int pinctrl_rx_only_active; + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_default; + struct pinctrl_state *pinctrl_rx_only; +#if defined(CONFIG_AVM_ENHANCED) + atomic_t reflink; +#endif }; +#if defined(CONFIG_AVM_ENHANCED) +#include "msm_dectuart.h" + +static int msm_serial_dectuart_get_char(void); +static void msm_serial_dectuart_put_char(unsigned char ch); +static void msm_serial_dectuart_init(unsigned int baud, int mode); +static void msm_serial_dectuart_exit(void); +static void msm_serial_console_stop(void); +static void msm_serial_console_start(void); +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ + +#define UART_TO_MSM(uart_port) container_of(uart_port, 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, UART_MREG); + msm_write(port, 0xF1, UART_NREG); + msm_write(port, 0x0F, UART_DREG); + msm_write(port, 0x1A, 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, UART_MREG); + msm_write(port, 0xF6, UART_NREG); + msm_write(port, 0x0F, UART_DREG); + msm_write(port, 0x0A, UART_MNDREG); + port->uartclk = 1843200; +} + +static void msm_serial_set_mnd_regs(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(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); -void msm_stop_dma(struct uart_port *port, struct msm_dma *dma) +static void msm_stop_dma(struct uart_port *port, struct msm_dma *dma) { struct device *dev = port->dev; unsigned int mapped; @@ -247,6 +434,11 @@ struct msm_port *msm_port = UART_TO_MSM(port); struct msm_dma *dma = &msm_port->tx_dma; +#if defined(CONFIG_AVM_ENHANCED) + if ((msm_dectuart.port == port) && msm_dectuart_is_busy(&msm_dectuart)) { + return; + } +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ /* Already started in DMA mode */ if (dma->count) return; @@ -392,10 +584,6 @@ val &= ~dma->enable_bit; msm_write(port, val, UARTDM_DMEN); - /* Restore interrupts */ - msm_port->imr |= UART_IMR_RXLEV | UART_IMR_RXSTALE; - msm_write(port, msm_port->imr, UART_IMR); - if (msm_read(port, UART_SR) & UART_SR_OVERRUN) { port->icount.overrun++; tty_insert_flip_char(tport, 0, TTY_OVERRUN); @@ -424,6 +612,13 @@ if (!(port->read_status_mask & UART_SR_RX_BREAK)) flag = TTY_NORMAL; +#if defined(CONFIG_AVM_ENHANCED) + if ((msm_dectuart.port == port) && msm_dectuart_is_busy(&msm_dectuart)) { + msm_dectuart_pipe_enqueue(&msm_dectuart, dma->virt[i]); + continue; + } +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ + spin_unlock_irqrestore(&port->lock, flags); sysrq = uart_handle_sysrq_char(port, dma->virt[i]); spin_lock_irqsave(&port->lock, flags); @@ -579,6 +774,13 @@ if (!(port->read_status_mask & UART_SR_RX_BREAK)) flag = TTY_NORMAL; +#if defined(CONFIG_AVM_ENHANCED) + if ((msm_dectuart.port == port) && msm_dectuart_is_busy(&msm_dectuart)) { + msm_dectuart_pipe_enqueue(&msm_dectuart, buf[i]); + continue; + } +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ + spin_unlock(&port->lock); sysrq = uart_handle_sysrq_char(port, buf[i]); spin_lock(&port->lock); @@ -711,6 +913,11 @@ void __iomem *tf; int err = 0; +#if defined(CONFIG_AVM_ENHANCED) + if ((msm_dectuart.port == port) && msm_dectuart_is_busy(&msm_dectuart)) { + return; + } +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ if (port->x_char) { if (msm_port->is_uartdm) tf = port->membase + UARTDM_TF; @@ -736,7 +943,7 @@ 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 */ + dma_min = 1; /* Always DMA */ if (msm_port->is_uartdm > UARTDM_1P3) { dma_count = UARTDM_TX_AIGN(dma_count); dma_min = UARTDM_BURST_SIZE; @@ -753,7 +960,7 @@ else err = msm_handle_tx_dma(msm_port, dma_count); - if (err) /* fall back to PIO mode */ + if (err) /* fall back to PIO mode */ msm_handle_tx_pio(port, pio_count); } @@ -775,7 +982,7 @@ spin_lock_irqsave(&port->lock, flags); misr = msm_read(port, UART_MISR); - msm_write(port, 0, UART_IMR); /* disable interrupt */ + msm_write(port, 0, UART_IMR); /* disable interrupt */ if (misr & UART_IMR_RXBREAK_START) { msm_port->break_detected = true; @@ -792,7 +999,13 @@ * Flush DMA input fifo to memory, this will also * trigger DMA RX completion */ - dmaengine_terminate_all(dma->chan); + if (msm_port->is_uartdm >= UARTDM_1P4) { + dmaengine_terminate_all(dma->chan); + } else if (dmaengine_tx_status(dma->chan, dma->cookie, + NULL) != DMA_COMPLETE) { + dmaengine_terminate_all_graceful(dma->chan, + true); + } } else if (msm_port->is_uartdm) { msm_handle_rx_dm(port, misr); } else { @@ -804,7 +1017,7 @@ if (misr & UART_IMR_DELTA_CTS) msm_handle_delta_cts(port); - msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */ + msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */ spin_unlock_irqrestore(&port->lock, flags); return IRQ_HANDLED; @@ -820,14 +1033,17 @@ return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS; } -static void msm_reset(struct uart_port *port) +static void msm_reset(struct uart_port *port, bool reset_tx) { struct msm_port *msm_port = UART_TO_MSM(port); unsigned int mr; /* reset everything */ msm_write(port, UART_CR_CMD_RESET_RX, UART_CR); - msm_write(port, UART_CR_CMD_RESET_TX, UART_CR); + + if (reset_tx) + msm_write(port, UART_CR_CMD_RESET_TX, UART_CR); + msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR); msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR); msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); @@ -866,9 +1082,9 @@ } struct msm_baud_map { - u16 divisor; - u8 code; - u8 rxstale; + u16 divisor; + u8 code; + u8 rxstale; }; static const struct msm_baud_map * @@ -941,11 +1157,13 @@ } static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, - unsigned long *saved_flags) + unsigned long *saved_flags) { unsigned int rxstale, watermark, mask; struct msm_port *msm_port = UART_TO_MSM(port); const struct msm_baud_map *entry; + struct device *dev = msm_port->uart.dev; + u32 tx_watermark = 10; unsigned long flags, rate; flags = *saved_flags; @@ -980,10 +1198,17 @@ msm_write(port, watermark, UART_RFWR); /* set TX watermark */ - msm_write(port, 10, UART_TFWR); + of_property_read_u32(dev->of_node, "tx-watermark", &tx_watermark); + msm_write(port, tx_watermark, UART_TFWR); msm_write(port, UART_CR_CMD_PROTECTION_EN, UART_CR); - msm_reset(port); + + /* + * Check for console in this port and don't do TX reset + * if console is enabled in this port. + */ + msm_reset(port, !(uart_console(port) && + (port->cons->flags & CON_ENABLED))); /* Enable RX and TX */ msm_write(port, UART_CR_TX_ENABLE | UART_CR_RX_ENABLE, UART_CR); @@ -1018,6 +1243,15 @@ unsigned int data, rfr_level, mask; int ret; +#if defined(CONFIG_AVM_ENHANCED) + unsigned int reflink = atomic_inc_return(&msm_port->reflink); + + if (reflink > 1) { + pr_debug("%s add reflink %d\n", __func__, reflink); + return 0; + } + pr_debug("%s ttyMSM%d %d\n", __func__, port->line, reflink); +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ snprintf(msm_port->name, sizeof(msm_port->name), "msm_serial%d", port->line); @@ -1059,8 +1293,18 @@ { struct msm_port *msm_port = UART_TO_MSM(port); +#if defined(CONFIG_AVM_ENHANCED) + unsigned int reflink = atomic_dec_return(&msm_port->reflink); + + if (reflink != 0) { + pr_debug("%s sub reflink %d\n", __func__, reflink); + return; + } + pr_debug("%s ttyMSM%d %d\n", __func__, port->line, reflink); +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ + msm_port->imr = 0; - msm_write(port, 0, UART_IMR); /* disable interrupts */ + msm_write(port, 0, UART_IMR); /* disable interrupts */ if (msm_port->is_uartdm) msm_release_dma(msm_port); @@ -1071,7 +1315,7 @@ } static void msm_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) + struct ktermios *old) { struct msm_port *msm_port = UART_TO_MSM(port); struct msm_dma *dma = &msm_port->rx_dma; @@ -1080,7 +1324,7 @@ spin_lock_irqsave(&port->lock, flags); - if (dma->chan) /* Terminate if any */ + if (dma->chan) /* Terminate if any */ msm_stop_dma(port, dma); /* calculate and set baud rate */ @@ -1225,7 +1469,7 @@ } static void msm_power(struct uart_port *port, unsigned int state, - unsigned int oldstate) + unsigned int oldstate) { struct msm_port *msm_port = UART_TO_MSM(port); @@ -1266,7 +1510,7 @@ if (count) { c = sp[sizeof(slop) - count]; count--; - /* Or if FIFO is empty */ + /* Or if FIFO is empty */ } else if (!(msm_read(port, UART_SR) & UART_SR_RX_READY)) { /* * If RX packing buffer has less than a word, force stale to @@ -1286,7 +1530,7 @@ } else { c = NO_POLL_CHAR; } - /* FIFO has a word */ + /* FIFO has a word */ } else { slop = msm_read(port, UARTDM_RF); c = sp[0]; @@ -1316,7 +1560,9 @@ return c; } +#endif +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_AVM_ENHANCED) static void msm_poll_put_char(struct uart_port *port, unsigned char c) { u32 imr; @@ -1343,9 +1589,9 @@ /* Enable interrupts */ msm_write(port, imr, UART_IMR); } -#endif +#endif/*--- #if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_AVM_ENHANCED) ---*/ -static struct uart_ops msm_uart_pops = { +static const struct uart_ops msm_uart_pops = { .tx_empty = msm_tx_empty, .set_mctrl = msm_set_mctrl, .get_mctrl = msm_get_mctrl, @@ -1364,19 +1610,37 @@ .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, + .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 = 0, + .line = 2, }, }, { @@ -1385,7 +1649,7 @@ .ops = &msm_uart_pops, .flags = UPF_BOOT_AUTOCONF, .fifosize = 64, - .line = 1, + .line = 3, }, }, { @@ -1394,7 +1658,16 @@ .ops = &msm_uart_pops, .flags = UPF_BOOT_AUTOCONF, .fifosize = 64, - .line = 2, + .line = 4, + }, + }, + { + .uart = { + .iotype = UPIO_MEM, + .ops = &msm_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .fifosize = 64, + .line = 5, }, }, }; @@ -1408,13 +1681,21 @@ #ifdef CONFIG_SERIAL_MSM_CONSOLE static void __msm_console_write(struct uart_port *port, const char *s, - unsigned int count, bool is_uartdm) + unsigned int count, bool is_uartdm, + bool is_early) { int i; int num_newlines = 0; bool replaced = false; void __iomem *tf; int locked = 1; + struct msm_port *msm_port; + struct msm_dma *dma; + + if (!is_early) { + msm_port = UART_TO_MSM(port); + dma = &msm_port->tx_dma; + } if (is_uartdm) tf = port->membase + UARTDM_TF; @@ -1434,6 +1715,14 @@ else spin_lock(&port->lock); + + /* + * If any TX DMA operation is ongoing in BAM DMA then console write + * can not be used since it uses the FIFO mode. + */ + if ((!is_early) && (dma->count)) + goto out; + if (is_uartdm) msm_reset_dm_count(port, count); @@ -1469,13 +1758,13 @@ iowrite32_rep(tf, buf, 1); i += num_chars; } - +out: if (locked) spin_unlock(&port->lock); } static void msm_console_write(struct console *co, const char *s, - unsigned int count) + unsigned int count) { struct uart_port *port; struct msm_port *msm_port; @@ -1485,7 +1774,7 @@ port = msm_get_port_from_line(co->index); msm_port = UART_TO_MSM(port); - __msm_console_write(port, s, count, msm_port->is_uartdm); + __msm_console_write(port, s, count, msm_port->is_uartdm, false); } static int __init msm_console_setup(struct console *co, char *options) @@ -1515,11 +1804,11 @@ } static void -msm_serial_early_write(struct console *con, const char *s, unsigned n) +msm_serial_early_write(struct console *con, const char *s, unsigned int n) { struct earlycon_device *dev = con->data; - __msm_console_write(&dev->port, s, n, false); + __msm_console_write(&dev->port, s, n, false, true); } static int __init @@ -1531,16 +1820,15 @@ device->con->write = msm_serial_early_write; return 0; } -EARLYCON_DECLARE(msm_serial, msm_serial_early_console_setup); OF_EARLYCON_DECLARE(msm_serial, "qcom,msm-uart", - msm_serial_early_console_setup); +msm_serial_early_console_setup); static void -msm_serial_early_write_dm(struct console *con, const char *s, unsigned n) +msm_serial_early_write_dm(struct console *con, const char *s, unsigned int n) { struct earlycon_device *dev = con->data; - __msm_console_write(&dev->port, s, n, true); + __msm_console_write(&dev->port, s, n, true, true); } static int __init @@ -1553,9 +1841,8 @@ device->con->write = msm_serial_early_write_dm; return 0; } -EARLYCON_DECLARE(msm_serial_dm, msm_serial_early_console_setup_dm); OF_EARLYCON_DECLARE(msm_serial_dm, "qcom,msm-uartdm", - msm_serial_early_console_setup_dm); + msm_serial_early_console_setup_dm); static struct uart_driver msm_uart_driver; @@ -1586,13 +1873,137 @@ 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 }, - { } + {.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}, + {} }; +#if defined(CONFIG_AVM_ENHANCED) +/** + */ +static void msm_dectuart_probe(struct uart_port *port, int line) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "avm,dectstub_uart"); + + if (np) { + u32 uart_line = 0; + + of_property_read_u32(np, "line", &uart_line); + + if (uart_line == line) { + msm_dectuart.port = port; + } + } else if (msm_dectuart.port == NULL) { + /*--- fallback: 7530/7520: no devicetree-entry ---*/ + msm_dectuart.port = port; + } + if (msm_dectuart.port == port) { + pr_info("%s: set dectuart_port to ttyMSM%d\n", __func__, line); + msm_dectuart.msm_dectuart_get_char = msm_serial_dectuart_get_char; + msm_dectuart.msm_dectuart_put_char = msm_serial_dectuart_put_char; + msm_dectuart.msm_dectuart_init = msm_serial_dectuart_init; + msm_dectuart.msm_console_stop = msm_serial_console_stop; + msm_dectuart.msm_console_start = msm_serial_console_start; + msm_dectuart.msm_dectuart_exit = msm_serial_dectuart_exit; + } +} +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ + +static ssize_t avm_rx_only_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = container_of(dev, struct platform_device, dev); + struct uart_port *port = platform_get_drvdata(pdev); + struct msm_port *msm_port = UART_TO_MSM(port); + + return sprintf(buf, "%d [0 1]\n", msm_port->pinctrl_rx_only_active); +} + +static ssize_t avm_rx_only_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct platform_device *pdev = container_of(dev, struct platform_device, dev); + struct uart_port *port = platform_get_drvdata(pdev); + struct msm_port *msm_port = UART_TO_MSM(port); + struct pinctrl_state *tgt; + long value; + int ret; + + ret = kstrtol(buf, 0, &value); + if (ret != 0 || (value != 0 && value != 1)) + return -EINVAL; + + tgt = value ? msm_port->pinctrl_rx_only : msm_port->pinctrl_default; + + ret = pinctrl_select_state(msm_port->pinctrl, tgt); + if (ret) + return ret; + + msm_port->pinctrl_rx_only_active = value; + + return len; +} + +static DEVICE_ATTR_RW(avm_rx_only); + +static int avm_rx_only_setup(struct platform_device *pdev) +{ + struct uart_port *port = platform_get_drvdata(pdev); + struct msm_port *msm_port = UART_TO_MSM(port); + int ret = 0; + + msm_port->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(msm_port->pinctrl)) { + dev_info(&pdev->dev, "no pinctrl configured (%ld)\n", + PTR_ERR(msm_port->pinctrl)); + goto fail_null; + } + + msm_port->pinctrl_default = pinctrl_lookup_state(msm_port->pinctrl, PINCTRL_STATE_DEFAULT); + if (IS_ERR(msm_port->pinctrl_default)) { + dev_info(&pdev->dev, "no default pinctrl configured (%ld)\n", + PTR_ERR(msm_port->pinctrl_default)); + goto fail_put; + } + + msm_port->pinctrl_rx_only = pinctrl_lookup_state(msm_port->pinctrl, "avm,rx-only"); + if (IS_ERR(msm_port->pinctrl_rx_only)) + goto fail_put; + + dev_info(&pdev->dev, "enabling support for the rx only pinctrl setting\n"); + + ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_avm_rx_only.attr); + if (ret) + goto fail_put; + + msm_port->pinctrl_rx_only_active = 0; + + return 0; + +fail_put: + devm_pinctrl_put(msm_port->pinctrl); +fail_null: + msm_port->pinctrl = NULL; + msm_port->pinctrl_default = NULL; + msm_port->pinctrl_rx_only = NULL; + msm_port->pinctrl_rx_only_active = -1; + return ret; +} + +static void avm_rx_only_cleanup(struct platform_device *pdev) +{ + struct uart_port *port = platform_get_drvdata(pdev); + struct msm_port *msm_port = UART_TO_MSM(port); + + if (msm_port->pinctrl_rx_only_active >= 0) + sysfs_remove_file(&pdev->dev.kobj, &dev_attr_avm_rx_only.attr); +} + static int msm_serial_probe(struct platform_device *pdev) { struct msm_port *msm_port; @@ -1600,6 +2011,8 @@ struct uart_port *port; const struct of_device_id *id; int irq, line; + u32 serial_clk; + int ret; if (pdev->dev.of_node) line = of_alias_get_id(pdev->dev.of_node, "serial"); @@ -1617,6 +2030,9 @@ port = msm_get_port_from_line(line); port->dev = &pdev->dev; msm_port = UART_TO_MSM(port); +#if defined(CONFIG_AVM_ENHANCED) + msm_dectuart_probe(port, line); +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ id = of_match_device(msm_uartdm_table, &pdev->dev); if (id) @@ -1632,8 +2048,17 @@ msm_port->pclk = devm_clk_get(&pdev->dev, "iface"); if (IS_ERR(msm_port->pclk)) return PTR_ERR(msm_port->pclk); + } - clk_set_rate(msm_port->clk, 1843200); + /* Setting the clock only if defined in the dts */ + if (!of_property_read_u32(pdev->dev.of_node, "serial_clk", + &serial_clk)) { + ret = clk_set_rate(msm_port->clk, serial_clk); + if (ret) { + dev_err(&pdev->dev, "clk set rate failed (%d) for %u\n", + ret, serial_clk); + return ret; + } } port->uartclk = clk_get_rate(msm_port->clk); @@ -1651,6 +2076,10 @@ platform_set_drvdata(pdev, port); + ret = avm_rx_only_setup(pdev); + if (ret < 0) + return ret; + return uart_add_one_port(&msm_uart_driver, port); } @@ -1658,14 +2087,15 @@ { struct uart_port *port = platform_get_drvdata(pdev); + avm_rx_only_cleanup(pdev); uart_remove_one_port(&msm_uart_driver, port); return 0; } static const struct of_device_id msm_match_table[] = { - { .compatible = "qcom,msm-uart" }, - { .compatible = "qcom,msm-uartdm" }, + {.compatible = "qcom,msm-uart"}, + {.compatible = "qcom,msm-uartdm"}, {} }; MODULE_DEVICE_TABLE(of, msm_match_table); @@ -1674,9 +2104,9 @@ .remove = msm_serial_remove, .probe = msm_serial_probe, .driver = { - .name = "msm_serial", - .of_match_table = msm_match_table, - }, + .name = "msm_serial", + .of_match_table = msm_match_table, + }, }; static int __init msm_serial_init(void) @@ -1702,6 +2132,214 @@ uart_unregister_driver(&msm_uart_driver); } +#if defined(CONFIG_AVM_ENHANCED) +/** + */ +static __maybe_unused void dump_msm_regs(const char *prefix, struct uart_port *port) +{ + unsigned int sr, isr, misr, mr1, mr2, ncf, rxfs, ts; + + sr = msm_read(port, UART_SR); + isr = msm_read(port, UART_ISR); + misr = msm_read(port, UART_MISR); + mr1 = msm_read(port, UART_MR1); + mr2 = msm_read(port, UART_MR2); + ncf = msm_read(port, UARTDM_NCF_TX); + rxfs = msm_read(port, UARTDM_RXFS); + ts = msm_read(port, UARTDM_RX_TOTAL_SNAP); + + pr_info("%s:\n", prefix); + pr_info("SR: %08x\n", sr); + pr_info("ISR: %08x\n", isr); + pr_info("MISR: %08x\n", misr); + pr_info("MR1: %08x\n", mr1); + pr_info("MR2: %08x\n", mr2); + pr_info("NCF: %08x\n", ncf); + pr_info("RXFS: %08x\n", rxfs); + pr_info("TS: %08x\n", ts); +} + +/** + */ +static void msm_dectuart_dm_save(struct _msm_dectuart *pdectuart) +{ + struct uart_port *port = pdectuart->port; + struct msm_port *msm_port = UART_TO_MSM(port); + + /*--- dump_msm_regs(__func__, port); ---*/ + pdectuart->mr1 = msm_read(port, UART_MR1); + pdectuart->mr2 = msm_read(port, UART_MR2); + pdectuart->imr = msm_port->imr; /*--- save actual imr-Status ---*/ + /* switch of all interrupts */ + msm_port->imr = 0; + msm_write(port, msm_port->imr, UART_IMR); +} + +/** + */ +static void msm_dectuart_dm_restore(struct _msm_dectuart *pdectuart) +{ + struct uart_port *port = pdectuart->port; + struct msm_port *msm_port = UART_TO_MSM(port); + + /*--- pr_info("%s\n", __func__); ---*/ + msm_port->imr = pdectuart->imr; + msm_write(port, pdectuart->mr1, UART_MR1); + msm_write(port, pdectuart->mr2, UART_MR2); + msm_write(port, msm_port->imr, UART_IMR); +} + +/** + * 8-N-1 configuration: 8 data bits - No parity - 1 stop bit + * an no hw-flowcontrol + */ +static void msm_dectuart_dm_init(struct _msm_dectuart *pdectuart) +{ + struct uart_port *port = pdectuart->port; + unsigned int mr; + + /*--- pr_info("%s\n", __func__); ---*/ + /* no Hardware flow control */ + mr = msm_read(port, UART_MR1); + mr &= ~(UART_MR1_CTS_CTL | UART_MR1_RX_RDY_CTL); + msm_write(port, mr, UART_MR1); + + mr = msm_read(port, UART_MR2); + /* no parity */ + mr &= ~UART_MR2_PARITY_MODE; + /* CS8 */ + mr &= ~UART_MR2_BITS_PER_CHAR; + mr |= UART_MR2_BITS_PER_CHAR_8; + /* 1 stop bits */ + mr &= ~(UART_MR2_STOP_BIT_LEN_ONE | UART_MR2_STOP_BIT_LEN_TWO); + mr |= UART_MR2_STOP_BIT_LEN_ONE; + + /* set parity, bits per char, and stop bit */ + msm_write(port, mr, UART_MR2); +} + +/** + */ +static int msm_serial_dectuart_get_char(void) +{ + struct _msm_dectuart *pdectuart = &msm_dectuart; + int c; + + if (!pdectuart->port) { + return -ENXIO; + } + c = msm_dectuart_pipe_dequeue(pdectuart); + if (c == -1) { + return -EAGAIN; + } + return c; +} + +/** + */ +static void msm_serial_dectuart_put_char(unsigned char ch) +{ + struct uart_port *port = msm_dectuart.port; + + if (port == NULL) { + return; + } + msm_poll_put_char(port, ch); +} + +/** + * mode: != 0 Tx/Rx-Irq an + */ +static void msm_serial_dectuart_init(unsigned int baud, int mode) +{ + struct _msm_dectuart *pdectuart = &msm_dectuart; + struct uart_port *port = pdectuart->port; + unsigned long flags; + + if (port == NULL) { + return; + } + + /*--- pr_info("%s: dect_baud=%u %s\n", __func__, baud, !mode ? "pollmode" : ""); ---*/ +/*--- dump_msm_regs(__func__, port); ---*/ + if (mode == 0) { + struct msm_dma *dma; + struct msm_port *msm_port = UART_TO_MSM(port); + + if (atomic_inc_return(&pdectuart->busy) > 1) { + msm_shutdown(pdectuart->port); + } + msm_startup(port); + spin_lock_irqsave(&port->lock, flags); + atomic_inc(&pdectuart->busy); + msm_dectuart_dm_init(pdectuart); + msm_set_baud_rate(port, baud, &flags); + msm_dectuart_pipe_flush(pdectuart); + msm_stop_tx(port); + + dma = &msm_port->rx_dma; + if (dma->chan) { /* Terminate if any */ + msm_stop_dma(port, dma); + } + dma = &msm_port->tx_dma; + if (dma->chan) { + msm_stop_dma(port, dma); + } + spin_unlock_irqrestore(&port->lock, flags); + } +} + +/** + */ +static void msm_serial_console_stop(void) +{ + unsigned long flags; + struct _msm_dectuart *pdectuart = &msm_dectuart; + struct uart_port *port = pdectuart->port; + + if (port) { + spin_lock_irqsave(&port->lock, flags); + atomic_set(&pdectuart->busy, 1); + msm_dectuart_dm_save(pdectuart); + spin_unlock_irqrestore(&port->lock, flags); + console_stop(port->cons); + } +} + +/** + */ +static void msm_serial_console_start(void) +{ + unsigned long flags; + struct _msm_dectuart *pdectuart = &msm_dectuart; + struct uart_port *port = pdectuart->port; + + if (port) { + struct msm_port *msm_port = UART_TO_MSM(port); + spin_lock_irqsave(&port->lock, flags); + /*--- msm_set_baud_rate(port, 115200, &flags); ---*/ + msm_dectuart_dm_restore(pdectuart); + atomic_set(&pdectuart->busy, 0); + msm_start_rx_dma(msm_port); + msm_start_tx(port); + spin_unlock_irqrestore(&port->lock, flags); + console_start(port->cons); + } +} + +/** + */ +static void msm_serial_dectuart_exit(void) +{ + struct _msm_dectuart *pdectuart = &msm_dectuart; + + if (atomic_xchg(&pdectuart->busy, 0)) { + msm_shutdown(pdectuart->port); + } + pr_info("%s\n", __func__); +} +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ + module_init(msm_serial_init); module_exit(msm_serial_exit);