// SPDX-License-Identifier: GPL-2.0 /* * NVT UART * * Driver for NVT Soc * * @file nvt_serial.c * * Copyright Novatek Microelectronics Corp. 2020. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #if defined(CONFIG_SERIAL_NVT_CONSOLE) && defined(CONFIG_MAGIC_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 #define DRIVER_VERSION "2.0.6" #define DRIVER_NAME "nvt_serial" #define RX_DMA_ADDR_ALIGN 16 static int global_line = 0; static int __serial_rx_size = -1; module_param_named(serial_rx_size, __serial_rx_size, int, 0600); static int __serial_tx_dma_en_debug = -1; module_param_named(serial_tx_dma_en_debug, __serial_tx_dma_en_debug, int, 0600); static int __serial_rx_dma_en_debug = -1; module_param_named(serial_rx_dma_en_debug, __serial_rx_dma_en_debug, int, 0600); static int __serial_debug = 0; module_param_named(serial_debug, __serial_debug, int, 0600); static int __serial_debug_console = 0; module_param_named(serial_debug_console, __serial_debug_console, int, 0600); static int __serial_flow_debug = 0; module_param_named(serial_flow_debug, __serial_flow_debug, int, 0600); #define serial_dbg(fmt, ...) do { \ if (port->line != 0 || __serial_debug_console) { \ if (__serial_debug) \ pr_info("nvt_serial%d: "fmt, port->line, ##__VA_ARGS__); \ else \ pr_debug("nvt_serial%d: "fmt, port->line,##__VA_ARGS__); \ } \ } while (0) #define serial_err(fmt, ...) do { \ pr_err("nvt_serial%d: "fmt, port->line, ##__VA_ARGS__); \ } while (0) #define serial_flow_dbg(fmt, ...) do { \ if (port->line != 0 || __serial_debug_console) { \ if (__serial_flow_debug) \ pr_info("nvt_serial%d: %s\n"fmt, port->line, __func__, ##__VA_ARGS__); \ } \ } while (0) #define UART_TO_NVT(uart_port) ((struct nvt_port *) uart_port) static unsigned int nvt_read(struct uart_port *port, unsigned int off) { return readl_relaxed(port->membase + off); } static void nvt_write(struct uart_port *port, unsigned int off, unsigned int val) { struct nvt_port *nvt_port = container_of(port, struct nvt_port, uart); unsigned long flags = 0; spin_lock_irqsave(&nvt_port->write_lock, flags); writel_relaxed(val, port->membase + off); spin_unlock_irqrestore(&nvt_port->write_lock, flags); } static void nvt_masked_write(struct uart_port *port, unsigned int off, unsigned int mask, unsigned int val) { struct nvt_port *nvt_port = container_of(port, struct nvt_port, uart); unsigned long flags = 0; unsigned int tmp = nvt_read(port, off); /* be careful that some registers are cleared after reading, such as RBR */ spin_lock_irqsave(&nvt_port->write_lock, flags); tmp &= ~mask; tmp |= val & mask; writel_relaxed(tmp, port->membase + off); spin_unlock_irqrestore(&nvt_port->write_lock, flags); } static DEFINE_SPINLOCK(write_lock_early); static void nvt_write_early(struct uart_port *port, unsigned int off, unsigned int val) { unsigned long flags = 0; spin_lock_irqsave(&write_lock_early, flags); writel_relaxed(val, port->membase + off); spin_unlock_irqrestore(&write_lock_early, flags); } static void nvt_clear_fifos(struct uart_port *port) { struct circ_buf *xmit; xmit = &port->state->xmit; /* Enable FIFO and Reset */ nvt_write(port, UART_FCR_REG, FIFO_EN_BIT | RX_FIFO_RESET_BIT | TX_FIFO_RESET_BIT); /* Disable FIFO */ nvt_write(port, UART_FCR_REG, 0); if(xmit->head != xmit->tail){ serial_dbg("\nFIFO remain %d\n",CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); /* Force reset FIFO buffer */ xmit->head = xmit->tail; } } static void nvt_enable_rx_dma(struct work_struct *work) { struct nvt_port *nvt_port = container_of(work, struct nvt_port, rx_dma_work); struct uart_port *port = &nvt_port->uart; serial_flow_dbg(); if (__serial_rx_size > 0) nvt_port->rx_size = __serial_rx_size; else nvt_port->rx_size = UART_XMIT_SIZE; nvt_write(port, UART_RX_DMA_ADDR_REG, nvt_port->rx_dma_addr); nvt_write(port, UART_RX_DMA_SIZE_REG, nvt_port->rx_size); nvt_masked_write(port, UART_DMA_INT_CTRL_REG, RX_DMA_DONE_INTEN_BIT, RX_DMA_DONE_INTEN_BIT); nvt_masked_write(port, UART_DMA_INT_CTRL_REG, RX_DMA_ERR_INTEN_BIT, RX_DMA_ERR_INTEN_BIT); nvt_masked_write(port, UART_RX_DMA_CTRL_REG, RX_DMA_EN_BIT, RX_DMA_EN_BIT); } static void nvt_enable_tx_dma(struct uart_port *port) { struct nvt_port *nvt_port = UART_TO_NVT(port); struct circ_buf *xmit = &port->state->xmit; serial_flow_dbg(); nvt_port->tx_size = 0; while (!uart_circ_empty(xmit)) { *(nvt_port->tx_virt_addr + nvt_port->tx_size) = xmit->buf[xmit->tail]; port->icount.tx++; serial_dbg("head(%d) tail(%d) cnt_tx(%d), tx_virt_addr[%d](0x%x)\n", xmit->head, xmit->tail, port->icount.tx, nvt_port->tx_size, *(nvt_port->tx_virt_addr + nvt_port->tx_size)); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); nvt_port->tx_size++; } if (nvt_port->tx_size > 0) { nvt_write(port, UART_TX_DMA_ADDR_REG, nvt_port->tx_dma_addr); nvt_write(port, UART_TX_DMA_SIZE_REG, nvt_port->tx_size); nvt_masked_write(port, UART_DMA_INT_CTRL_REG, TX_DMA_DONE_INTEN_BIT, TX_DMA_DONE_INTEN_BIT); nvt_masked_write(port, UART_TX_DMA_CTRL_REG, TX_DMA_EN_BIT, TX_DMA_EN_BIT); } } static void nvt_handle_rx_dma(struct uart_port *port, unsigned int lsr_r) { struct nvt_port *nvt_port = UART_TO_NVT(port); struct tty_port *tport = &port->state->port; unsigned char ch; char flag; unsigned int idx, dma_transfered; serial_flow_dbg(); dma_transfered = (nvt_read(port, UART_RX_CUR_ADDR_REG) & RX_CUR_ADDRESS_MASK) - nvt_port->rx_dma_addr; for (idx = 0; idx < dma_transfered; idx++) { ch = *(nvt_port->rx_virt_addr + idx); flag = TTY_NORMAL; port->icount.rx++; serial_dbg("start(0x%x) end(0x%x) cnt_rx(%d), rx_virt_addr[%d](0x%x)\n", (unsigned int)nvt_port->rx_virt_addr, (unsigned int)nvt_port->rx_virt_addr + dma_transfered, port->icount.rx, idx, ch); lsr_r |= nvt_port->lsr_break_flag; nvt_port->lsr_break_flag = 0; if (lsr_r & BREAK_INT_BIT) { port->icount.brk++; if (uart_handle_break(port)) continue; } else if (lsr_r & PARITY_ERR_BIT) port->icount.parity++; else if (lsr_r & FRAMING_ERR_BIT) port->icount.frame++; if (lsr_r & OVERRUN_ERR_BIT) port->icount.overrun++; /* * Mask off conditions which should be ignored. */ lsr_r &= port->read_status_mask; if (lsr_r & BREAK_INT_BIT) flag = TTY_BREAK; else if (lsr_r & PARITY_ERR_BIT) flag = TTY_PARITY; else if (lsr_r & FRAMING_ERR_BIT) flag = TTY_FRAME; if (!(uart_handle_sysrq_char(port, ch))) uart_insert_char(port, lsr_r, OVERRUN_ERR_BIT, ch, flag); lsr_r = nvt_read(port, UART_LSR_REG); } /* * Drop the lock here since it might end up calling * uart_start(), which takes the lock. */ spin_unlock(&port->lock); tty_flip_buffer_push(tport); spin_lock(&port->lock); } static void nvt_flush_rx(struct uart_port *port) { struct nvt_port *nvt_port = UART_TO_NVT(port); struct tty_port *tport = &port->state->port; unsigned char ch; char flag; unsigned int lsr_r; serial_flow_dbg(); while ((lsr_r = nvt_read(port, UART_LSR_REG)) & DATA_READY_BIT) { ch = nvt_read(port, UART_RBR_REG); flag = TTY_NORMAL; port->icount.rx++; serial_dbg("LSR(0x%x) cnt_rx(%d), read ch(0x%x) from RBR\n", lsr_r, port->icount.rx, ch); lsr_r |= nvt_port->lsr_break_flag; nvt_port->lsr_break_flag = 0; if (lsr_r & BREAK_INT_BIT) { port->icount.brk++; if (uart_handle_break(port)) continue; } else if (lsr_r & PARITY_ERR_BIT) port->icount.parity++; else if (lsr_r & FRAMING_ERR_BIT) port->icount.frame++; if (lsr_r & OVERRUN_ERR_BIT) port->icount.overrun++; /* * Mask off conditions which should be ignored. */ lsr_r &= port->read_status_mask; if (lsr_r & BREAK_INT_BIT) flag = TTY_BREAK; else if (lsr_r & PARITY_ERR_BIT) flag = TTY_PARITY; else if (lsr_r & FRAMING_ERR_BIT) flag = TTY_FRAME; if (!(uart_handle_sysrq_char(port, ch))) uart_insert_char(port, lsr_r, OVERRUN_ERR_BIT, ch, flag); } /* * Drop the lock here since it might end up calling * uart_start(), which takes the lock. */ spin_unlock(&port->lock); tty_flip_buffer_push(tport); spin_lock(&port->lock); } static void nvt_stop_rx(struct uart_port *port) { struct nvt_port *nvt_port = UART_TO_NVT(port); serial_flow_dbg(); if (nvt_port->rx_dma_en && port->line != 0) { nvt_masked_write(port, UART_DMA_INT_CTRL_REG, RX_DMA_DONE_INTEN_BIT, 0); nvt_masked_write(port, UART_DMA_INT_CTRL_REG, RX_DMA_ERR_INTEN_BIT, 0); if (nvt_read(port, UART_RX_DMA_CTRL_REG) & RX_DMA_EN_BIT) { nvt_handle_rx_dma(port, nvt_read(port, UART_LSR_REG)); } nvt_masked_write(port, UART_RX_DMA_CTRL_REG, RX_DMA_EN_BIT, 0); while (nvt_read(port, UART_RX_DMA_CTRL_REG) & RX_DMA_EN_BIT) { ; } nvt_flush_rx(port); } nvt_masked_write(port, UART_IER_REG, RLS_INTEN_BIT | RDA_INTEN_BIT, 0); } static void nvt_stop_tx(struct uart_port *port) { struct nvt_port *nvt_port = UART_TO_NVT(port); serial_flow_dbg(); if (nvt_port->tx_dma_en && port->line != 0) { nvt_masked_write(port, UART_DMA_INT_CTRL_REG, TX_DMA_DONE_INTEN_BIT, 0); nvt_masked_write(port, UART_TX_DMA_CTRL_REG, TX_DMA_EN_BIT, 0); while (nvt_read(port, UART_TX_DMA_CTRL_REG) & TX_DMA_EN_BIT) { ; } } nvt_masked_write(port, UART_IER_REG, THR_EMPTY_INTEN_BIT, 0); while (nvt_read(port, UART_IER_REG) & THR_EMPTY_INTEN_BIT) { ; } } #define UART_LSR_BRK_ERROR_BITS 0x1E /* BI, FE, PE, OE bits */ #define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS static void nvt_handle_tx(struct uart_port *port); static void nvt_start_tx(struct uart_port *port) { struct nvt_port *nvt_port = UART_TO_NVT(port); unsigned char lsr; serial_flow_dbg(); /* * We can't control the number of start_tx in one startup cycle, * which is determined by the number of SyS_write calls. * * If handle_tx exhausted the xmit buffer, it needs to be returned * when xmit buffer is empty to prevent start_tx from hanging without * data. */ if (uart_circ_empty(&port->state->xmit) || uart_tx_stopped(port)) { nvt_stop_tx(port); serial_dbg("uart_circ_empty or uart_tx_stopped, so return\n"); return; } if (nvt_port->tx_dma_en && port->line != 0) { while (nvt_read(port, UART_TX_DMA_CTRL_REG) & TX_DMA_EN_BIT) { serial_dbg("wait TX_DMA_EN_BIT auto clear\n"); ; } nvt_enable_tx_dma(port); } else { nvt_masked_write(port, UART_IER_REG, THR_EMPTY_INTEN_BIT, THR_EMPTY_INTEN_BIT); while (!(nvt_read(port, UART_IER_REG) & THR_EMPTY_INTEN_BIT)) { ; } lsr = nvt_read(port, UART_LSR_REG); nvt_port->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; if (lsr & THR_EMPTY_BIT) { nvt_handle_tx(port); } } } static unsigned int nvt_tx_empty(struct uart_port *port) { struct nvt_port *nvt_port = UART_TO_NVT(port); unsigned int lsr_r; unsigned long flags; serial_flow_dbg(); spin_lock_irqsave(&port->lock, flags); lsr_r = nvt_read(port, UART_LSR_REG); nvt_port->lsr_saved_flags |= lsr_r & LSR_SAVE_FLAGS; spin_unlock_irqrestore(&port->lock, flags); return (lsr_r & BOTH_EMPTY_BIT) ? TIOCSER_TEMT : 0; } static void nvt_handle_rx(struct uart_port *port, unsigned int lsr_r) { struct nvt_port *nvt_port = UART_TO_NVT(port); struct tty_port *tport = &port->state->port; unsigned char ch; char flag; serial_flow_dbg(); do { if (likely(lsr_r & DATA_READY_BIT)) ch = nvt_read(port, UART_RBR_REG); else ch = 0; flag = TTY_NORMAL; port->icount.rx++; serial_dbg("LSR(0x%x) IER(0x%x) cnt_rx(%d), read ch(0x%x) from RBR\n", lsr_r, nvt_read(port, UART_IER_REG), port->icount.rx, ch); lsr_r |= nvt_port->lsr_saved_flags; nvt_port->lsr_saved_flags = 0; lsr_r |= nvt_port->lsr_break_flag; nvt_port->lsr_break_flag = 0; if (lsr_r & BREAK_INT_BIT) { port->icount.brk++; if (uart_handle_break(port)) continue; } else if (lsr_r & PARITY_ERR_BIT) port->icount.parity++; else if (lsr_r & FRAMING_ERR_BIT) port->icount.frame++; if (lsr_r & OVERRUN_ERR_BIT) port->icount.overrun++; /* * Mask off conditions which should be ignored. */ lsr_r &= port->read_status_mask; if (lsr_r & BREAK_INT_BIT) flag = TTY_BREAK; else if (lsr_r & PARITY_ERR_BIT) flag = TTY_PARITY; else if (lsr_r & FRAMING_ERR_BIT) flag = TTY_FRAME; if (!(uart_handle_sysrq_char(port, ch))) uart_insert_char(port, lsr_r, OVERRUN_ERR_BIT, ch, flag); } while ((lsr_r = nvt_read(port, UART_LSR_REG)) & DATA_READY_BIT); /* * Drop the lock here since it might end up calling * uart_start(), which takes the lock. */ spin_unlock(&port->lock); tty_flip_buffer_push(tport); spin_lock(&port->lock); } static void nvt_handle_tx(struct uart_port *port) { struct nvt_port *nvt_port = UART_TO_NVT(port); struct circ_buf *xmit = &port->state->xmit; unsigned char ch; int count; serial_flow_dbg(); /* For software flow control, xon resume transmission, xoff pause transmission */ if (port->x_char) { nvt_write(port, UART_THR_REG, port->x_char); port->icount.tx++; port->x_char = 0; return; } /* If there isn't anything more to transmit, or the uart is now stopped, disable the uart and exit */ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { nvt_stop_tx(port); return; } /* Drain the buffer by size of tx_loadsz in one cycle */ count = nvt_port->tx_loadsz; do { ch = xmit->buf[xmit->tail]; nvt_write(port, UART_THR_REG, ch); port->icount.tx++; serial_dbg("head(%d) tail(%d) cnt_tx(%d), write ch(0x%x) to THR\n", xmit->head, xmit->tail, port->icount.tx, ch); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); if (uart_circ_empty(xmit)) break; } while (--count > 0); /* * If num chars in xmit buffer are too few, ask tty layer for more. * By Hard ISR to schedule processing in software interrupt part. */ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) { spin_unlock(&port->lock); uart_write_wakeup(port); spin_lock(&port->lock); } if (uart_circ_empty(xmit)) { nvt_stop_tx(port); } } static irqreturn_t nvt_irq(int irq, void *dev_id) { struct uart_port *port = dev_id; struct nvt_port *nvt_port = UART_TO_NVT(port); unsigned int lsr_r, ier_r, iir_r, dma_sts, dma_int; unsigned long flags; spin_lock_irqsave(&port->lock, flags); lsr_r = nvt_read(port, UART_LSR_REG); /* error bits are cleared after reading */ ier_r = nvt_read(port, UART_IER_REG); iir_r = nvt_read(port, UART_IIR_REG); /* clear interrupt status to avoid irq storm */ if ((nvt_port->tx_dma_en || nvt_port->rx_dma_en) && port->line != 0) { dma_sts = nvt_read(port, UART_DMA_INT_STS_REG); dma_int = nvt_read(port, UART_DMA_INT_CTRL_REG); nvt_write(port, UART_DMA_INT_STS_REG, dma_sts); /* clear dma interrupt status */ serial_dbg("irq stage LSR(0x%x) IER(0x%x) IIR(0x%x) DMA_STS(0x%x) DMA_INT(0x%x)\n", lsr_r, ier_r, iir_r, dma_sts, dma_int); } else { serial_dbg("irq stage LSR(0x%x) IER(0x%x) IIR(0x%x)\n", lsr_r, ier_r, iir_r); } /* Handle UART rx */ if ((lsr_r & DATA_READY_BIT) && (ier_r & RDA_INTEN_BIT)) { nvt_handle_rx(port, lsr_r); } else if (nvt_port->rx_dma_en && port->line != 0) { if ((dma_sts & RX_DMA_DONE_BIT) && (dma_int & RX_DMA_DONE_INTEN_BIT)) { nvt_masked_write(port, UART_DMA_INT_CTRL_REG, RX_DMA_DONE_INTEN_BIT, 0); nvt_masked_write(port, UART_DMA_INT_CTRL_REG, RX_DMA_ERR_INTEN_BIT, 0); while (nvt_read(port, UART_RX_DMA_CTRL_REG) & RX_DMA_EN_BIT) { serial_dbg("wait RX_DMA_EN_BIT auto clear\n"); ; } nvt_handle_rx_dma(port, lsr_r); schedule_work(&nvt_port->rx_dma_work); } else if ((dma_sts & RX_DMA_ERR_BIT) && (dma_int & RX_DMA_ERR_INTEN_BIT)) { nvt_masked_write(port, UART_DMA_INT_CTRL_REG, RX_DMA_DONE_INTEN_BIT, 0); nvt_masked_write(port, UART_DMA_INT_CTRL_REG, RX_DMA_ERR_INTEN_BIT, 0); nvt_masked_write(port, UART_RX_DMA_CTRL_REG, RX_DMA_EN_BIT, 0); while (nvt_read(port, UART_RX_DMA_CTRL_REG) & RX_DMA_EN_BIT) { ; } nvt_handle_rx_dma(port, lsr_r); if (dma_sts & DMA_OVERRUN_ERR_BIT) serial_err("dma overrun error!\r\n"); if (dma_sts & DMA_PARITY_ERR_BIT) serial_err("dma parity error!\r\n"); if (dma_sts & DMA_FRAMING_ERR_BIT) serial_dbg("dma framing error!\r\n"); if (dma_sts & DMA_BREAK_ERR_BIT) serial_dbg("dma break interrupt!\r\n"); schedule_work(&nvt_port->rx_dma_work); } } /* Handle UART tx */ if ((lsr_r & THR_EMPTY_BIT) && (ier_r & THR_EMPTY_INTEN_BIT)) { nvt_handle_tx(port); } else if (nvt_port->tx_dma_en && port->line != 0) { if ((dma_sts & TX_DMA_DONE_BIT) && (dma_int & TX_DMA_DONE_INTEN_BIT)) { nvt_masked_write(port, UART_DMA_INT_CTRL_REG, TX_DMA_DONE_INTEN_BIT, 0); } } /* Print UART error */ if (lsr_r & FIFO_DATA_ERR_BIT) { if (lsr_r & OVERRUN_ERR_BIT) serial_err("overrun error!\r\n"); if (lsr_r & PARITY_ERR_BIT) serial_err("parity error!\r\n"); if (lsr_r & FRAMING_ERR_BIT) serial_dbg("framing error!\r\n"); if (lsr_r & BREAK_INT_BIT) serial_dbg("break interrupt!\r\n"); } /* Clear RBR when IIR stuck in CRT status */ if (((iir_r & UART_IIR_INT_ID_MASK) == _UART_IIR_INT_CRT) && ((lsr_r & DATA_READY_BIT) == 0)) nvt_read(port, UART_RBR_REG); spin_unlock_irqrestore(&port->lock, flags); return IRQ_HANDLED; } static unsigned int nvt_get_mctrl(struct uart_port *port) { serial_flow_dbg(); /* * Pretend we have a Modem status reg and following bits are * always set, to satify the serial core state machine * (DSR) Data Set Ready * (CTS) Clear To Send * (CAR) Carrier Detect */ return TIOCM_DSR | TIOCM_CTS | TIOCM_CAR; } static void nvt_set_mctrl(struct uart_port *port, unsigned int mctrl) { serial_flow_dbg(); /* MCR not present */ } static void nvt_break_ctl(struct uart_port *port, int break_ctl) { unsigned long flags; serial_flow_dbg(); spin_lock_irqsave(&port->lock, flags); if (break_ctl) nvt_masked_write(port, UART_LCR_REG, SET_BREAK_BIT, SET_BREAK_BIT); else nvt_masked_write(port, UART_LCR_REG, SET_BREAK_BIT, 0); spin_unlock_irqrestore(&port->lock, flags); } static int nvt_startup(struct uart_port *port) { struct nvt_port *nvt_port = UART_TO_NVT(port); int ret; serial_flow_dbg(); snprintf(nvt_port->name, sizeof(nvt_port->name), "nvt_serial%d", port->line); ret = request_irq(port->irq, nvt_irq, IRQF_TRIGGER_HIGH, nvt_port->name, port); if (unlikely(ret)) return ret; /* * Clear the FIFO buffers and disable. */ nvt_clear_fifos(port); /* * Clear the interrupt registers again for luck, and clear the * saved flags to avoid getting false values from polling * routines or the previous session. */ nvt_read(port, UART_LSR_REG); nvt_read(port, UART_THR_REG); nvt_read(port, UART_IIR_REG); nvt_port->lsr_saved_flags = 0; nvt_port->lsr_break_flag = 0; #if defined(NVT_FR) && defined(CONFIG_SERIAL_NVT_DMA) /* Set DMA feature */ nvt_port->tx_dma_en = 1; nvt_port->rx_dma_en = 1; #endif /* A debug node to enable/disable DMA feature */ if (__serial_tx_dma_en_debug == 1) nvt_port->tx_dma_en = 1; else if (__serial_tx_dma_en_debug == 0) nvt_port->tx_dma_en = 0; if (__serial_rx_dma_en_debug == 1) nvt_port->rx_dma_en = 1; else if (__serial_rx_dma_en_debug == 0) nvt_port->rx_dma_en = 0; /* Allocate DMA */ if (nvt_port->tx_dma_en && port->line != 0) { nvt_port->tx_virt_addr = dma_alloc_coherent(port->dev, UART_XMIT_SIZE, &nvt_port->tx_dma_addr, GFP_KERNEL); if (!nvt_port->tx_virt_addr) { serial_err("allocate DMA failed, using PIO\n"); nvt_port->tx_dma_en = 0; } else { serial_dbg("allocate tx_virt_addr(0x%x) tx_dma_addr(0x%x)\n", (unsigned int)nvt_port->tx_virt_addr, nvt_port->tx_dma_addr); } } if (nvt_port->rx_dma_en && port->line != 0) { nvt_port->rx_virt_addr = dma_alloc_coherent(port->dev, UART_XMIT_SIZE, &nvt_port->rx_dma_addr, GFP_KERNEL); if (!nvt_port->rx_virt_addr) { serial_err("allocate DMA failed, using PIO\n"); nvt_port->rx_dma_en = 0; } else if (!IS_ALIGNED(nvt_port->rx_dma_addr, (dma_addr_t)RX_DMA_ADDR_ALIGN) && (nvt_get_chip_id() == CHIP_NA51055 || nvt_get_chip_id() == CHIP_NA51084)) { serial_err("rx_dma_addr(0x%x) failed to align %d bytes, using PIO\n", nvt_port->rx_dma_addr, RX_DMA_ADDR_ALIGN); nvt_port->rx_dma_en = 0; } else { serial_dbg("allocate rx_virt_addr(0x%x) rx_dma_addr(0x%x)\n", (unsigned int)nvt_port->rx_virt_addr, nvt_port->rx_dma_addr); } } return 0; } static void nvt_shutdown(struct uart_port *port) { struct nvt_port *nvt_port = UART_TO_NVT(port); unsigned long flags; serial_flow_dbg(); spin_lock_irqsave(&port->lock, flags); nvt_write(port, UART_IER_REG, 0); /* disable interrupts to avoid hanging in start_tx */ spin_unlock_irqrestore(&port->lock, flags); free_irq(port->irq, port); /* Free DMA */ if ((nvt_port->tx_dma_en || nvt_port->rx_dma_en) && port->line != 0) { if (nvt_port->tx_virt_addr) { serial_dbg("free tx_virt_addr(0x%x) tx_dma_addr(0x%x)\n", (unsigned int)nvt_port->tx_virt_addr, nvt_port->tx_dma_addr); dma_free_coherent(port->dev, UART_XMIT_SIZE, nvt_port->tx_virt_addr, nvt_port->tx_dma_addr); nvt_port->tx_virt_addr = NULL; } if (nvt_port->rx_virt_addr) { serial_dbg("free rx_virt_addr(0x%x) rx_dma_addr(0x%x)\n", (unsigned int)nvt_port->rx_virt_addr, nvt_port->rx_dma_addr); dma_free_coherent(port->dev, UART_XMIT_SIZE, nvt_port->rx_virt_addr, nvt_port->rx_dma_addr); nvt_port->rx_virt_addr = NULL; } nvt_write(port, UART_DMA_INT_CTRL_REG, 0); nvt_write(port, UART_TX_DMA_CTRL_REG, 0); nvt_write(port, UART_RX_DMA_CTRL_REG, 0); } } static void nvt_set_baud_rate(struct uart_port *port, unsigned int baud) { unsigned int PSR, DLR; PSR = 0x01; DLR = port->uartclk / 16 / PSR / baud; if (DLR > UART_DLR_MAX) { DLR = UART_DLR_MAX; serial_err("baud out of MAX range error!\n"); } if (DLR < UART_DLR_MIN) { DLR = UART_DLR_MIN; serial_err("baud out of MIN range error!\n"); } nvt_write(port, UART_DLL_REG, DLR & DLL_MASK); nvt_masked_write(port, UART_DLM_REG, DLM_MASK, DLR >> 8); nvt_write(port, UART_PSR_REG, PSR & PSR_MASK); } static void nvt_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct nvt_port *nvt_port = UART_TO_NVT(port); unsigned long flags; unsigned int lcr_val = 0, baud; serial_flow_dbg(); spin_lock_irqsave(&port->lock, flags); /* Set baud rate and divisor */ if (nvt_port->baud) { if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, nvt_port->baud, nvt_port->baud); nvt_port->baud = 0; } baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); nvt_masked_write(port, UART_LCR_REG, DLAB_BIT, DLAB_BIT); /* set DLAB bit to access divisor */ nvt_set_baud_rate(port, baud); if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); /* Set length */ switch (termios->c_cflag & CSIZE) { case CS5: lcr_val |= _WL_L5; break; case CS6: lcr_val |= _WL_L6; break; case CS7: lcr_val |= _WL_L7; break; case CS8: default: lcr_val |= _WL_L8; break; } /* Set stop bits */ if (termios->c_cflag & CSTOPB) lcr_val |= STOP_BIT; /* Set parity */ if (termios->c_cflag & PARENB) { if (termios->c_cflag & CMSPAR) { if (termios->c_cflag & PARODD) lcr_val |= _UART_PARITY_ONE << _UART_LCR_PARITY_SHIFT; else lcr_val |= _UART_PARITY_ZERO << _UART_LCR_PARITY_SHIFT; } else { if (termios->c_cflag & PARODD) lcr_val |= _UART_PARITY_ODD << _UART_LCR_PARITY_SHIFT; else lcr_val |= _UART_PARITY_EVEN << _UART_LCR_PARITY_SHIFT; } } nvt_write(port, UART_LCR_REG, lcr_val); /* clear DLAB bit */ /* Set rx fifo trigger level */ nvt_clear_fifos(port); nvt_write(port, UART_FCR_REG, FIFO_EN_BIT | (nvt_port->rx_trig_level << _UART_FCR_RX_TRIGGER_SHIFT)); /* Set hardware flow control */ if (nvt_port->hw_flowctrl) { termios->c_cflag |= CRTSCTS; nvt_port->hw_flowctrl = 0; } #ifdef NVT_FR if (termios->c_cflag & CRTSCTS) { nvt_masked_write(port, UART_MCR_REG, HW_FLOW_CTRL_BIT, HW_FLOW_CTRL_BIT); } else { nvt_masked_write(port, UART_MCR_REG, HW_FLOW_CTRL_BIT, 0); } #else if (termios->c_cflag & CRTSCTS) { nvt_masked_write(port, UART_IER_REG, HW_FLOW_CTRL_BIT, HW_FLOW_CTRL_BIT); } else { nvt_masked_write(port, UART_IER_REG, HW_FLOW_CTRL_BIT, 0); } #endif #ifdef NVT_FR /* Set rs485 feature */ if (nvt_port->rs485_en) { nvt_write(port, UART_RS485_REG, ENABLE_BIT | (nvt_port->rs485_delay_before_send << _UART_RS485_SETUP_TIME_SHIFT) | (nvt_port->rs485_delay_after_send << _UART_RS485_HOLD_TIME_SHIFT)); } #endif if (nvt_port->rx_dma_en && port->line != 0) { schedule_work(&nvt_port->rx_dma_work); } else { /* Enanle receive interrupts */ nvt_write(port, UART_IER_REG, RLS_INTEN_BIT | RDA_INTEN_BIT); } /* Configure status bits to ignore based on termio flags */ port->read_status_mask = OVERRUN_ERR_BIT; if (termios->c_iflag & INPCK) port->read_status_mask |= FRAMING_ERR_BIT | PARITY_ERR_BIT; if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK)) port->read_status_mask |= BREAK_INT_BIT; uart_update_timeout(port, termios->c_cflag, baud); serial_dbg("baud(%d) hw_flowctrl(%d) tx_dma(%d) rx_dma(%d)\n", baud, ((termios->c_cflag & CRTSCTS) ? 1 : 0), nvt_port->tx_dma_en, nvt_port->rx_dma_en); spin_unlock_irqrestore(&port->lock, flags); } static const char *nvt_type(struct uart_port *port) { return "NVT"; } static void nvt_release_port(struct uart_port *port) { serial_flow_dbg(); } static int nvt_request_port(struct uart_port *port) { serial_flow_dbg(); return 0; } static void nvt_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE) port->type = PORT_16550A; } static int nvt_verify_port(struct uart_port *port, struct serial_struct *ser) { serial_flow_dbg(); if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_16550A)) return -EINVAL; if (unlikely(port->irq != ser->irq)) return -EINVAL; return 0; } static inline void wait_for_xmitr(struct uart_port *port, int bits) { struct nvt_port *nvt_port = UART_TO_NVT(port); unsigned int status, tmout = 10000; /* Wait up to 10ms for the character(s) to be sent */ for (;;) { status = nvt_read(port, UART_LSR_REG); nvt_port->lsr_saved_flags |= status & LSR_SAVE_FLAGS; if (status & BREAK_INT_BIT) nvt_port->lsr_break_flag = BREAK_INT_BIT; if ((status & bits) == bits) break; if (--tmout == 0) break; udelay(1); } } #ifdef CONFIG_CONSOLE_POLL static int nvt_poll_get_char(struct uart_port *port) { unsigned char ch; while (!(nvt_read(port, UART_LSR_REG) & DATA_READY_BIT)) cpu_relax(); ch = nvt_read(port, UART_RBR_REG); return ch; } static void nvt_poll_put_char(struct uart_port *port, unsigned char ch) { unsigned int ier; /* First save the IER then disable the interrupts */ ier = nvt_read(port, UART_IER_REG); nvt_write(port, UART_IER_REG, 0); wait_for_xmitr(port, BOTH_EMPTY_BIT); /* Send the character out */ nvt_write(port, UART_THR_REG, ch); /* Finally, wait for transmitter to become empty and restore the IER */ wait_for_xmitr(port, BOTH_EMPTY_BIT); nvt_write(port, UART_IER_REG, ier); } #endif static struct uart_ops nvt_uart_pops = { .stop_rx = nvt_stop_rx, .stop_tx = nvt_stop_tx, .start_tx = nvt_start_tx, .tx_empty = nvt_tx_empty, .get_mctrl = nvt_get_mctrl, .set_mctrl = nvt_set_mctrl, .break_ctl = nvt_break_ctl, .startup = nvt_startup, .shutdown = nvt_shutdown, .set_termios = nvt_set_termios, .type = nvt_type, .release_port = nvt_release_port, .request_port = nvt_request_port, .config_port = nvt_config_port, .verify_port = nvt_verify_port, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = nvt_poll_get_char, .poll_put_char = nvt_poll_put_char, #endif }; static struct nvt_port nvt_uart_ports[] = { #ifdef CONFIG_SERIAL_NVT_CONSOLE { .uart = { .line = 0, .ops = &nvt_uart_pops, .flags = UPF_BOOT_AUTOCONF, /********************************************************************** * Following items are default values and will be overwritten by dts * **********************************************************************/ .uartclk = 24000000, .iotype = UPIO_MEM32, .fifosize = 64, .regshift = 2, }, .baud = 115200, /* set into termios structure when first used, and can be modified through user interface such as stty */ .tx_loadsz = 64, /* maximum number of characters tx can send in one handle_tx cycle, usually following fifosize */ .hw_flowctrl = 0, /* default hardware flow control is on or off, also we can use stty crtscts/-crtscts to turn on/off */ .rx_trig_level = 3, }, #endif { .uart = { .line = 1, .ops = &nvt_uart_pops, .flags = UPF_BOOT_AUTOCONF, /********************************************************************** * Following items are default values and will be overwritten by dts * **********************************************************************/ .uartclk = 48000000, .iotype = UPIO_MEM32, .fifosize = 64, .regshift = 2, }, .baud = 115200, .tx_loadsz = 64, .hw_flowctrl = 0, .rx_trig_level = 3, }, { .uart = { .line = 2, .ops = &nvt_uart_pops, .flags = UPF_BOOT_AUTOCONF, /********************************************************************** * Following items are default values and will be overwritten by dts * **********************************************************************/ .uartclk = 48000000, .iotype = UPIO_MEM32, .fifosize = 64, .regshift = 2, }, .baud = 0, .tx_loadsz = 64, .hw_flowctrl = 0, .rx_trig_level = 3, }, { .uart = { .line = 3, .ops = &nvt_uart_pops, .flags = UPF_BOOT_AUTOCONF, /********************************************************************** * Following items are default values and will be overwritten by dts * **********************************************************************/ .uartclk = 48000000, .iotype = UPIO_MEM32, .fifosize = 64, .regshift = 2, }, .baud = 0, .tx_loadsz = 64, .hw_flowctrl = 0, .rx_trig_level = 3, }, { .uart = { .line = 4, .ops = &nvt_uart_pops, .flags = UPF_BOOT_AUTOCONF, /********************************************************************** * Following items are default values and will be overwritten by dts * **********************************************************************/ .uartclk = 48000000, .iotype = UPIO_MEM32, .fifosize = 64, .regshift = 2, }, .baud = 0, .tx_loadsz = 64, .hw_flowctrl = 0, .rx_trig_level = 3, }, { .uart = { .line = 5, .ops = &nvt_uart_pops, .flags = UPF_BOOT_AUTOCONF, /********************************************************************** * Following items are default values and will be overwritten by dts * **********************************************************************/ .uartclk = 48000000, .iotype = UPIO_MEM32, .fifosize = 64, .regshift = 2, }, .baud = 0, .tx_loadsz = 64, .hw_flowctrl = 0, .rx_trig_level = 3, }, }; static int UART_NR; static inline struct uart_port *get_port_from_line(unsigned int line) { #ifdef CONFIG_SERIAL_NVT_CONSOLE return &nvt_uart_ports[line].uart; #else return &nvt_uart_ports[line-1].uart; #endif } #ifdef CONFIG_SERIAL_NVT_CONSOLE static void nvt_console_putchar(struct uart_port *port, int ch) { wait_for_xmitr(port, THR_EMPTY_BIT); nvt_write(port, UART_THR_REG, ch); } static void nvt_console_write(struct console *co, const char *s, unsigned int count) { struct uart_port *port = get_port_from_line(co->index); unsigned long flags; unsigned int ier; int locked = 1; if (port->sysrq) locked = 0; else if (oops_in_progress) locked = spin_trylock_irqsave(&port->lock, flags); else spin_lock_irqsave(&port->lock, flags); /* First save the IER then disable the interrupts */ ier = nvt_read(port, UART_IER_REG); nvt_write(port, UART_IER_REG, 0); uart_console_write(port, s, count, nvt_console_putchar); /* Finally, wait for transmitter to become empty and restore the IER */ wait_for_xmitr(port, BOTH_EMPTY_BIT); nvt_write(port, UART_IER_REG, ier); if (locked) spin_unlock_irqrestore(&port->lock, flags); } static int __init nvt_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 >= UART_NR || co->index < 0)) return -ENXIO; /* * The uart port backing the console (e.g. ttyS0) might not have been * init yet. If so, defer the console setup to after the port. */ port = get_port_from_line(co->index); if (unlikely(!port->membase)) return -ENXIO; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); pr_info("nvt_serial: console setup on port #%d\n", port->line); /* * Serial core will call port->ops->set_termios( ) * which will set the baud reg. */ return uart_set_options(port, co, baud, parity, bits, flow); } static void nvt_serial_early_putchar(struct uart_port *port, int ch) { wait_for_xmitr(port, THR_EMPTY_BIT); nvt_write_early(port, UART_THR_REG, ch); } static void nvt_serial_early_write(struct console *con, const char *s, unsigned n) { struct earlycon_device *dev = con->data; uart_console_write(&dev->port, s, n, nvt_serial_early_putchar); } static int __init nvt_serial_early_console_setup(struct earlycon_device *device, const char *opt) { if (!device->port.membase) return -ENODEV; device->con->write = nvt_serial_early_write; return 0; } EARLYCON_DECLARE(nvt_serial, nvt_serial_early_console_setup); OF_EARLYCON_DECLARE(nvt_serial, "nvt.nvt_uart", nvt_serial_early_console_setup); static struct uart_driver nvt_uart_driver; static struct console nvt_console = { .name = "ttyS", .write = nvt_console_write, .device = uart_console_device, .setup = nvt_console_setup, .flags = CON_PRINTBUFFER, .index = -1, .data = &nvt_uart_driver, }; #define NVT_CONSOLE (&nvt_console) #else #define NVT_CONSOLE NULL #endif static struct uart_driver nvt_uart_driver = { .owner = THIS_MODULE, .driver_name = DRIVER_NAME, .dev_name = "ttyS", .cons = NVT_CONSOLE, }; static int nvt_serial_probe(struct platform_device *pdev) { struct nvt_port *nvt_port; struct uart_port *port; u32 prop, prop_array[2] = {0}; int line, irq; struct resource *resource; if (of_property_read_u32(pdev->dev.of_node, "uart_id", &prop) == 0) { line = prop; } else { dev_dbg(&pdev->dev, "get uart_id failed, use default uart_id(%d)\n", global_line); line = global_line; global_line++; } if (line >= UART_NR) { dev_err(&pdev->dev, "unsupported uart line(%d)\n", line); return -ENXIO; } port = get_port_from_line(line); port->dev = &pdev->dev; nvt_port = UART_TO_NVT(port); spin_lock_init(&nvt_port->write_lock); INIT_WORK(&nvt_port->rx_dma_work, nvt_enable_rx_dma); resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(!resource)) { dev_err(&pdev->dev, "get address failed\n"); return -ENXIO; } port->mapbase = resource->start; port->membase = devm_ioremap(port->dev, resource->start, resource_size(resource)); if (!port->membase) { dev_err(&pdev->dev, "get ioremap failed\n"); return -EBUSY; } irq = irq_of_parse_and_map(pdev->dev.of_node, 0); if (unlikely(irq < 0)) { dev_err(&pdev->dev, "get irq failed\n"); return -ENXIO; } port->irq = irq; if (line == 0) { if (nvt_get_chip_id() == CHIP_NA51055) { port->fifosize = 16; nvt_port->tx_loadsz = 16; } } if (of_property_read_u32(pdev->dev.of_node, "reg-shift", &prop) == 0) port->regshift = prop; if (of_property_read_u32(pdev->dev.of_node, "reg-io-width", &prop) == 0) { switch (prop) { case 1: port->iotype = UPIO_MEM; break; case 4: port->iotype = of_device_is_big_endian(pdev->dev.of_node) ? UPIO_MEM32BE : UPIO_MEM32; break; default: dev_warn(&pdev->dev, "unsupported reg-io-width(%d)\n", prop); } } if (of_find_property(pdev->dev.of_node, "no-loopback-test", NULL)) port->flags |= UPF_SKIP_TEST; if (of_property_read_u32(pdev->dev.of_node, "clock-frequency", &prop) == 0) { port->uartclk = prop; } else { dev_dbg(&pdev->dev, "get clock-frequency failed, use default uartclk(%d)\n", port->uartclk); } nvt_port->clk = clk_get(&pdev->dev, dev_name(&pdev->dev)); if (IS_ERR(nvt_port->clk)) { dev_err(&pdev->dev, "clk %s not found\n", dev_name(&pdev->dev)); return PTR_ERR(nvt_port->clk); } else { if (!__clk_is_enabled(nvt_port->clk)) clk_prepare_enable(nvt_port->clk); clk_set_rate(nvt_port->clk, port->uartclk); } if (of_property_read_u32(pdev->dev.of_node, "baud", &prop) == 0) nvt_port->baud = prop; if (of_property_read_u32(pdev->dev.of_node, "rx_trig_level", &prop) == 0) nvt_port->rx_trig_level = prop; if (of_property_read_u32(pdev->dev.of_node, "hw_flowctrl", &prop) == 0) nvt_port->hw_flowctrl = prop; if (of_property_read_u32(pdev->dev.of_node, "rs485_en", &prop) == 0) { nvt_port->rs485_en = prop; if (of_property_read_u32_array(pdev->dev.of_node, "rs485_delay", prop_array, 2) == 0) { nvt_port->rs485_delay_before_send = prop_array[0]; nvt_port->rs485_delay_after_send = prop_array[1]; } } serial_dbg("driver probed\n"); platform_set_drvdata(pdev, port); return uart_add_one_port(&nvt_uart_driver, port); } static int nvt_serial_remove(struct platform_device *pdev) { struct uart_port *port = platform_get_drvdata(pdev); uart_remove_one_port(&nvt_uart_driver, port); return 0; } static const struct of_device_id nvt_match_table[] = { { .compatible = "nvt.nvt_uart" }, { .compatible = "ns16550a" }, {}, }; static struct platform_driver nvt_serial_platform_driver = { .probe = nvt_serial_probe, .remove = nvt_serial_remove, .driver = { .name = "nvt_serial", .of_match_table = nvt_match_table, }, }; static int __init nvt_serial_init(void) { int ret; if (nvt_get_chip_id() == CHIP_NA51084) { UART_NR = 6; } else { UART_NR = 3; } nvt_uart_driver.nr = UART_NR; ret = uart_register_driver(&nvt_uart_driver); if (unlikely(ret)) return ret; ret = platform_driver_register(&nvt_serial_platform_driver); if (unlikely(ret)) { uart_unregister_driver(&nvt_uart_driver); return ret; } pr_info("nvt_serial: driver initialized\n"); return ret; } static void __exit nvt_serial_exit(void) { platform_driver_unregister(&nvt_serial_platform_driver); uart_unregister_driver(&nvt_uart_driver); } module_init(nvt_serial_init); module_exit(nvt_serial_exit); MODULE_AUTHOR("Shawn Chou "); MODULE_DESCRIPTION("Driver for NVT Soc"); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL");