#if defined(CONFIG_SERIAL_AVM_ASC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ #endif #if defined(CONFIG_SERIAL_IFX_ASC) #error CONFIG Fehler: entweder CONFIG_SERIAL_IFX_ASC oder CONFIG_SERIAL_UART_AVM #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "serial_avm_asc.h" #define UART_PORT_WIDTH 8 #define SERIAL_UART_AVM_NAME "ttyS" #define SERIAL_UART_AVM_MAJOR TTY_MAJOR #define SERIAL_UART_AVM_MINOR 64 static const char uart_avm_name[] = "uart avm asc"; #define tx_enabled(port) ((port)->unused[0]) #define rx_enabled(port) ((port)->unused[1]) static void uart_avm_stop_tx(struct uart_port *port) { if (tx_enabled(port)) { struct ifx_asc_port_priv *port_priv_data = port->private_data; disable_irq(port_priv_data->tir); tx_enabled(port) = 0; } } static void uart_avm_start_tx(struct uart_port *port) { if (!tx_enabled(port)) { struct ifx_asc_port_priv *port_priv_data = port->private_data; enable_irq(port_priv_data->tir); tx_enabled(port) = 1; } } static void uart_avm_stop_rx(struct uart_port *port) { if (rx_enabled(port)) { struct ifx_asc_port_priv *port_priv_data = port->private_data; disable_irq(port_priv_data->rir); rx_enabled(port) = 0; } } static void uart_avm_enable_ms(struct uart_port *port) { } static irqreturn_t uart_avm_rx_chars(int irq, void *dev_id) { struct uart_port *port = dev_id; ifx_asc_port_priv_t *priv = port->private_data; ifx_asc_reg_t *asc_reg = priv->base; struct uart_info *info = port->info; struct tty_struct *tty = info->port.tty; unsigned int ch = 0, rsr = 0, fifocnt; char flag; fifocnt = asc_reg->asc_fstat & ASCFSTAT_RXFFLMASK; if ( unlikely(priv->clear_be_in_next_irq) ) { asc_reg->asc_whbstate = ASCWHBSTATE_CLRBE; priv->clear_be_in_next_irq = 0; } while (fifocnt--) { ch = asc_reg->asc_rbuf; rsr = (asc_reg->asc_state & (ASCSTATE_ANY | ASCSTATE_BE)) | UART_DUMMY_UER_RX; flag = TTY_NORMAL; port->icount.rx++; priv->rx_bytes++; /* * Note that the error handling code is * out of the main execution path */ if ( unlikely(rsr & (ASCSTATE_ANY | ASCSTATE_BE)) ) { if ( (rsr & ASCSTATE_PE) ) { port->icount.parity++; priv->rx_parity_error++; asc_reg->asc_whbstate = ASCWHBSTATE_CLRPE; } else if ( (rsr & ASCSTATE_FE) ) { port->icount.frame++; priv->rx_frame_error++; asc_reg->asc_whbstate = ASCWHBSTATE_CLRFE; } if ( (rsr & ASCSTATE_ROE) ) { port->icount.overrun++; priv->rx_overrun_error++; asc_reg->asc_whbstate = ASCWHBSTATE_CLRROE; } if ( (rsr & ASCSTATE_BE ) ) { flag = TTY_BREAK; port->icount.brk++; priv->clear_be_in_next_irq = 1; /* * We do the SysRQ and SAK checking * here because otherwise the break * may get masked by ignore_status_mask * or read_status_mask. */ if (uart_handle_break(port)){ goto ignore_char; } } rsr &= port->read_status_mask; if ( (rsr & ASCMCON_PAL) ) flag = TTY_PARITY; else if ( (rsr & ASCMCON_FEN) ) flag = TTY_FRAME; } if (uart_handle_sysrq_char(port, ch)){ goto ignore_char; } uart_insert_char(port, rsr, ASCMCON_ROEN, ch, flag); } ignore_char: if ( ch != 0 ) // If the final ch == 0, we will not do tty_flip_buffer_push. tty_flip_buffer_push(tty); return IRQ_HANDLED; } static irqreturn_t uart_avm_tx_chars(int irq, void *dev_id) { struct uart_port *port = dev_id; struct circ_buf *xmit = &port->info->xmit; ifx_asc_port_priv_t *priv = port->private_data; ifx_asc_reg_t *asc_reg = priv->base; int fifocnt; if (port->x_char) { uart_avm_console_putchar(port, (int)(port->x_char)); port->icount.tx++; port->x_char = 0; goto out; } if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { uart_avm_stop_tx(port); goto out; } fifocnt = (asc_reg->asc_fstat & ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF; while(fifocnt--) { if ( priv->portwidth == 8 ) { asc_reg->asc_tbuf = xmit->buf[xmit->tail]; } else { *(((char*)&asc_reg->asc_tbuf) + 3) = xmit->buf[xmit->tail]; } xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; if (uart_circ_empty(xmit)) break; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); if (uart_circ_empty(xmit)) uart_avm_stop_tx(port); out: return IRQ_HANDLED; } static unsigned int uart_avm_tx_empty(struct uart_port *port) { /* * FSTAT tells exactly how many bytes are in the FIFO. * The question is whether we really need to wait for all * 16 bytes to be transmitted before reporting that the * transmitter is empty. */ struct ifx_asc_port_priv *port_priv_data = port->private_data; return (port_priv_data->base->asc_fstat & ASCFSTAT_TXFFLMASK) ? 0 : TIOCSER_TEMT; } static unsigned int uart_avm_get_mctrl(struct uart_port *port) { /* no modem control lines */ return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; } static void uart_avm_set_mctrl(struct uart_port *port, unsigned int mctrl) { /* no modem control - just return */ return; } static void uart_avm_break_ctl(struct uart_port *port, int break_state) { /* no way to send a break */ return; } static int uart_avm_startup(struct uart_port *port){ int ret; unsigned long flags; struct ifx_asc_port_priv *port_priv_data = port->private_data; ifx_asc_reg_t *asc_reg = port_priv_data->base; /* block the IRQs */ local_irq_save(flags); /* disable ASC TBIR interrupts in module */ asc_reg->asc_irnen &= ~IFX_ASC_IRQ_LINE_TBIR; /* enable ASC interrupts in module */ asc_reg->asc_irnen = IFX_ASC_IRQ_LINE_RIR | IFX_ASC_IRQ_LINE_EIR | IFX_ASC_IRQ_LINE_TIR; asc_reg->asc_irnicr = 0x3; asc_reg->asc_irncr = 0x3; if ( port_priv_data->portwidth == 8){ /* setup fifos and timeout */ asc_reg->asc_txfcon = 0x101; asc_reg->asc_rxfcon = 0xa01; asc_reg->asc_eomcon = 0x1040600; } else { /* setup fifos and timeout */ asc_reg->asc_txfcon = 0x101; asc_reg->asc_rxfcon = 0x1001; asc_reg->asc_eomcon = 0x1040300; } local_irq_restore(flags); tx_enabled(port) = 1; rx_enabled(port) = 1; ret = request_irq_on(0, port_priv_data->rir, uart_avm_rx_chars, 0, uart_avm_name, port); if (ret == 0) { ret = request_irq_on(0, port_priv_data->tir, uart_avm_tx_chars, 0, uart_avm_name, port); if (ret) free_irq_on(0, port_priv_data->rir, port); } return ret; } static void uart_avm_shutdown(struct uart_port *port) { struct ifx_asc_port_priv *port_priv_data = port->private_data; ifx_asc_reg_t *asc_reg = port_priv_data->base; /* disable ASC interrupts in module */ asc_reg->asc_irnen = IFX_ASC_IRQ_LINE_MASK_ALL; disable_irq_on(0, port_priv_data->rir); disable_irq_on(0, port_priv_data->eir); free_irq_on(0, port_priv_data->tir, port); free_irq_on(0, port_priv_data->rir, port); /* turn off baudrate generator */ asc_reg->asc_mcon &= ~ASCMCON_R; /* flush and then disable the fifos */ asc_reg->asc_rxfcon = ASCRXFCON_RXFFLU; asc_reg->asc_txfcon = ASCTXFCON_TXFFLU; } #define F_SIZE_CHG 0x01 #define F_BAUDRATE_CHG 0x02 static void uart_avm_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { unsigned long flags; unsigned int cflag = termios->c_cflag; unsigned int f_chg = F_SIZE_CHG | F_BAUDRATE_CHG; struct ifx_asc_port_priv *port_priv_data = port->private_data; ifx_asc_reg_t *asc_reg; u32 word_length = 0; u32 baudrate = 0; u32 fdv, reload, quot; u32 asc_state; /* * We don't support modem control lines. */ termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR); termios->c_cflag |= CLOCAL; if (termios->c_iflag & (BRKINT | PARMRK)) port->read_status_mask |= ASCSTATE_BE; /* * Ask the core to calculate the divisor for us. */ if ( (cflag & CSIZE) != CS7 && (cflag & CSIZE) != CS8 ) { cflag = (cflag & ~CSIZE) | (old ? (old->c_cflag & CSIZE) : CS8); } termios->c_cflag = cflag; // only support baudrate change and word length change if ( old ) { if ( (old->c_cflag & CSIZE) == (cflag & CSIZE) ) { f_chg &= ~F_SIZE_CHG; } if ( (old->c_cflag & CBAUD) == (cflag & CBAUD) ) { f_chg &= ~F_BAUDRATE_CHG; } } if ( f_chg ) { asc_reg = port_priv_data->base; if ( (f_chg & F_SIZE_CHG) ) { if ( (cflag & CSIZE) == CS7 ) { word_length = ASCMCON_WLS_7BIT << ASCMCON_WLSOFFSET; } else { word_length = ASCMCON_WLS_8BIT << ASCMCON_WLSOFFSET; } } if ( (f_chg & F_BAUDRATE_CHG) ) { baudrate = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16 ); quot = uart_get_divisor(port, baudrate); if ( baudrate ) { get_fdv_and_reload_value(baudrate, &fdv, &reload); port_priv_data->baudrate = baudrate; } if (port->info && port->info->port.tty) { struct tty_struct *tty = port->info->port.tty; unsigned int b = port->uartclk / (16 * quot); printk("[%s] baudrate=%d, quot=%d, fdv=%d, reload=%d, b=%d\n", __FUNCTION__, baudrate, quot, fdv, reload, b); tty_encode_baud_rate(tty, baudrate, baudrate); // TODO ist das notwendig? /*--- tty_encode_baud_rate(tty, b, b); ---*/ // vgl. 21285 Treiber } } if ( (f_chg & F_BAUDRATE_CHG) && !baudrate ) { asc_state = 0; } else { asc_state = asc_reg->asc_state; } spin_lock_irqsave(&port->lock, flags); /* * Update the per-port timeout. */ uart_update_timeout(port, termios->c_cflag, baudrate); /* disable receiver */ asc_reg->asc_whbstate = ASCWHBSTATE_CLRREN; if ( (f_chg & F_SIZE_CHG) ) { asc_reg->asc_mcon = (asc_reg->asc_mcon & ~(3 << 2)) | word_length; } if ( (f_chg & F_BAUDRATE_CHG) ) { /* Now we can write the new baudrate into the register */ asc_reg->asc_fdv = fdv; asc_reg->asc_bg = reload; asc_reg->asc_mcon |= ASCMCON_R; } /* enable receiver if necessary */ if ( (asc_state & ASCSTATE_REN) ) { asc_reg->asc_whbstate = ASCWHBSTATE_SETREN; } spin_unlock_irqrestore(&port->lock, flags); } } static const char *uart_avm_type(struct uart_port *port) { return port->type == PORT_IFX_ASC ? "IFX_ASC" : NULL; } static void uart_avm_release_port(struct uart_port *port) { release_mem_region(CPHYSADDR(port->mapbase), sizeof(ifx_asc_reg_t)); } static int uart_avm_request_port(struct uart_port *port) { return request_mem_region(CPHYSADDR(port->mapbase), sizeof(ifx_asc_reg_t), uart_avm_name) != NULL ? 0 : -EBUSY; } static void uart_avm_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE && uart_avm_request_port(port) == 0) port->type = PORT_IFX_ASC; } static int uart_avm_verify_port(struct uart_port *port, struct serial_struct *ser) { int ret = 0; struct ifx_asc_port_priv *port_priv_data = port->private_data; if (ser->type != PORT_UNKNOWN && ser->type != PORT_IFX_ASC) ret = -EINVAL; if (ser->irq != port_priv_data->rir) ret = -EINVAL; if ( ser->baud_base < 9600 ) ret = -EINVAL; return ret; } static struct uart_ops uart_avm_ops = { .tx_empty = uart_avm_tx_empty, .get_mctrl = uart_avm_get_mctrl, .set_mctrl = uart_avm_set_mctrl, .stop_tx = uart_avm_stop_tx, .start_tx = uart_avm_start_tx, .stop_rx = uart_avm_stop_rx, .enable_ms = uart_avm_enable_ms, .break_ctl = uart_avm_break_ctl, .startup = uart_avm_startup, .shutdown = uart_avm_shutdown, .set_termios = uart_avm_set_termios, .type = uart_avm_type, .release_port = uart_avm_release_port, .request_port = uart_avm_request_port, .config_port = uart_avm_config_port, .verify_port = uart_avm_verify_port, }; static struct uart_port uart_avm_port[NR_UARTS]; static void uart_avm_setup_ports(void) { static int init_once = 0; int i; if ( init_once ){ return; } for (i = 0; i < NR_UARTS; i++) { struct ifx_asc_port_priv *port_priv_data = &ifx_asc_port_priv[i]; memset(&uart_avm_port[i], 0, sizeof(uart_avm_port[i])); port_priv_data->portwidth = (port_priv_data->base->asc_id & ASCID_TX32) ? 32 : 8; uart_avm_port[i].private_data = (void *) port_priv_data; uart_avm_port[i].iobase = (unsigned int)port_priv_data->base; uart_avm_port[i].membase = (unsigned char __iomem *)port_priv_data->base; uart_avm_port[i].mapbase = (unsigned long)port_priv_data->base; uart_avm_port[i].iotype = SERIAL_IO_MEM; uart_avm_port[i].irq = port_priv_data->rir; uart_avm_port[i].uartclk = ifx_get_fpi_hz(); uart_avm_port[i].fifosize = (port_priv_data->base->asc_id & ASCID_TXFS_MASK) >> ASCID_TXFS_OFF; uart_avm_port[i].type = PORT_IFX_ASC; uart_avm_port[i].ops = &uart_avm_ops; uart_avm_port[i].flags = ASYNC_BOOT_AUTOCONF; uart_avm_port[i].line = i; init_once ++; } } static void uart_avm_console_putchar(struct uart_port *port, int ch) { struct ifx_asc_port_priv *port_priv_data = port->private_data; ifx_asc_reg_t *asc_reg = port_priv_data->base; while ( ( asc_reg->asc_fstat & ASCFSTAT_TXFFLMASK) == (IFX_ASC_TXFIFO_FULL << ASCFSTAT_TXFFLOFF) ){ barrier(); } /* We have either portwidth = 8 or portwidth = 32 */ if ( port_priv_data->portwidth == 8 ) { asc_reg->asc_tbuf = (char)ch; } else { *(((char*)&asc_reg->asc_tbuf) + 3) = (char)ch; } asm("sync"); } #ifdef CONFIG_SERIAL_AVM_ASC_CONSOLE static void uart_avm_console_write(struct console *co, const char *s, unsigned int count) { uart_console_write(&uart_avm_port[CONSOLE_UART], s, count, uart_avm_console_putchar); } static int __init uart_avm_console_setup(struct console *co, char *options) { struct uart_port *port = &uart_avm_port[CONSOLE_UART]; int baud = 115200; int bits = 8; int parity = 'n'; int flow = 'n'; /* * Check whether an invalid uart number has been specified, and * if so, search for the first available port that does have * console support. */ if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); return uart_set_options(port, co, baud, parity, bits, flow); } static struct uart_driver uart_avm_reg; static struct console uart_avm_console = { .name = SERIAL_UART_AVM_NAME, .write = uart_avm_console_write, .device = uart_console_device, .setup = uart_avm_console_setup, .flags = CON_PRINTBUFFER, .index = -1, .data = &uart_avm_reg, }; static int __init rs_avm_console_init(void) { uart_avm_setup_ports(); register_console(&uart_avm_console); return 0; } console_initcall(rs_avm_console_init); #define UART_AVM_ASC_CONSOLE &uart_avm_console #else /*--- #ifdef CONFIG_SERIAL_AVM_ASC_CONSOLE ---*/ #define UART_AVM_ASC_CONSOLE NULL #endif /*--- #else ---*/ /*--- #ifdef CONFIG_SERIAL_AVM_ASC_CONSOLE ---*/ static struct uart_driver uart_avm_reg = { .owner = THIS_MODULE, .driver_name = "ttyS", .dev_name = "ttyS", .major = SERIAL_UART_AVM_MAJOR, .minor = SERIAL_UART_AVM_MINOR, .nr = NR_UARTS, .cons = UART_AVM_ASC_CONSOLE, }; static int __init uart_avm_init(void) { int ret; printk(KERN_INFO "Serial: SERIAL_AVM_ASC driver\n"); uart_avm_setup_ports(); ret = uart_register_driver(&uart_avm_reg); if (ret == 0){ int i; for ( i = 0; i < NR_UARTS; i++){ uart_add_one_port(&uart_avm_reg, &uart_avm_port[i]); } } return ret; } static void __exit uart_avm_exit(void) { int i; for ( i = 0; i < NR_UARTS; i++){ uart_remove_one_port(&uart_avm_reg, &uart_avm_port[i]); } uart_unregister_driver(&uart_avm_reg); } void get_fdv_and_reload_value(unsigned int baudrate, unsigned int *fdv, unsigned int *reload) { unsigned int fpi_clk = ifx_get_fpi_hz(); unsigned int baudrate1 = baudrate * 8192; unsigned long long baudrate2 = (unsigned long long)baudrate * 1000; unsigned long long fdv_over_bg_fpi; unsigned long long fdv_over_bg; unsigned long long difference; unsigned long long min_difference; unsigned int bg; /* Sanity check first */ if (baudrate >= (fpi_clk >> 4)) { printk(KERN_ERR "%s current fpi clock %u can't provide baudrate %u!!!\n", __func__, fpi_clk, baudrate); return; } // baudrate = fpiclk * (fdv / 512) / (16 * (bg + 1)) min_difference = UINT_MAX; fdv_over_bg_fpi = baudrate1; for ( bg = 1; bg <= 8192; bg++, fdv_over_bg_fpi += baudrate1 ) { fdv_over_bg = fdv_over_bg_fpi + fpi_clk / 2; do_div(fdv_over_bg, fpi_clk); if ( fdv_over_bg <= 512 ) { difference = fdv_over_bg * fpi_clk * 1000; do_div(difference, 8192 * bg); if ( difference < baudrate2 ) difference = baudrate2 - difference; else difference -= baudrate2; if ( difference < min_difference ) { *fdv = (unsigned int)fdv_over_bg & 511; *reload = bg - 1; min_difference = difference; } /* Perfect one found */ if (min_difference == 0) { break; } } } } EXPORT_SYMBOL(get_fdv_and_reload_value); /*------------------------------------------------------------------------------------------*\ * wird von cgu_set_clock benoetigt \*------------------------------------------------------------------------------------------*/ void ifx_update_asc_clock_settings(void) { ifx_asc_reg_t *asc_reg; u32 fdv, reload; u32 asc_state; int i; for ( i = 0; i < NUM_ENTITY(uart_avm_port); i++ ) { asc_reg = ifx_asc_port_priv[i].base; get_fdv_and_reload_value(ifx_asc_port_priv[i].baudrate, &fdv, &reload); asc_state = asc_reg->asc_state; /* disable receiver */ asc_reg->asc_whbstate = ASCWHBSTATE_CLRREN; /* now we can write the new baudrate into the register */ asc_reg->asc_fdv = fdv; asc_reg->asc_bg = reload; /* enable receiver if necessary */ if ( (asc_state & ASCSTATE_REN) ) asc_reg->asc_whbstate = ASCWHBSTATE_SETREN; } } EXPORT_SYMBOL(ifx_update_asc_clock_settings); module_init(uart_avm_init); module_exit(uart_avm_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("AVM Serial Driver for Infineon ASC Uart");