📄 serial_sicc.c
字号:
/* * If the transmitter hasn't cleared in twice the approximate * amount of time to send the entire FIFO, it probably won't * ever clear. This assumes the UART isn't doing flow * control, which is currently the case. Hence, if it ever * takes longer than info->timeout, this is probably due to a * UART bug of some kind. So, we clamp the timeout parameter at * 2*info->timeout. */ if (!timeout || timeout > 2 * info->timeout) timeout = 2 * info->timeout; expire = jiffies + timeout; pr_debug("siccuart_wait_until_sent(%d), jiff=%lu, expire=%lu char_time=%lu...\n", tty->index, jiffies, expire, char_time); while ((readb(info->port->uart_base + BL_SICC_LSR) & _LSR_TX_ALL) != _LSR_TX_ALL) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(char_time); if (signal_pending(current)) break; if (timeout && time_after(jiffies, expire)) break; } set_current_state(TASK_RUNNING);}static void siccuart_hangup(struct tty_struct *tty){ struct SICC_info *info = tty->driver_data; struct SICC_state *state = info->state; siccuart_flush_buffer(tty); if (info->flags & ASYNC_CLOSING) return; siccuart_shutdown(info); info->event = 0; state->count = 0; info->flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = NULL; wake_up_interruptible(&info->open_wait);}static int block_til_ready(struct tty_struct *tty, struct file *filp, struct SICC_info *info){ DECLARE_WAITQUEUE(wait, current); struct SICC_state *state = info->state; unsigned long flags; int do_clocal = 0, extra_count = 0, retval; /* * If the device is in the middle of being closed, then block * until it's done, and then try again. */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); return (info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; } /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } if (tty->termios->c_cflag & CLOCAL) do_clocal = 1; /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in * this loop, state->count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; add_wait_queue(&info->open_wait, &wait); save_flags(flags); cli(); if (!tty_hung_up_p(filp)) { extra_count = 1; state->count--; } restore_flags(flags); info->blocked_open++; while (1) { save_flags(flags); cli(); if (tty->termios->c_cflag & CBAUD) { info->mctrl = TIOCM_DTR | TIOCM_RTS; info->port->set_mctrl(info->port, info->mctrl); } restore_flags(flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { if (info->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } if (!(info->flags & ASYNC_CLOSING) && (do_clocal /*|| (UART_GET_FR(info->port) & SICC_UARTFR_DCD)*/)) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; } schedule(); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); if (extra_count) state->count++; info->blocked_open--; if (retval) return retval; info->flags |= ASYNC_NORMAL_ACTIVE; return 0;}static struct SICC_info *siccuart_get(int line){ struct SICC_info *info; struct SICC_state *state = sicc_state + line; state->count++; if (state->info) return state->info; info = kmalloc(sizeof(struct SICC_info), GFP_KERNEL); if (info) { memset(info, 0, sizeof(struct SICC_info)); init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); init_waitqueue_head(&info->delta_msr_wait); info->flags = state->flags; info->state = state; info->port = sicc_ports + line; tasklet_init(&info->tlet, siccuart_tasklet_action, (unsigned long)info); } if (state->info) { kfree(info); return state->info; } state->info = info; return info;}static int siccuart_open(struct tty_struct *tty, struct file *filp){ struct SICC_info *info; int retval, line = tty->index; // is this a line that we've got? if (line >= SERIAL_SICC_NR) { return -ENODEV; } info = siccuart_get(line); if (!info) return -ENOMEM; tty->driver_data = info; info->tty = tty; info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; /* * Make sure we have the temporary buffer allocated */ if (!tmp_buf) { unsigned long page = get_zeroed_page(GFP_KERNEL); if (tmp_buf) free_page(page); else if (!page) { return -ENOMEM; } tmp_buf = (u_char *)page; } /* * If the port is in the middle of closing, bail out now. */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); return -EAGAIN; } /* * Start up the serial port */ retval = siccuart_startup(info); if (retval) { return retval; } retval = block_til_ready(tty, filp, info); if (retval) { return retval; }#ifdef CONFIG_SERIAL_SICC_CONSOLE if (siccuart_cons.cflag && siccuart_cons.index == line) { tty->termios->c_cflag = siccuart_cons.cflag; siccuart_cons.cflag = 0; siccuart_change_speed(info, NULL); }#endif return 0;}static struct tty_operations sicc_ops = { .open = siccuart_open, .close = siccuart_close, .write = siccuart_write, .put_char = siccuart_put_char, .flush_chars = siccuart_flush_chars, .write_room = siccuart_write_room, .chars_in_buffer = siccuart_chars_in_buffer, .flush_buffer = siccuart_flush_buffer, .ioctl = siccuart_ioctl, .throttle = siccuart_throttle, .unthrottle = siccuart_unthrottle, .send_xchar = siccuart_send_xchar, .set_termios = siccuart_set_termios, .stop = siccuart_stop, .start = siccuart_start, .hangup = siccuart_hangup, .break_ctl = siccuart_break_ctl, .wait_until_sent = siccuart_wait_until_sent,};int __init siccuart_init(void){ int i; siccnormal_driver = alloc_tty_driver(SERIAL_SICC_NR); if (!siccnormal_driver) return -ENOMEM; printk("IBM Vesta SICC serial port driver V 0.1 by Yudong Yang and Yi Ge / IBM CRL .\n"); siccnormal_driver->driver_name = "serial_sicc"; siccnormal_driver->owner = THIS_MODULE; siccnormal_driver->name = SERIAL_SICC_NAME; siccnormal_driver->major = SERIAL_SICC_MAJOR; siccnormal_driver->minor_start = SERIAL_SICC_MINOR; siccnormal_driver->type = TTY_DRIVER_TYPE_SERIAL; siccnormal_driver->subtype = SERIAL_TYPE_NORMAL; siccnormal_driver->init_termios = tty_std_termios; siccnormal_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; siccnormal_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; tty_set_operations(siccnormal_driver, &sicc_ops); if (tty_register_driver(siccnormal_driver)) panic("Couldn't register SICC serial driver\n"); for (i = 0; i < SERIAL_SICC_NR; i++) { struct SICC_state *state = sicc_state + i; state->line = i; state->close_delay = 5 * HZ / 10; state->closing_wait = 30 * HZ; } return 0;}__initcall(siccuart_init);#ifdef CONFIG_SERIAL_SICC_CONSOLE/************** console driver *****************//* * This code is currently never used; console->read is never called. * Therefore, although we have an implementation, we don't use it. * FIXME: the "const char *s" should be fixed to "char *s" some day. * (when the definition in include/linux/console.h is also fixed) */#ifdef used_and_not_const_char_pointerstatic int siccuart_console_read(struct console *co, const char *s, u_int count){ struct SICC_port *port = &sicc_ports[co->index]; unsigned int status; char *w; int c; pr_debug("siccuart_console_read() called\n"); c = 0; w = s; while (c < count) { if(readb(port->uart_base + BL_SICC_LSR) & _LSR_RBR_FULL) { *w++ = readb(port->uart_base + BL_SICC_RBR); c++; } else { // nothing more to get, return return c; } } // return the count return c;}#endif/* * Print a string to the serial port trying not to disturb * any possible real use of the port... * * The console_lock must be held when we get here. */static void siccuart_console_write(struct console *co, const char *s, u_int count){ struct SICC_port *port = &sicc_ports[co->index]; unsigned int old_cr; int i; /* * First save the CR then disable the interrupts */ old_cr = readb(port->uart_base + BL_SICC_TxCR); writeb(old_cr & ~_TxCR_DME_MASK, port->uart_base + BL_SICC_TxCR); /* * Now, do each character */ for (i = 0; i < count; i++) { while ((readb(port->uart_base + BL_SICC_LSR)&_LSR_TX_ALL) != _LSR_TX_ALL); writeb(s[i], port->uart_base + BL_SICC_TBR); if (s[i] == '\n') { while ((readb(port->uart_base + BL_SICC_LSR)&_LSR_TX_ALL) != _LSR_TX_ALL); writeb('\r', port->uart_base + BL_SICC_TBR); } } /* * Finally, wait for transmitter to become empty * and restore the TCR */ while ((readb(port->uart_base + BL_SICC_LSR)&_LSR_TX_ALL) != _LSR_TX_ALL); writeb(old_cr, port->uart_base + BL_SICC_TxCR);}/* * Receive character from the serial port */static int siccuart_console_wait_key(struct console *co){ struct SICC_port *port = &sicc_ports[co->index]; int c; while(!(readb(port->uart_base + BL_SICC_LSR) & _LSR_RBR_FULL)); c = readb(port->uart_base + BL_SICC_RBR); return c;}static struct tty_driver *siccuart_console_device(struct console *c, int *index){ *index = c->index; return siccnormal_driver;}static int __init siccuart_console_setup(struct console *co, char *options){ struct SICC_port *port; int baud = 9600; int bits = 8; int parity = 'n'; u_int cflag = CREAD | HUPCL | CLOCAL; u_int lcr_h, quot; if (co->index >= SERIAL_SICC_NR) co->index = 0; port = &sicc_ports[co->index]; if (port->uart_base == 0) port->uart_base = (int)ioremap(port->uart_base_phys, PAGE_SIZE); if (options) { char *s = options; baud = simple_strtoul(s, NULL, 10); while (*s >= '0' && *s <= '9') s++; if (*s) parity = *s++; if (*s) bits = *s - '0'; } /* * Now construct a cflag setting. */ switch (baud) { case 1200: cflag |= B1200; break; case 2400: cflag |= B2400; break; case 4800: cflag |= B4800; break; default: cflag |= B9600; baud = 9600; break; case 19200: cflag |= B19200; break; case 38400: cflag |= B38400; break; case 57600: cflag |= B57600; break; case 115200: cflag |= B115200; break; } switch (bits) { case 7: cflag |= CS7; lcr_h = _LCR_PE_DISABLE | _LCR_DB_7_BITS | _LCR_SB_1_BIT; break; default: cflag |= CS8; lcr_h = _LCR_PE_DISABLE | _LCR_DB_8_BITS | _LCR_SB_1_BIT; break; } switch (parity) { case 'o': case 'O': cflag |= PARODD; lcr_h |= _LCR_PTY_ODD; break; case 'e': case 'E': cflag |= PARENB; lcr_h |= _LCR_PE_ENABLE | _LCR_PTY_ODD; break; } co->cflag = cflag; { // a copy of is inserted here ppc403SetBaud(com_port, (int)9600); unsigned long divisor, clockSource, temp; unsigned int rate = baud; /* 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*rate) - 1; /* divisor has only 12 bits of resolution */ if(divisor>0x00000FFF){ divisor=0x00000FFF; } quot = divisor; } writeb((quot & 0x00000F00)>>8, port->uart_base + BL_SICC_BRDH ); writeb( quot & 0x00000FF, 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, port->uart_base + BL_SICC_CTL2); writeb(lcr_h, port->uart_base + BL_SICC_LCR); writeb(_RCR_ER_ENABLE | _RCR_PME_HARD, port->uart_base + BL_SICC_RCR); writeb( _TxCR_ET_ENABLE , port->uart_base + BL_SICC_TxCR); // writeb(, info->port->uart_base + BL_SICC_RCR ); /* * Transmitter Command Register: Transmitter enabled & DMA + TBR interrupt * + Transmitter Empty interrupt + Transmitter error interrupt disabled & * Stop mode when CTS active enabled & Transmit Break + Pattern Generation * mode disabled. */ writeb( 0x00, port->uart_base + BL_SICC_IrCR ); // disable IrDA readb(port->uart_base + BL_SICC_RBR); writeb(0xf8, port->uart_base + BL_SICC_LSR); /* reset bits 0-4 of LSR */ /* we will enable the port as we need it */ return 0;}static struct console siccuart_cons ={ .name = SERIAL_SICC_NAME, .write = siccuart_console_write,#ifdef used_and_not_const_char_pointer .read = siccuart_console_read,#endif .device = siccuart_console_device, .wait_key = siccuart_console_wait_key, .setup = siccuart_console_setup, .flags = CON_PRINTBUFFER, .index = -1,};void __init sicc_console_init(void){ register_console(&siccuart_cons);}#endif /* CONFIG_SERIAL_SICC_CONSOLE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -