📄 mpsc.c
字号:
flag = TTY_BREAK; else if (cmdstat & SDMA_DESC_CMDSTAT_FR) flag = TTY_FRAME; else if (cmdstat & SDMA_DESC_CMDSTAT_OR) flag = TTY_OVERRUN; else if (cmdstat & SDMA_DESC_CMDSTAT_PE) flag = TTY_PARITY; } if (uart_handle_sysrq_char(&pi->port, *bp)) { bp++; bytes_in--; goto next_frame; } if ((unlikely(cmdstat & (SDMA_DESC_CMDSTAT_BR | SDMA_DESC_CMDSTAT_FR | SDMA_DESC_CMDSTAT_OR))) && !(cmdstat & pi->port.ignore_status_mask)) { tty_insert_flip_char(tty, *bp, flag); } else { for (i=0; i<bytes_in; i++) tty_insert_flip_char(tty, *bp++, TTY_NORMAL); pi->port.icount.rx += bytes_in; }next_frame: rxre->bytecnt = cpu_to_be16(0); wmb(); rxre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | SDMA_DESC_CMDSTAT_EI | SDMA_DESC_CMDSTAT_F | SDMA_DESC_CMDSTAT_L); wmb(); dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE, DMA_BIDIRECTIONAL);#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ flush_dcache_range((ulong)rxre, (ulong)rxre + MPSC_RXRE_SIZE);#endif /* Advance to next descriptor */ pi->rxr_posn = (pi->rxr_posn + 1) & (MPSC_RXR_ENTRIES - 1); rxre = (struct mpsc_rx_desc *) (pi->rxr + (pi->rxr_posn * MPSC_RXRE_SIZE)); dma_cache_sync(pi->port.dev, (void *)rxre, MPSC_RXRE_SIZE, DMA_FROM_DEVICE);#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ invalidate_dcache_range((ulong)rxre, (ulong)rxre + MPSC_RXRE_SIZE);#endif rc = 1; } /* Restart rx engine, if its stopped */ if ((readl(pi->sdma_base + SDMA_SDCM) & SDMA_SDCM_ERD) == 0) mpsc_start_rx(pi); tty_flip_buffer_push(tty); return rc;}static void mpsc_setup_tx_desc(struct mpsc_port_info *pi, u32 count, u32 intr){ struct mpsc_tx_desc *txre; txre = (struct mpsc_tx_desc *)(pi->txr + (pi->txr_head * MPSC_TXRE_SIZE)); txre->bytecnt = cpu_to_be16(count); txre->shadow = txre->bytecnt; wmb(); /* ensure cmdstat is last field updated */ txre->cmdstat = cpu_to_be32(SDMA_DESC_CMDSTAT_O | SDMA_DESC_CMDSTAT_F | SDMA_DESC_CMDSTAT_L | ((intr) ? SDMA_DESC_CMDSTAT_EI : 0)); wmb(); dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE, DMA_BIDIRECTIONAL);#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ flush_dcache_range((ulong)txre, (ulong)txre + MPSC_TXRE_SIZE);#endif}static void mpsc_copy_tx_data(struct mpsc_port_info *pi){ struct circ_buf *xmit = &pi->port.info->xmit; u8 *bp; u32 i; /* Make sure the desc ring isn't full */ while (CIRC_CNT(pi->txr_head, pi->txr_tail, MPSC_TXR_ENTRIES) < (MPSC_TXR_ENTRIES - 1)) { if (pi->port.x_char) { /* * Ideally, we should use the TCS field in * CHR_1 to put the x_char out immediately but * errata prevents us from being able to read * CHR_2 to know that its safe to write to * CHR_1. Instead, just put it in-band with * all the other Tx data. */ bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); *bp = pi->port.x_char; pi->port.x_char = 0; i = 1; } else if (!uart_circ_empty(xmit) && !uart_tx_stopped(&pi->port)) { i = min((u32)MPSC_TXBE_SIZE, (u32)uart_circ_chars_pending(xmit)); i = min(i, (u32)CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); bp = pi->txb + (pi->txr_head * MPSC_TXBE_SIZE); memcpy(bp, &xmit->buf[xmit->tail], i); xmit->tail = (xmit->tail + i) & (UART_XMIT_SIZE - 1); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&pi->port); } else { /* All tx data copied into ring bufs */ return; } dma_cache_sync(pi->port.dev, (void *)bp, MPSC_TXBE_SIZE, DMA_BIDIRECTIONAL);#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ flush_dcache_range((ulong)bp, (ulong)bp + MPSC_TXBE_SIZE);#endif mpsc_setup_tx_desc(pi, i, 1); /* Advance to next descriptor */ pi->txr_head = (pi->txr_head + 1) & (MPSC_TXR_ENTRIES - 1); }}static int mpsc_tx_intr(struct mpsc_port_info *pi){ struct mpsc_tx_desc *txre; int rc = 0; unsigned long iflags; spin_lock_irqsave(&pi->tx_lock, iflags); if (!mpsc_sdma_tx_active(pi)) { txre = (struct mpsc_tx_desc *)(pi->txr + (pi->txr_tail * MPSC_TXRE_SIZE)); dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE, DMA_FROM_DEVICE);#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ invalidate_dcache_range((ulong)txre, (ulong)txre + MPSC_TXRE_SIZE);#endif while (!(be32_to_cpu(txre->cmdstat) & SDMA_DESC_CMDSTAT_O)) { rc = 1; pi->port.icount.tx += be16_to_cpu(txre->bytecnt); pi->txr_tail = (pi->txr_tail+1) & (MPSC_TXR_ENTRIES-1); /* If no more data to tx, fall out of loop */ if (pi->txr_head == pi->txr_tail) break; txre = (struct mpsc_tx_desc *)(pi->txr + (pi->txr_tail * MPSC_TXRE_SIZE)); dma_cache_sync(pi->port.dev, (void *)txre, MPSC_TXRE_SIZE, DMA_FROM_DEVICE);#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) if (pi->cache_mgmt) /* GT642[46]0 Res #COMM-2 */ invalidate_dcache_range((ulong)txre, (ulong)txre + MPSC_TXRE_SIZE);#endif } mpsc_copy_tx_data(pi); mpsc_sdma_start_tx(pi); /* start next desc if ready */ } spin_unlock_irqrestore(&pi->tx_lock, iflags); return rc;}/* * This is the driver's interrupt handler. To avoid a race, we first clear * the interrupt, then handle any completed Rx/Tx descriptors. When done * handling those descriptors, we restart the Rx/Tx engines if they're stopped. */static irqreturn_t mpsc_sdma_intr(int irq, void *dev_id){ struct mpsc_port_info *pi = dev_id; ulong iflags; int rc = IRQ_NONE; pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Received\n",pi->port.line); spin_lock_irqsave(&pi->port.lock, iflags); mpsc_sdma_intr_ack(pi); if (mpsc_rx_intr(pi)) rc = IRQ_HANDLED; if (mpsc_tx_intr(pi)) rc = IRQ_HANDLED; spin_unlock_irqrestore(&pi->port.lock, iflags); pr_debug("mpsc_sdma_intr[%d]: SDMA Interrupt Handled\n", pi->port.line); return rc;}/* ****************************************************************************** * * serial_core.c Interface routines * ****************************************************************************** */static uint mpsc_tx_empty(struct uart_port *port){ struct mpsc_port_info *pi = (struct mpsc_port_info *)port; ulong iflags; uint rc; spin_lock_irqsave(&pi->port.lock, iflags); rc = mpsc_sdma_tx_active(pi) ? 0 : TIOCSER_TEMT; spin_unlock_irqrestore(&pi->port.lock, iflags); return rc;}static void mpsc_set_mctrl(struct uart_port *port, uint mctrl){ /* Have no way to set modem control lines AFAICT */}static uint mpsc_get_mctrl(struct uart_port *port){ struct mpsc_port_info *pi = (struct mpsc_port_info *)port; u32 mflags, status; status = (pi->mirror_regs) ? pi->MPSC_CHR_10_m : readl(pi->mpsc_base + MPSC_CHR_10); mflags = 0; if (status & 0x1) mflags |= TIOCM_CTS; if (status & 0x2) mflags |= TIOCM_CAR; return mflags | TIOCM_DSR; /* No way to tell if DSR asserted */}static void mpsc_stop_tx(struct uart_port *port){ struct mpsc_port_info *pi = (struct mpsc_port_info *)port; pr_debug("mpsc_stop_tx[%d]\n", port->line); mpsc_freeze(pi);}static void mpsc_start_tx(struct uart_port *port){ struct mpsc_port_info *pi = (struct mpsc_port_info *)port; unsigned long iflags; spin_lock_irqsave(&pi->tx_lock, iflags); mpsc_unfreeze(pi); mpsc_copy_tx_data(pi); mpsc_sdma_start_tx(pi); spin_unlock_irqrestore(&pi->tx_lock, iflags); pr_debug("mpsc_start_tx[%d]\n", port->line);}static void mpsc_start_rx(struct mpsc_port_info *pi){ pr_debug("mpsc_start_rx[%d]: Starting...\n", pi->port.line); if (pi->rcv_data) { mpsc_enter_hunt(pi); mpsc_sdma_cmd(pi, SDMA_SDCM_ERD); }}static void mpsc_stop_rx(struct uart_port *port){ struct mpsc_port_info *pi = (struct mpsc_port_info *)port; pr_debug("mpsc_stop_rx[%d]: Stopping...\n", port->line); if (pi->mirror_regs) { writel(pi->MPSC_CHR_2_m | MPSC_CHR_2_RA, pi->mpsc_base + MPSC_CHR_2); /* Erratum prevents reading CHR_2 so just delay for a while */ udelay(100); } else { writel(readl(pi->mpsc_base + MPSC_CHR_2) | MPSC_CHR_2_RA, pi->mpsc_base + MPSC_CHR_2); while (readl(pi->mpsc_base + MPSC_CHR_2) & MPSC_CHR_2_RA) udelay(10); } mpsc_sdma_cmd(pi, SDMA_SDCM_AR);}static void mpsc_enable_ms(struct uart_port *port){}static void mpsc_break_ctl(struct uart_port *port, int ctl){ struct mpsc_port_info *pi = (struct mpsc_port_info *)port; ulong flags; u32 v; v = ctl ? 0x00ff0000 : 0; spin_lock_irqsave(&pi->port.lock, flags); if (pi->mirror_regs) pi->MPSC_CHR_1_m = v; writel(v, pi->mpsc_base + MPSC_CHR_1); spin_unlock_irqrestore(&pi->port.lock, flags);}static int mpsc_startup(struct uart_port *port){ struct mpsc_port_info *pi = (struct mpsc_port_info *)port; u32 flag = 0; int rc; pr_debug("mpsc_startup[%d]: Starting up MPSC, irq: %d\n", port->line, pi->port.irq); if ((rc = mpsc_make_ready(pi)) == 0) { /* Setup IRQ handler */ mpsc_sdma_intr_ack(pi); /* If irq's are shared, need to set flag */ if (mpsc_ports[0].port.irq == mpsc_ports[1].port.irq) flag = IRQF_SHARED; if (request_irq(pi->port.irq, mpsc_sdma_intr, flag, "mpsc-sdma", pi)) printk(KERN_ERR "MPSC: Can't get SDMA IRQ %d\n", pi->port.irq); mpsc_sdma_intr_unmask(pi, 0xf); mpsc_sdma_set_rx_ring(pi, (struct mpsc_rx_desc *)(pi->rxr_p + (pi->rxr_posn * MPSC_RXRE_SIZE))); } return rc;}static void mpsc_shutdown(struct uart_port *port){ struct mpsc_port_info *pi = (struct mpsc_port_info *)port; pr_debug("mpsc_shutdown[%d]: Shutting down MPSC\n", port->line); mpsc_sdma_stop(pi); free_irq(pi->port.irq, pi);}static void mpsc_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old){ struct mpsc_port_info *pi = (struct mpsc_port_info *)port; u32 baud; ulong flags; u32 chr_bits, stop_bits, par; pi->c_iflag = termios->c_iflag; pi->c_cflag = termios->c_cflag; switch (termios->c_cflag & CSIZE) { case CS5: chr_bits = MPSC_MPCR_CL_5; break; case CS6: chr_bits = MPSC_MPCR_CL_6; break; case CS7: chr_bits = MPSC_MPCR_CL_7; break; case CS8: default: chr_bits = MPSC_MPCR_CL_8; break; } if (termios->c_cflag & CSTOPB) stop_bits = MPSC_MPCR_SBL_2; else stop_bits = MPSC_MPCR_SBL_1; par = MPSC_CHR_2_PAR_EVEN; if (termios->c_cflag & PARENB) if (termios->c_cflag & PARODD) par = MPSC_CHR_2_PAR_ODD;#ifdef CMSPAR if (termios->c_cflag & CMSPAR) { if (termios->c_cflag & PARODD) par = MPSC_CHR_2_PAR_MARK; else par = MPSC_CHR_2_PAR_SPACE; }#endif baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk); spin_lock_irqsave(&pi->port.lock, flags); uart_update_timeout(port, termios->c_cflag, baud); mpsc_set_char_length(pi, chr_bits); mpsc_set_stop_bit_length(pi, stop_bits); mpsc_set_parity(pi, par); mpsc_set_baudrate(pi, baud); /* Characters/events to read */ pi->port.read_status_mask = SDMA_DESC_CMDSTAT_OR; if (termios->c_iflag & INPCK) pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_PE | SDMA_DESC_CMDSTAT_FR; if (termios->c_iflag & (BRKINT | PARMRK)) pi->port.read_status_mask |= SDMA_DESC_CMDSTAT_BR; /* Characters/events to ignore */ pi->port.ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_PE | SDMA_DESC_CMDSTAT_FR; if (termios->c_iflag & IGNBRK) { pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_BR; if (termios->c_iflag & IGNPAR) pi->port.ignore_status_mask |= SDMA_DESC_CMDSTAT_OR; } if ((termios->c_cflag & CREAD)) { if (!pi->rcv_data) { pi->rcv_data = 1; mpsc_start_rx(pi); } } else if (pi->rcv_data) { mpsc_stop_rx(port); pi->rcv_data = 0; } spin_unlock_irqrestore(&pi->port.lock, flags);}static const char *mpsc_type(struct uart_port *port){ pr_debug("mpsc_type[%d]: port type: %s\n", port->line,MPSC_DRIVER_NAME); return MPSC_DRIVER_NAME;}static int mpsc_request_port(struct uart_port *port){ /* Should make chip/platform specific call */ return 0;}static void mpsc_release_port(struct uart_port *port){ struct mpsc_port_info *pi = (struct mpsc_port_info *)port; if (pi->ready) { mpsc_uninit_rings(pi); mpsc_free_ring_mem(pi); pi->ready = 0; }}static void mpsc_config_port(struct uart_port *port, int flags){}static int mpsc_verify_port(struct uart_port *port, struct serial_struct *ser){ struct mpsc_port_info *pi = (struct mpsc_port_info *)port; int rc = 0; pr_debug("mpsc_verify_port[%d]: Verifying port data\n", pi->port.line); if (ser->type != PORT_UNKNOWN && ser->type != PORT_MPSC) rc = -EINVAL; else if (pi->port.irq != ser->irq) rc = -EINVAL; else if (ser->io_type != SERIAL_IO_MEM) rc = -EINVAL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -