📄 serial_sicc.c
字号:
}#ifdef SUPPORT_SYSRQ info->sysrq = 0;#endif goto error_return;}static void siccuart_tx_chars(struct SICC_info *info){ struct SICC_port *port = info->port; int count; unsigned char status; if (info->x_char) { writeb(info->x_char, port->uart_base+ BL_SICC_TBR); info->state->icount.tx++; info->x_char = 0; return; } if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) { siccuart_disable_tx_interrupt(info); writeb(status&(~_LSR_RBR_MASK),port->uart_base+BL_SICC_LSR); return; } count = port->fifosize; do { writeb(info->xmit.buf[info->xmit.tail], port->uart_base+ BL_SICC_TBR); info->xmit.tail = (info->xmit.tail + 1) & (SICC_XMIT_SIZE - 1); info->state->icount.tx++; if (info->xmit.head == info->xmit.tail) break; } while (--count > 0); if (CIRC_CNT(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE) < WAKEUP_CHARS) siccuart_event(info, EVT_WRITE_WAKEUP); if (info->xmit.head == info->xmit.tail) { siccuart_disable_tx_interrupt(info); }}static irqreturn_t siccuart_int_rx(int irq, void *dev_id, struct pt_regs *regs){ struct SICC_info *info = dev_id; siccuart_rx_chars(info, regs); return IRQ_HANDLED;}static irqreturn_t siccuart_int_tx(int irq, void *dev_id, struct pt_regs *regs){ struct SICC_info *info = dev_id; siccuart_tx_chars(info); return IRQ_HANDLED;}static void siccuart_tasklet_action(unsigned long data){ struct SICC_info *info = (struct SICC_info *)data; struct tty_struct *tty; tty = info->tty; if (!tty || !test_and_clear_bit(EVT_WRITE_WAKEUP, &info->event)) return; if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait);}static int siccuart_startup(struct SICC_info *info){ unsigned long flags; unsigned long page; int retval = 0; if (info->flags & ASYNC_INITIALIZED) { return 0; } page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; if (info->port->uart_base == 0) info->port->uart_base = (int)ioremap(info->port->uart_base_phys, PAGE_SIZE); if (info->port->uart_base == 0) { free_page(page); return -ENOMEM; } save_flags(flags); cli(); if (info->xmit.buf) free_page(page); else info->xmit.buf = (unsigned char *) page; info->mctrl = 0; if (info->tty->termios->c_cflag & CBAUD) info->mctrl = TIOCM_RTS | TIOCM_DTR; info->port->set_mctrl(info->port, info->mctrl); /* * initialise the old status of the modem signals */ info->old_status = 0; // UART_GET_FR(info->port) & AMBA_UARTFR_MODEM_ANY; if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit.head = info->xmit.tail = 0; /* * Set up the tty->alt_speed kludge */ if (info->tty) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) info->tty->alt_speed = 115200; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) info->tty->alt_speed = 230400; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; } writeb( 0x00, info->port->uart_base + BL_SICC_IrCR ); // disable IrDA /* * and set the speed of the serial port */ siccuart_change_speed(info, 0); // enable rx/tx ports writeb(_RCR_ER_ENABLE /*| _RCR_PME_HARD*/, info->port->uart_base + BL_SICC_RCR); writeb(_TxCR_ET_ENABLE , info->port->uart_base + BL_SICC_TxCR); readb(info->port->uart_base + BL_SICC_RBR); // clear rx port writeb(0xf8, info->port->uart_base + BL_SICC_LSR); /* reset bits 0-4 of LSR */ /* * Finally, enable interrupts */ /* * Allocate the IRQ */ retval = request_irq(info->port->irqrx, siccuart_int_rx, 0, "SICC rx", info); if (retval) { if (capable(CAP_SYS_ADMIN)) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); retval = 0; } goto errout; } retval = request_irq(info->port->irqtx, siccuart_int_tx, 0, "SICC tx", info); if (retval) { if (capable(CAP_SYS_ADMIN)) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); retval = 0; } free_irq(info->port->irqrx, info); goto errout; } siccuart_enable_rx_interrupt(info); info->flags |= ASYNC_INITIALIZED; restore_flags(flags); return 0;errout: restore_flags(flags); return retval;}/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */static void siccuart_shutdown(struct SICC_info *info){ unsigned long flags; if (!(info->flags & ASYNC_INITIALIZED)) return; save_flags(flags); cli(); /* Disable interrupts */ /* * clear delta_msr_wait queue to avoid mem leaks: we may free the irq * here so the queue might never be woken up */ wake_up_interruptible(&info->delta_msr_wait); /* * disable all interrupts, disable the port */ siccuart_disable_rx_interrupt(info); siccuart_disable_tx_interrupt(info); /* * Free the IRQ */ free_irq(info->port->irqtx, info); free_irq(info->port->irqrx, info); if (info->xmit.buf) { unsigned long pg = (unsigned long) info->xmit.buf; info->xmit.buf = NULL; free_page(pg); } if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) info->mctrl &= ~(TIOCM_DTR|TIOCM_RTS); info->port->set_mctrl(info->port, info->mctrl); /* kill off our tasklet */ tasklet_kill(&info->tlet); if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags);}static void siccuart_change_speed(struct SICC_info *info, struct termios *old_termios){ unsigned int lcr_h, baud, quot, cflag, old_rcr, old_tcr, bits; unsigned long flags; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; pr_debug("siccuart_set_cflag(0x%x) called\n", cflag); /* byte size and parity */ switch (cflag & CSIZE) { case CS7: lcr_h = _LCR_PE_DISABLE | _LCR_DB_7_BITS | _LCR_SB_1_BIT; bits = 9; break; default: lcr_h = _LCR_PE_DISABLE | _LCR_DB_8_BITS | _LCR_SB_1_BIT; bits = 10; break; // CS8 } if (cflag & CSTOPB) { lcr_h |= _LCR_SB_2_BIT; bits ++; } if (cflag & PARENB) { lcr_h |= _LCR_PE_ENABLE; bits++; if (!(cflag & PARODD)) lcr_h |= _LCR_PTY_ODD; else lcr_h |= _LCR_PTY_EVEN; } do { /* Determine divisor based on baud rate */ baud = tty_get_baud_rate(info->tty); if (!baud) baud = 9600; { // here is ppc403SetBaud(com_port, baud); unsigned long divisor, clockSource, temp; /* Ensure CICCR[7] is 0 to select Internal Baud Clock */ powerpcMtcic_cr((unsigned long)(powerpcMfcic_cr() & 0xFEFFFFFF)); /* Determine Internal Baud Clock Frequency */ /* powerpcMfclkgpcr() reads DCR 0x120 - the*/ /* SCCR (Serial Clock Control Register) on Vesta */ temp = powerpcMfclkgpcr(); if(temp & 0x00000080) { clockSource = 324000000; } else { clockSource = 216000000; } clockSource = clockSource/(unsigned long)((temp&0x00FC0000)>>18); divisor = clockSource/(16*baud) - 1; /* divisor has only 12 bits of resolution */ if(divisor>0x00000FFF){ divisor=0x00000FFF; } quot = divisor; } if (baud == 38400 && ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) quot = info->state->custom_divisor; if (!quot && old_termios) { info->tty->termios->c_cflag &= ~CBAUD; info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); old_termios = NULL; } } while (quot == 0 && old_termios); /* As a last resort, if the quotient is zero, default to 9600 bps */ if (!quot) quot = (info->port->uartclk / (16 * 9600)) - 1; info->timeout = info->port->fifosize * HZ * bits / baud; info->timeout += HZ/50; /* Add .02 seconds of slop */ if (cflag & CRTSCTS) info->flags |= ASYNC_CTS_FLOW; else info->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD; else info->flags |= ASYNC_CHECK_CD; /* * Set up parity check flag */#define RELEVENT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) info->read_status_mask = _LSR_OE_MASK; if (I_INPCK(info->tty)) info->read_status_mask |= _LSR_FE_MASK | _LSR_PE_MASK; if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) info->read_status_mask |= _LSR_LB_MASK; /* * Characters to ignore */ info->ignore_status_mask = 0; if (I_IGNPAR(info->tty)) info->ignore_status_mask |= _LSR_FE_MASK | _LSR_PE_MASK; if (I_IGNBRK(info->tty)) { info->ignore_status_mask |= _LSR_LB_MASK; /* * If we're ignoring parity and break indicators, * ignore overruns to (for real raw support). */ if (I_IGNPAR(info->tty)) info->ignore_status_mask |= _LSR_OE_MASK; } /* first, disable everything */ save_flags(flags); cli(); old_rcr = readb(info->port->uart_base + BL_SICC_RCR); old_tcr = readb(info->port->uart_base + BL_SICC_TxCR); writeb(0, info->port->uart_base + BL_SICC_RCR); writeb(0, info->port->uart_base + BL_SICC_TxCR); /*RLBtrace (&ppc403Chan0, 0x2000000c, 0, 0);*/ restore_flags(flags); /* Set baud rate */ writeb((quot & 0x00000F00)>>8, info->port->uart_base + BL_SICC_BRDH ); writeb( quot & 0x00000FF, info->port->uart_base + BL_SICC_BRDL ); /* Set CTL2 reg to use external clock (ExtClk) and enable FIFOs. */ /* For now, do NOT use FIFOs since 403 UART did not have this */ /* capability and this driver was inherited from 403UART. */ writeb(_CTL2_EXTERN, info->port->uart_base + BL_SICC_CTL2); writeb(lcr_h, info->port->uart_base + BL_SICC_LCR); writeb(old_rcr, info->port->uart_base + BL_SICC_RCR); // restore rcr writeb(old_tcr, info->port->uart_base + BL_SICC_TxCR); // restore txcr}static void siccuart_put_char(struct tty_struct *tty, u_char ch){ struct SICC_info *info = tty->driver_data; unsigned long flags; if (!tty || !info->xmit.buf) return; save_flags(flags); cli(); if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE) != 0) { info->xmit.buf[info->xmit.head] = ch; info->xmit.head = (info->xmit.head + 1) & (SICC_XMIT_SIZE - 1); } restore_flags(flags);}static void siccuart_flush_chars(struct tty_struct *tty){ struct SICC_info *info = tty->driver_data; unsigned long flags; if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped || !info->xmit.buf) return; save_flags(flags); cli(); siccuart_enable_tx_interrupt(info); restore_flags(flags);}static int siccuart_write(struct tty_struct *tty, int from_user, const u_char * buf, int count){ struct SICC_info *info = tty->driver_data; unsigned long flags; int c, ret = 0; if (!tty || !info->xmit.buf || !tmp_buf) return 0; save_flags(flags); if (from_user) { down(&tmp_buf_sem); while (1) { int c1; c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE); if (count < c) c = count; if (c <= 0) break; c -= copy_from_user(tmp_buf, buf, c); if (!c) { if (!ret) ret = -EFAULT; break; } cli(); c1 = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE); if (c1 < c) c = c1; memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); info->xmit.head = (info->xmit.head + c) & (SICC_XMIT_SIZE - 1); restore_flags(flags); buf += c; count -= c; ret += c; } up(&tmp_buf_sem); } else { cli(); while (1) { c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE); if (count < c) c = count; if (c <= 0) break; memcpy(info->xmit.buf + info->xmit.head, buf, c); info->xmit.head = (info->xmit.head + c) & (SICC_XMIT_SIZE - 1); buf += c; count -= c; ret += c; } restore_flags(flags); } if (info->xmit.head != info->xmit.tail && !tty->stopped && !tty->hw_stopped) siccuart_enable_tx_interrupt(info); return ret;}static int siccuart_write_room(struct tty_struct *tty){ struct SICC_info *info = tty->driver_data; return CIRC_SPACE(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE);}static int siccuart_chars_in_buffer(struct tty_struct *tty){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -