📄 serial_sicc.c
字号:
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; } /* disable interrupts while reading and clearing registers */ spin_lock_irqsave(&info->state->sicc_lock,flags); 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);*/ spin_unlock_irqrestore(&info->state->sicc_lock,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; /* lock info->xmit while adding character to tx buffer */ spin_lock_irqsave(&info->state->sicc_lock,flags); 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); } spin_unlock_irqrestore(&info->state->sicc_lock,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; /* disable interrupts while transmitting characters */ spin_lock_irqsave(&info->state->sicc_lock,flags); siccuart_enable_tx_interrupt(info); spin_unlock_irqrestore(&info->state->sicc_lock,flags);}static int siccuart_write(struct tty_struct *tty, 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; /* lock info->xmit while removing characters from buffer */ spin_lock_irqsave(&info->state->sicc_lock,flags); 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; } if (info->xmit.head != info->xmit.tail && !tty->stopped && !tty->hw_stopped) siccuart_enable_tx_interrupt(info); spin_unlock_irqrestore(&info->state->sicc_lock,flags); 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){ struct SICC_info *info = tty->driver_data; return CIRC_CNT(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE);}static void siccuart_flush_buffer(struct tty_struct *tty){ struct SICC_info *info = tty->driver_data; unsigned long flags; pr_debug("siccuart_flush_buffer(%d) called\n", tty->index); /* lock info->xmit while zeroing buffer counts */ spin_lock_irqsave(&info->state->sicc_lock,flags); info->xmit.head = info->xmit.tail = 0; spin_unlock_irqrestore(&info->state->sicc_lock,flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty);}/* * This function is used to send a high-priority XON/XOFF character to * the device */static void siccuart_send_xchar(struct tty_struct *tty, char ch){ struct SICC_info *info = tty->driver_data; info->x_char = ch; if (ch) siccuart_enable_tx_interrupt(info);}static void siccuart_throttle(struct tty_struct *tty){ struct SICC_info *info = tty->driver_data; unsigned long flags; if (I_IXOFF(tty)) siccuart_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios->c_cflag & CRTSCTS) { /* disable interrupts while setting modem control lines */ spin_lock_irqsave(&info->state->sicc_lock,flags); info->mctrl &= ~TIOCM_RTS; info->port->set_mctrl(info->port, info->mctrl); spin_unlock_irqrestore(&info->state->sicc_lock,flags); }}static void siccuart_unthrottle(struct tty_struct *tty){ struct SICC_info *info = (struct SICC_info *) tty->driver_data; unsigned long flags; if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else siccuart_send_xchar(tty, START_CHAR(tty)); } if (tty->termios->c_cflag & CRTSCTS) { /* disable interrupts while setting modem control lines */ spin_lock_irqsave(&info->state->sicc_lock,flags); info->mctrl |= TIOCM_RTS; info->port->set_mctrl(info->port, info->mctrl); spin_unlock_irqrestore(&info->state->sicc_lock,flags); }}static int get_serial_info(struct SICC_info *info, struct serial_struct *retinfo){ struct SICC_state *state = info->state; struct SICC_port *port = info->port; struct serial_struct tmp; memset(&tmp, 0, sizeof(tmp)); tmp.type = 0; tmp.line = state->line; tmp.port = port->uart_base; if (HIGH_BITS_OFFSET) tmp.port_high = port->uart_base >> HIGH_BITS_OFFSET; tmp.irq = port->irqrx; tmp.flags = 0; tmp.xmit_fifo_size = port->fifosize; tmp.baud_base = port->uartclk / 16; tmp.close_delay = state->close_delay; tmp.closing_wait = state->closing_wait; tmp.custom_divisor = state->custom_divisor; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; return 0;}static int set_serial_info(struct SICC_info *info, struct serial_struct *newinfo){ struct serial_struct new_serial; struct SICC_state *state, old_state; struct SICC_port *port; unsigned long new_port; unsigned int i, change_irq, change_port; int retval = 0; if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) return -EFAULT; state = info->state; old_state = *state; port = info->port; new_port = new_serial.port; if (HIGH_BITS_OFFSET) new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; change_irq = new_serial.irq != port->irqrx; change_port = new_port != port->uart_base; if (!capable(CAP_SYS_ADMIN)) { if (change_irq || change_port || (new_serial.baud_base != port->uartclk / 16) || (new_serial.close_delay != state->close_delay) || (new_serial.xmit_fifo_size != port->fifosize) || ((new_serial.flags & ~ASYNC_USR_MASK) != (state->flags & ~ASYNC_USR_MASK))) return -EPERM; state->flags = ((state->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); info->flags = ((info->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); state->custom_divisor = new_serial.custom_divisor; goto check_and_exit; } if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || (new_serial.baud_base < 9600)) return -EINVAL; if (new_serial.type && change_port) { for (i = 0; i < SERIAL_SICC_NR; i++) if ((port != sicc_ports + i) && sicc_ports[i].uart_base != new_port) return -EADDRINUSE; } if ((change_port || change_irq) && (state->count > 1)) return -EBUSY; /* * OK, past this point, all the error checking has been done. * At this point, we start making changes..... */ port->uartclk = new_serial.baud_base * 16; state->flags = ((state->flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | (info->flags & ASYNC_INTERNAL_FLAGS)); state->custom_divisor = new_serial.custom_divisor; state->close_delay = new_serial.close_delay * HZ / 100; state->closing_wait = new_serial.closing_wait * HZ / 100; info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; port->fifosize = new_serial.xmit_fifo_size; if (change_port || change_irq) { /* * We need to shutdown the serial port at the old * port/irq combination. */ siccuart_shutdown(info); port->irqrx = new_serial.irq; port->uart_base = new_port; }check_and_exit: if (!port->uart_base) return 0; if (info->flags & ASYNC_INITIALIZED) { if ((old_state.flags & ASYNC_SPD_MASK) != (state->flags & ASYNC_SPD_MASK) || (old_state.custom_divisor != state->custom_divisor)) { if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) info->tty->alt_speed = 115200; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) info->tty->alt_speed = 230400; if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; siccuart_change_speed(info, NULL); } } else retval = siccuart_startup(info); return retval;}/* * get_lsr_info - get line status register info */static int get_lsr_info(struct SICC_info *info, unsigned int *value){ unsigned int result, status; unsigned long flags; /* disable interrupts while reading status from port */ spin_lock_irqsave(&info->state->sicc_lock,flags); status = readb(info->port->uart_base + BL_SICC_LSR); spin_unlock_irqrestore(&info->state->sicc_lock,flags); result = status & _LSR_TSR_EMPTY ? TIOCSER_TEMT : 0; /* * If we're about to load something into the transmit * register, we'll pretend the transmitter isn't empty to * avoid a race condition (depending on when the transmit * interrupt happens). */ if (info->x_char || ((CIRC_CNT(info->xmit.head, info->xmit.tail, SICC_XMIT_SIZE) > 0) && !info->tty->stopped && !info->tty->hw_stopped))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -