| /* MN10300 On-chip serial port UART driver | |
| * | |
| * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. | |
| * Written by David Howells (dhowells@redhat.com) | |
| * | |
| * This program is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU General Public Licence | |
| * as published by the Free Software Foundation; either version | |
| * 2 of the Licence, or (at your option) any later version. | |
| */ | |
| static const char serial_name[] = "MN10300 Serial driver"; | |
| static const char serial_version[] = "mn10300_serial-1.0"; | |
| static const char serial_revdate[] = "2007-11-06"; | |
| #if defined(CONFIG_MN10300_TTYSM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) | |
| #define SUPPORT_SYSRQ | |
| #endif | |
| #include <linux/module.h> | |
| #include <linux/serial.h> | |
| #include <linux/circ_buf.h> | |
| #include <linux/errno.h> | |
| #include <linux/signal.h> | |
| #include <linux/sched.h> | |
| #include <linux/timer.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/tty.h> | |
| #include <linux/tty_flip.h> | |
| #include <linux/major.h> | |
| #include <linux/string.h> | |
| #include <linux/ioport.h> | |
| #include <linux/mm.h> | |
| #include <linux/slab.h> | |
| #include <linux/init.h> | |
| #include <linux/console.h> | |
| #include <linux/sysrq.h> | |
| #include <asm/io.h> | |
| #include <asm/irq.h> | |
| #include <asm/bitops.h> | |
| #include <asm/serial-regs.h> | |
| #include <unit/timex.h> | |
| #include "mn10300-serial.h" | |
| #ifdef CONFIG_SMP | |
| #undef GxICR | |
| #define GxICR(X) CROSS_GxICR(X, 0) | |
| #endif /* CONFIG_SMP */ | |
| #define kenter(FMT, ...) \ | |
| printk(KERN_DEBUG "-->%s(" FMT ")\n", __func__, ##__VA_ARGS__) | |
| #define _enter(FMT, ...) \ | |
| no_printk(KERN_DEBUG "-->%s(" FMT ")\n", __func__, ##__VA_ARGS__) | |
| #define kdebug(FMT, ...) \ | |
| printk(KERN_DEBUG "--- " FMT "\n", ##__VA_ARGS__) | |
| #define _debug(FMT, ...) \ | |
| no_printk(KERN_DEBUG "--- " FMT "\n", ##__VA_ARGS__) | |
| #define kproto(FMT, ...) \ | |
| printk(KERN_DEBUG "### MNSERIAL " FMT " ###\n", ##__VA_ARGS__) | |
| #define _proto(FMT, ...) \ | |
| no_printk(KERN_DEBUG "### MNSERIAL " FMT " ###\n", ##__VA_ARGS__) | |
| #ifndef CODMSB | |
| /* c_cflag bit meaning */ | |
| #define CODMSB 004000000000 /* change Transfer bit-order */ | |
| #endif | |
| #define NR_UARTS 3 | |
| #ifdef CONFIG_MN10300_TTYSM_CONSOLE | |
| static void mn10300_serial_console_write(struct console *co, | |
| const char *s, unsigned count); | |
| static int __init mn10300_serial_console_setup(struct console *co, | |
| char *options); | |
| static struct uart_driver mn10300_serial_driver; | |
| static struct console mn10300_serial_console = { | |
| .name = "ttySM", | |
| .write = mn10300_serial_console_write, | |
| .device = uart_console_device, | |
| .setup = mn10300_serial_console_setup, | |
| .flags = CON_PRINTBUFFER, | |
| .index = -1, | |
| .data = &mn10300_serial_driver, | |
| }; | |
| #endif | |
| static struct uart_driver mn10300_serial_driver = { | |
| .owner = NULL, | |
| .driver_name = "mn10300-serial", | |
| .dev_name = "ttySM", | |
| .major = TTY_MAJOR, | |
| .minor = 128, | |
| .nr = NR_UARTS, | |
| #ifdef CONFIG_MN10300_TTYSM_CONSOLE | |
| .cons = &mn10300_serial_console, | |
| #endif | |
| }; | |
| static unsigned int mn10300_serial_tx_empty(struct uart_port *); | |
| static void mn10300_serial_set_mctrl(struct uart_port *, unsigned int mctrl); | |
| static unsigned int mn10300_serial_get_mctrl(struct uart_port *); | |
| static void mn10300_serial_stop_tx(struct uart_port *); | |
| static void mn10300_serial_start_tx(struct uart_port *); | |
| static void mn10300_serial_send_xchar(struct uart_port *, char ch); | |
| static void mn10300_serial_stop_rx(struct uart_port *); | |
| static void mn10300_serial_enable_ms(struct uart_port *); | |
| static void mn10300_serial_break_ctl(struct uart_port *, int ctl); | |
| static int mn10300_serial_startup(struct uart_port *); | |
| static void mn10300_serial_shutdown(struct uart_port *); | |
| static void mn10300_serial_set_termios(struct uart_port *, | |
| struct ktermios *new, | |
| struct ktermios *old); | |
| static const char *mn10300_serial_type(struct uart_port *); | |
| static void mn10300_serial_release_port(struct uart_port *); | |
| static int mn10300_serial_request_port(struct uart_port *); | |
| static void mn10300_serial_config_port(struct uart_port *, int); | |
| static int mn10300_serial_verify_port(struct uart_port *, | |
| struct serial_struct *); | |
| #ifdef CONFIG_CONSOLE_POLL | |
| static void mn10300_serial_poll_put_char(struct uart_port *, unsigned char); | |
| static int mn10300_serial_poll_get_char(struct uart_port *); | |
| #endif | |
| static const struct uart_ops mn10300_serial_ops = { | |
| .tx_empty = mn10300_serial_tx_empty, | |
| .set_mctrl = mn10300_serial_set_mctrl, | |
| .get_mctrl = mn10300_serial_get_mctrl, | |
| .stop_tx = mn10300_serial_stop_tx, | |
| .start_tx = mn10300_serial_start_tx, | |
| .send_xchar = mn10300_serial_send_xchar, | |
| .stop_rx = mn10300_serial_stop_rx, | |
| .enable_ms = mn10300_serial_enable_ms, | |
| .break_ctl = mn10300_serial_break_ctl, | |
| .startup = mn10300_serial_startup, | |
| .shutdown = mn10300_serial_shutdown, | |
| .set_termios = mn10300_serial_set_termios, | |
| .type = mn10300_serial_type, | |
| .release_port = mn10300_serial_release_port, | |
| .request_port = mn10300_serial_request_port, | |
| .config_port = mn10300_serial_config_port, | |
| .verify_port = mn10300_serial_verify_port, | |
| #ifdef CONFIG_CONSOLE_POLL | |
| .poll_put_char = mn10300_serial_poll_put_char, | |
| .poll_get_char = mn10300_serial_poll_get_char, | |
| #endif | |
| }; | |
| static irqreturn_t mn10300_serial_interrupt(int irq, void *dev_id); | |
| /* | |
| * the first on-chip serial port: ttySM0 (aka SIF0) | |
| */ | |
| #ifdef CONFIG_MN10300_TTYSM0 | |
| struct mn10300_serial_port mn10300_serial_port_sif0 = { | |
| .uart.ops = &mn10300_serial_ops, | |
| .uart.membase = (void __iomem *) &SC0CTR, | |
| .uart.mapbase = (unsigned long) &SC0CTR, | |
| .uart.iotype = UPIO_MEM, | |
| .uart.irq = 0, | |
| .uart.uartclk = 0, /* MN10300_IOCLK, */ | |
| .uart.fifosize = 1, | |
| .uart.flags = UPF_BOOT_AUTOCONF, | |
| .uart.line = 0, | |
| .uart.type = PORT_MN10300, | |
| .uart.lock = | |
| __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif0.uart.lock), | |
| .name = "ttySM0", | |
| ._iobase = &SC0CTR, | |
| ._control = &SC0CTR, | |
| ._status = (volatile u8 *)&SC0STR, | |
| ._intr = &SC0ICR, | |
| ._rxb = &SC0RXB, | |
| ._txb = &SC0TXB, | |
| .rx_name = "ttySM0:Rx", | |
| .tx_name = "ttySM0:Tx", | |
| #if defined(CONFIG_MN10300_TTYSM0_TIMER8) | |
| .tm_name = "ttySM0:Timer8", | |
| ._tmxmd = &TM8MD, | |
| ._tmxbr = &TM8BR, | |
| ._tmicr = &TM8ICR, | |
| .tm_irq = TM8IRQ, | |
| .div_timer = MNSCx_DIV_TIMER_16BIT, | |
| #elif defined(CONFIG_MN10300_TTYSM0_TIMER0) | |
| .tm_name = "ttySM0:Timer0", | |
| ._tmxmd = &TM0MD, | |
| ._tmxbr = (volatile u16 *)&TM0BR, | |
| ._tmicr = &TM0ICR, | |
| .tm_irq = TM0IRQ, | |
| .div_timer = MNSCx_DIV_TIMER_8BIT, | |
| #elif defined(CONFIG_MN10300_TTYSM0_TIMER2) | |
| .tm_name = "ttySM0:Timer2", | |
| ._tmxmd = &TM2MD, | |
| ._tmxbr = (volatile u16 *)&TM2BR, | |
| ._tmicr = &TM2ICR, | |
| .tm_irq = TM2IRQ, | |
| .div_timer = MNSCx_DIV_TIMER_8BIT, | |
| #else | |
| #error "Unknown config for ttySM0" | |
| #endif | |
| .rx_irq = SC0RXIRQ, | |
| .tx_irq = SC0TXIRQ, | |
| .rx_icr = &GxICR(SC0RXIRQ), | |
| .tx_icr = &GxICR(SC0TXIRQ), | |
| .clock_src = MNSCx_CLOCK_SRC_IOCLK, | |
| .options = 0, | |
| #ifdef CONFIG_GDBSTUB_ON_TTYSM0 | |
| .gdbstub = 1, | |
| #endif | |
| }; | |
| #endif /* CONFIG_MN10300_TTYSM0 */ | |
| /* | |
| * the second on-chip serial port: ttySM1 (aka SIF1) | |
| */ | |
| #ifdef CONFIG_MN10300_TTYSM1 | |
| struct mn10300_serial_port mn10300_serial_port_sif1 = { | |
| .uart.ops = &mn10300_serial_ops, | |
| .uart.membase = (void __iomem *) &SC1CTR, | |
| .uart.mapbase = (unsigned long) &SC1CTR, | |
| .uart.iotype = UPIO_MEM, | |
| .uart.irq = 0, | |
| .uart.uartclk = 0, /* MN10300_IOCLK, */ | |
| .uart.fifosize = 1, | |
| .uart.flags = UPF_BOOT_AUTOCONF, | |
| .uart.line = 1, | |
| .uart.type = PORT_MN10300, | |
| .uart.lock = | |
| __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif1.uart.lock), | |
| .name = "ttySM1", | |
| ._iobase = &SC1CTR, | |
| ._control = &SC1CTR, | |
| ._status = (volatile u8 *)&SC1STR, | |
| ._intr = &SC1ICR, | |
| ._rxb = &SC1RXB, | |
| ._txb = &SC1TXB, | |
| .rx_name = "ttySM1:Rx", | |
| .tx_name = "ttySM1:Tx", | |
| #if defined(CONFIG_MN10300_TTYSM1_TIMER9) | |
| .tm_name = "ttySM1:Timer9", | |
| ._tmxmd = &TM9MD, | |
| ._tmxbr = &TM9BR, | |
| ._tmicr = &TM9ICR, | |
| .tm_irq = TM9IRQ, | |
| .div_timer = MNSCx_DIV_TIMER_16BIT, | |
| #elif defined(CONFIG_MN10300_TTYSM1_TIMER3) | |
| .tm_name = "ttySM1:Timer3", | |
| ._tmxmd = &TM3MD, | |
| ._tmxbr = (volatile u16 *)&TM3BR, | |
| ._tmicr = &TM3ICR, | |
| .tm_irq = TM3IRQ, | |
| .div_timer = MNSCx_DIV_TIMER_8BIT, | |
| #elif defined(CONFIG_MN10300_TTYSM1_TIMER12) | |
| .tm_name = "ttySM1/Timer12", | |
| ._tmxmd = &TM12MD, | |
| ._tmxbr = &TM12BR, | |
| ._tmicr = &TM12ICR, | |
| .tm_irq = TM12IRQ, | |
| .div_timer = MNSCx_DIV_TIMER_16BIT, | |
| #else | |
| #error "Unknown config for ttySM1" | |
| #endif | |
| .rx_irq = SC1RXIRQ, | |
| .tx_irq = SC1TXIRQ, | |
| .rx_icr = &GxICR(SC1RXIRQ), | |
| .tx_icr = &GxICR(SC1TXIRQ), | |
| .clock_src = MNSCx_CLOCK_SRC_IOCLK, | |
| .options = 0, | |
| #ifdef CONFIG_GDBSTUB_ON_TTYSM1 | |
| .gdbstub = 1, | |
| #endif | |
| }; | |
| #endif /* CONFIG_MN10300_TTYSM1 */ | |
| /* | |
| * the third on-chip serial port: ttySM2 (aka SIF2) | |
| */ | |
| #ifdef CONFIG_MN10300_TTYSM2 | |
| struct mn10300_serial_port mn10300_serial_port_sif2 = { | |
| .uart.ops = &mn10300_serial_ops, | |
| .uart.membase = (void __iomem *) &SC2CTR, | |
| .uart.mapbase = (unsigned long) &SC2CTR, | |
| .uart.iotype = UPIO_MEM, | |
| .uart.irq = 0, | |
| .uart.uartclk = 0, /* MN10300_IOCLK, */ | |
| .uart.fifosize = 1, | |
| .uart.flags = UPF_BOOT_AUTOCONF, | |
| .uart.line = 2, | |
| #ifdef CONFIG_MN10300_TTYSM2_CTS | |
| .uart.type = PORT_MN10300_CTS, | |
| #else | |
| .uart.type = PORT_MN10300, | |
| #endif | |
| .uart.lock = | |
| __SPIN_LOCK_UNLOCKED(mn10300_serial_port_sif2.uart.lock), | |
| .name = "ttySM2", | |
| ._iobase = &SC2CTR, | |
| ._control = &SC2CTR, | |
| ._status = (volatile u8 *)&SC2STR, | |
| ._intr = &SC2ICR, | |
| ._rxb = &SC2RXB, | |
| ._txb = &SC2TXB, | |
| .rx_name = "ttySM2:Rx", | |
| .tx_name = "ttySM2:Tx", | |
| #if defined(CONFIG_MN10300_TTYSM2_TIMER10) | |
| .tm_name = "ttySM2/Timer10", | |
| ._tmxmd = &TM10MD, | |
| ._tmxbr = &TM10BR, | |
| ._tmicr = &TM10ICR, | |
| .tm_irq = TM10IRQ, | |
| .div_timer = MNSCx_DIV_TIMER_16BIT, | |
| #elif defined(CONFIG_MN10300_TTYSM2_TIMER9) | |
| .tm_name = "ttySM2/Timer9", | |
| ._tmxmd = &TM9MD, | |
| ._tmxbr = &TM9BR, | |
| ._tmicr = &TM9ICR, | |
| .tm_irq = TM9IRQ, | |
| .div_timer = MNSCx_DIV_TIMER_16BIT, | |
| #elif defined(CONFIG_MN10300_TTYSM2_TIMER1) | |
| .tm_name = "ttySM2/Timer1", | |
| ._tmxmd = &TM1MD, | |
| ._tmxbr = (volatile u16 *)&TM1BR, | |
| ._tmicr = &TM1ICR, | |
| .tm_irq = TM1IRQ, | |
| .div_timer = MNSCx_DIV_TIMER_8BIT, | |
| #elif defined(CONFIG_MN10300_TTYSM2_TIMER3) | |
| .tm_name = "ttySM2/Timer3", | |
| ._tmxmd = &TM3MD, | |
| ._tmxbr = (volatile u16 *)&TM3BR, | |
| ._tmicr = &TM3ICR, | |
| .tm_irq = TM3IRQ, | |
| .div_timer = MNSCx_DIV_TIMER_8BIT, | |
| #else | |
| #error "Unknown config for ttySM2" | |
| #endif | |
| .rx_irq = SC2RXIRQ, | |
| .tx_irq = SC2TXIRQ, | |
| .rx_icr = &GxICR(SC2RXIRQ), | |
| .tx_icr = &GxICR(SC2TXIRQ), | |
| .clock_src = MNSCx_CLOCK_SRC_IOCLK, | |
| #ifdef CONFIG_MN10300_TTYSM2_CTS | |
| .options = MNSCx_OPT_CTS, | |
| #else | |
| .options = 0, | |
| #endif | |
| #ifdef CONFIG_GDBSTUB_ON_TTYSM2 | |
| .gdbstub = 1, | |
| #endif | |
| }; | |
| #endif /* CONFIG_MN10300_TTYSM2 */ | |
| /* | |
| * list of available serial ports | |
| */ | |
| struct mn10300_serial_port *mn10300_serial_ports[NR_UARTS + 1] = { | |
| #ifdef CONFIG_MN10300_TTYSM0 | |
| [0] = &mn10300_serial_port_sif0, | |
| #endif | |
| #ifdef CONFIG_MN10300_TTYSM1 | |
| [1] = &mn10300_serial_port_sif1, | |
| #endif | |
| #ifdef CONFIG_MN10300_TTYSM2 | |
| [2] = &mn10300_serial_port_sif2, | |
| #endif | |
| [NR_UARTS] = NULL, | |
| }; | |
| /* | |
| * we abuse the serial ports' baud timers' interrupt lines to get the ability | |
| * to deliver interrupts to userspace as we use the ports' interrupt lines to | |
| * do virtual DMA on account of the ports having no hardware FIFOs | |
| * | |
| * we can generate an interrupt manually in the assembly stubs by writing to | |
| * the enable and detect bits in the interrupt control register, so all we need | |
| * to do here is disable the interrupt line | |
| * | |
| * note that we can't just leave the line enabled as the baud rate timer *also* | |
| * generates interrupts | |
| */ | |
| static void mn10300_serial_mask_ack(unsigned int irq) | |
| { | |
| unsigned long flags; | |
| u16 tmp; | |
| flags = arch_local_cli_save(); | |
| GxICR(irq) = GxICR_LEVEL_6; | |
| tmp = GxICR(irq); /* flush write buffer */ | |
| arch_local_irq_restore(flags); | |
| } | |
| static void mn10300_serial_chip_mask_ack(struct irq_data *d) | |
| { | |
| mn10300_serial_mask_ack(d->irq); | |
| } | |
| static void mn10300_serial_nop(struct irq_data *d) | |
| { | |
| } | |
| static struct irq_chip mn10300_serial_pic = { | |
| .name = "mnserial", | |
| .irq_ack = mn10300_serial_chip_mask_ack, | |
| .irq_mask = mn10300_serial_chip_mask_ack, | |
| .irq_mask_ack = mn10300_serial_chip_mask_ack, | |
| .irq_unmask = mn10300_serial_nop, | |
| }; | |
| static void mn10300_serial_low_mask(struct irq_data *d) | |
| { | |
| unsigned long flags; | |
| u16 tmp; | |
| flags = arch_local_cli_save(); | |
| GxICR(d->irq) = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); | |
| tmp = GxICR(d->irq); /* flush write buffer */ | |
| arch_local_irq_restore(flags); | |
| } | |
| static void mn10300_serial_low_unmask(struct irq_data *d) | |
| { | |
| unsigned long flags; | |
| u16 tmp; | |
| flags = arch_local_cli_save(); | |
| GxICR(d->irq) = | |
| NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) | GxICR_ENABLE; | |
| tmp = GxICR(d->irq); /* flush write buffer */ | |
| arch_local_irq_restore(flags); | |
| } | |
| static struct irq_chip mn10300_serial_low_pic = { | |
| .name = "mnserial-low", | |
| .irq_mask = mn10300_serial_low_mask, | |
| .irq_unmask = mn10300_serial_low_unmask, | |
| }; | |
| /* | |
| * serial virtual DMA interrupt jump table | |
| */ | |
| struct mn10300_serial_int mn10300_serial_int_tbl[NR_IRQS]; | |
| static void mn10300_serial_dis_tx_intr(struct mn10300_serial_port *port) | |
| { | |
| int retries = 100; | |
| u16 x; | |
| /* nothing to do if irq isn't set up */ | |
| if (!mn10300_serial_int_tbl[port->tx_irq].port) | |
| return; | |
| port->tx_flags |= MNSCx_TX_STOP; | |
| mb(); | |
| /* | |
| * Here we wait for the irq to be disabled. Either it already is | |
| * disabled or we wait some number of retries for the VDMA handler | |
| * to disable it. The retries give the VDMA handler enough time to | |
| * run to completion if it was already in progress. If the VDMA IRQ | |
| * is enabled but the handler is not yet running when arrive here, | |
| * the STOP flag will prevent the handler from conflicting with the | |
| * driver code following this loop. | |
| */ | |
| while ((*port->tx_icr & GxICR_ENABLE) && retries-- > 0) | |
| ; | |
| if (retries <= 0) { | |
| *port->tx_icr = | |
| NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); | |
| x = *port->tx_icr; | |
| } | |
| } | |
| static void mn10300_serial_en_tx_intr(struct mn10300_serial_port *port) | |
| { | |
| u16 x; | |
| /* nothing to do if irq isn't set up */ | |
| if (!mn10300_serial_int_tbl[port->tx_irq].port) | |
| return; | |
| /* stop vdma irq if not already stopped */ | |
| if (!(port->tx_flags & MNSCx_TX_STOP)) | |
| mn10300_serial_dis_tx_intr(port); | |
| port->tx_flags &= ~MNSCx_TX_STOP; | |
| mb(); | |
| *port->tx_icr = | |
| NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL) | | |
| GxICR_ENABLE | GxICR_REQUEST | GxICR_DETECT; | |
| x = *port->tx_icr; | |
| } | |
| static void mn10300_serial_dis_rx_intr(struct mn10300_serial_port *port) | |
| { | |
| unsigned long flags; | |
| u16 x; | |
| flags = arch_local_cli_save(); | |
| *port->rx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); | |
| x = *port->rx_icr; | |
| arch_local_irq_restore(flags); | |
| } | |
| /* | |
| * multi-bit equivalent of test_and_clear_bit() | |
| */ | |
| static int mask_test_and_clear(volatile u8 *ptr, u8 mask) | |
| { | |
| u32 epsw; | |
| asm volatile(" bclr %1,(%2) \n" | |
| " mov epsw,%0 \n" | |
| : "=d"(epsw) : "d"(mask), "a"(ptr) | |
| : "cc", "memory"); | |
| return !(epsw & EPSW_FLAG_Z); | |
| } | |
| /* | |
| * receive chars from the ring buffer for this serial port | |
| * - must do break detection here (not done in the UART) | |
| */ | |
| static void mn10300_serial_receive_interrupt(struct mn10300_serial_port *port) | |
| { | |
| struct uart_icount *icount = &port->uart.icount; | |
| struct tty_port *tport = &port->uart.state->port; | |
| unsigned ix; | |
| int count; | |
| u8 st, ch, push, status, overrun; | |
| _enter("%s", port->name); | |
| push = 0; | |
| count = CIRC_CNT(port->rx_inp, port->rx_outp, MNSC_BUFFER_SIZE); | |
| count = tty_buffer_request_room(tport, count); | |
| if (count == 0) { | |
| if (!tport->low_latency) | |
| tty_flip_buffer_push(tport); | |
| return; | |
| } | |
| try_again: | |
| /* pull chars out of the hat */ | |
| ix = ACCESS_ONCE(port->rx_outp); | |
| if (CIRC_CNT(port->rx_inp, ix, MNSC_BUFFER_SIZE) == 0) { | |
| if (push && !tport->low_latency) | |
| tty_flip_buffer_push(tport); | |
| return; | |
| } | |
| smp_read_barrier_depends(); | |
| ch = port->rx_buffer[ix++]; | |
| st = port->rx_buffer[ix++]; | |
| smp_mb(); | |
| port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1); | |
| port->uart.icount.rx++; | |
| st &= SC01STR_FEF | SC01STR_PEF | SC01STR_OEF; | |
| status = 0; | |
| overrun = 0; | |
| /* the UART doesn't detect BREAK, so we have to do that ourselves | |
| * - it starts as a framing error on a NUL character | |
| * - then we count another two NUL characters before issuing TTY_BREAK | |
| * - then we end on a normal char or one that has all the bottom bits | |
| * zero and the top bits set | |
| */ | |
| switch (port->rx_brk) { | |
| case 0: | |
| /* not breaking at the moment */ | |
| break; | |
| case 1: | |
| if (st & SC01STR_FEF && ch == 0) { | |
| port->rx_brk = 2; | |
| goto try_again; | |
| } | |
| goto not_break; | |
| case 2: | |
| if (st & SC01STR_FEF && ch == 0) { | |
| port->rx_brk = 3; | |
| _proto("Rx Break Detected"); | |
| icount->brk++; | |
| if (uart_handle_break(&port->uart)) | |
| goto ignore_char; | |
| status |= 1 << TTY_BREAK; | |
| goto insert; | |
| } | |
| goto not_break; | |
| default: | |
| if (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF)) | |
| goto try_again; /* still breaking */ | |
| port->rx_brk = 0; /* end of the break */ | |
| switch (ch) { | |
| case 0xFF: | |
| case 0xFE: | |
| case 0xFC: | |
| case 0xF8: | |
| case 0xF0: | |
| case 0xE0: | |
| case 0xC0: | |
| case 0x80: | |
| case 0x00: | |
| /* discard char at probable break end */ | |
| goto try_again; | |
| } | |
| break; | |
| } | |
| process_errors: | |
| /* handle framing error */ | |
| if (st & SC01STR_FEF) { | |
| if (ch == 0) { | |
| /* framing error with NUL char is probably a BREAK */ | |
| port->rx_brk = 1; | |
| goto try_again; | |
| } | |
| _proto("Rx Framing Error"); | |
| icount->frame++; | |
| status |= 1 << TTY_FRAME; | |
| } | |
| /* handle parity error */ | |
| if (st & SC01STR_PEF) { | |
| _proto("Rx Parity Error"); | |
| icount->parity++; | |
| status = TTY_PARITY; | |
| } | |
| /* handle normal char */ | |
| if (status == 0) { | |
| if (uart_handle_sysrq_char(&port->uart, ch)) | |
| goto ignore_char; | |
| status = (1 << TTY_NORMAL); | |
| } | |
| /* handle overrun error */ | |
| if (st & SC01STR_OEF) { | |
| if (port->rx_brk) | |
| goto try_again; | |
| _proto("Rx Overrun Error"); | |
| icount->overrun++; | |
| overrun = 1; | |
| } | |
| insert: | |
| status &= port->uart.read_status_mask; | |
| if (!overrun && !(status & port->uart.ignore_status_mask)) { | |
| int flag; | |
| if (status & (1 << TTY_BREAK)) | |
| flag = TTY_BREAK; | |
| else if (status & (1 << TTY_PARITY)) | |
| flag = TTY_PARITY; | |
| else if (status & (1 << TTY_FRAME)) | |
| flag = TTY_FRAME; | |
| else | |
| flag = TTY_NORMAL; | |
| tty_insert_flip_char(tport, ch, flag); | |
| } | |
| /* overrun is special, since it's reported immediately, and doesn't | |
| * affect the current character | |
| */ | |
| if (overrun) | |
| tty_insert_flip_char(tport, 0, TTY_OVERRUN); | |
| count--; | |
| if (count <= 0) { | |
| if (!tport->low_latency) | |
| tty_flip_buffer_push(tport); | |
| return; | |
| } | |
| ignore_char: | |
| push = 1; | |
| goto try_again; | |
| not_break: | |
| port->rx_brk = 0; | |
| goto process_errors; | |
| } | |
| /* | |
| * handle an interrupt from the serial transmission "virtual DMA" driver | |
| * - note: the interrupt routine will disable its own interrupts when the Tx | |
| * buffer is empty | |
| */ | |
| static void mn10300_serial_transmit_interrupt(struct mn10300_serial_port *port) | |
| { | |
| _enter("%s", port->name); | |
| if (!port->uart.state || !port->uart.state->port.tty) { | |
| mn10300_serial_dis_tx_intr(port); | |
| return; | |
| } | |
| if (uart_tx_stopped(&port->uart) || | |
| uart_circ_empty(&port->uart.state->xmit)) | |
| mn10300_serial_dis_tx_intr(port); | |
| if (uart_circ_chars_pending(&port->uart.state->xmit) < WAKEUP_CHARS) | |
| uart_write_wakeup(&port->uart); | |
| } | |
| /* | |
| * deal with a change in the status of the CTS line | |
| */ | |
| static void mn10300_serial_cts_changed(struct mn10300_serial_port *port, u8 st) | |
| { | |
| u16 ctr; | |
| port->tx_cts = st; | |
| port->uart.icount.cts++; | |
| /* flip the CTS state selector flag to interrupt when it changes | |
| * back */ | |
| ctr = *port->_control; | |
| ctr ^= SC2CTR_TWS; | |
| *port->_control = ctr; | |
| uart_handle_cts_change(&port->uart, st & SC2STR_CTS); | |
| wake_up_interruptible(&port->uart.state->port.delta_msr_wait); | |
| } | |
| /* | |
| * handle a virtual interrupt generated by the lower level "virtual DMA" | |
| * routines (irq is the baud timer interrupt) | |
| */ | |
| static irqreturn_t mn10300_serial_interrupt(int irq, void *dev_id) | |
| { | |
| struct mn10300_serial_port *port = dev_id; | |
| u8 st; | |
| spin_lock(&port->uart.lock); | |
| if (port->intr_flags) { | |
| _debug("INT %s: %x", port->name, port->intr_flags); | |
| if (mask_test_and_clear(&port->intr_flags, MNSCx_RX_AVAIL)) | |
| mn10300_serial_receive_interrupt(port); | |
| if (mask_test_and_clear(&port->intr_flags, | |
| MNSCx_TX_SPACE | MNSCx_TX_EMPTY)) | |
| mn10300_serial_transmit_interrupt(port); | |
| } | |
| /* the only modem control line amongst the whole lot is CTS on | |
| * serial port 2 */ | |
| if (port->type == PORT_MN10300_CTS) { | |
| st = *port->_status; | |
| if ((port->tx_cts ^ st) & SC2STR_CTS) | |
| mn10300_serial_cts_changed(port, st); | |
| } | |
| spin_unlock(&port->uart.lock); | |
| return IRQ_HANDLED; | |
| } | |
| /* | |
| * return indication of whether the hardware transmit buffer is empty | |
| */ | |
| static unsigned int mn10300_serial_tx_empty(struct uart_port *_port) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| _enter("%s", port->name); | |
| return (*port->_status & (SC01STR_TXF | SC01STR_TBF)) ? | |
| 0 : TIOCSER_TEMT; | |
| } | |
| /* | |
| * set the modem control lines (we don't have any) | |
| */ | |
| static void mn10300_serial_set_mctrl(struct uart_port *_port, | |
| unsigned int mctrl) | |
| { | |
| struct mn10300_serial_port *port __attribute__ ((unused)) = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| _enter("%s,%x", port->name, mctrl); | |
| } | |
| /* | |
| * get the modem control line statuses | |
| */ | |
| static unsigned int mn10300_serial_get_mctrl(struct uart_port *_port) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| _enter("%s", port->name); | |
| if (port->type == PORT_MN10300_CTS && !(*port->_status & SC2STR_CTS)) | |
| return TIOCM_CAR | TIOCM_DSR; | |
| return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR; | |
| } | |
| /* | |
| * stop transmitting characters | |
| */ | |
| static void mn10300_serial_stop_tx(struct uart_port *_port) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| _enter("%s", port->name); | |
| /* disable the virtual DMA */ | |
| mn10300_serial_dis_tx_intr(port); | |
| } | |
| /* | |
| * start transmitting characters | |
| * - jump-start transmission if it has stalled | |
| * - enable the serial Tx interrupt (used by the virtual DMA controller) | |
| * - force an interrupt to happen if necessary | |
| */ | |
| static void mn10300_serial_start_tx(struct uart_port *_port) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| _enter("%s{%lu}", | |
| port->name, | |
| CIRC_CNT(&port->uart.state->xmit.head, | |
| &port->uart.state->xmit.tail, | |
| UART_XMIT_SIZE)); | |
| /* kick the virtual DMA controller */ | |
| mn10300_serial_en_tx_intr(port); | |
| _debug("CTR=%04hx ICR=%02hx STR=%04x TMD=%02hx TBR=%04hx ICR=%04hx", | |
| *port->_control, *port->_intr, *port->_status, | |
| *port->_tmxmd, | |
| (port->div_timer == MNSCx_DIV_TIMER_8BIT) ? | |
| *(volatile u8 *)port->_tmxbr : *port->_tmxbr, | |
| *port->tx_icr); | |
| } | |
| /* | |
| * transmit a high-priority XON/XOFF character | |
| */ | |
| static void mn10300_serial_send_xchar(struct uart_port *_port, char ch) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| unsigned long flags; | |
| _enter("%s,%02x", port->name, ch); | |
| if (likely(port->gdbstub)) { | |
| port->tx_xchar = ch; | |
| if (ch) { | |
| spin_lock_irqsave(&port->uart.lock, flags); | |
| mn10300_serial_en_tx_intr(port); | |
| spin_unlock_irqrestore(&port->uart.lock, flags); | |
| } | |
| } | |
| } | |
| /* | |
| * stop receiving characters | |
| * - called whilst the port is being closed | |
| */ | |
| static void mn10300_serial_stop_rx(struct uart_port *_port) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| u16 ctr; | |
| _enter("%s", port->name); | |
| ctr = *port->_control; | |
| ctr &= ~SC01CTR_RXE; | |
| *port->_control = ctr; | |
| mn10300_serial_dis_rx_intr(port); | |
| } | |
| /* | |
| * enable modem status interrupts | |
| */ | |
| static void mn10300_serial_enable_ms(struct uart_port *_port) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| u16 ctr, cts; | |
| _enter("%s", port->name); | |
| if (port->type == PORT_MN10300_CTS) { | |
| /* want to interrupt when CTS goes low if CTS is now high and | |
| * vice versa | |
| */ | |
| port->tx_cts = *port->_status; | |
| cts = (port->tx_cts & SC2STR_CTS) ? | |
| SC2CTR_TWE : SC2CTR_TWE | SC2CTR_TWS; | |
| ctr = *port->_control; | |
| ctr &= ~SC2CTR_TWS; | |
| ctr |= cts; | |
| *port->_control = ctr; | |
| mn10300_serial_en_tx_intr(port); | |
| } | |
| } | |
| /* | |
| * transmit or cease transmitting a break signal | |
| */ | |
| static void mn10300_serial_break_ctl(struct uart_port *_port, int ctl) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| unsigned long flags; | |
| _enter("%s,%d", port->name, ctl); | |
| spin_lock_irqsave(&port->uart.lock, flags); | |
| if (ctl) { | |
| /* tell the virtual DMA handler to assert BREAK */ | |
| port->tx_flags |= MNSCx_TX_BREAK; | |
| mn10300_serial_en_tx_intr(port); | |
| } else { | |
| port->tx_flags &= ~MNSCx_TX_BREAK; | |
| *port->_control &= ~SC01CTR_BKE; | |
| mn10300_serial_en_tx_intr(port); | |
| } | |
| spin_unlock_irqrestore(&port->uart.lock, flags); | |
| } | |
| /* | |
| * grab the interrupts and enable the port for reception | |
| */ | |
| static int mn10300_serial_startup(struct uart_port *_port) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| struct mn10300_serial_int *pint; | |
| _enter("%s{%d}", port->name, port->gdbstub); | |
| if (unlikely(port->gdbstub)) | |
| return -EBUSY; | |
| /* allocate an Rx buffer for the virtual DMA handler */ | |
| port->rx_buffer = kmalloc(MNSC_BUFFER_SIZE, GFP_KERNEL); | |
| if (!port->rx_buffer) | |
| return -ENOMEM; | |
| port->rx_inp = port->rx_outp = 0; | |
| port->tx_flags = 0; | |
| /* finally, enable the device */ | |
| *port->_intr = SC01ICR_TI; | |
| *port->_control |= SC01CTR_TXE | SC01CTR_RXE; | |
| pint = &mn10300_serial_int_tbl[port->rx_irq]; | |
| pint->port = port; | |
| pint->vdma = mn10300_serial_vdma_rx_handler; | |
| pint = &mn10300_serial_int_tbl[port->tx_irq]; | |
| pint->port = port; | |
| pint->vdma = mn10300_serial_vdma_tx_handler; | |
| irq_set_chip(port->rx_irq, &mn10300_serial_low_pic); | |
| irq_set_chip(port->tx_irq, &mn10300_serial_low_pic); | |
| irq_set_chip(port->tm_irq, &mn10300_serial_pic); | |
| if (request_irq(port->rx_irq, mn10300_serial_interrupt, | |
| IRQF_NOBALANCING, | |
| port->rx_name, port) < 0) | |
| goto error; | |
| if (request_irq(port->tx_irq, mn10300_serial_interrupt, | |
| IRQF_NOBALANCING, | |
| port->tx_name, port) < 0) | |
| goto error2; | |
| if (request_irq(port->tm_irq, mn10300_serial_interrupt, | |
| IRQF_NOBALANCING, | |
| port->tm_name, port) < 0) | |
| goto error3; | |
| mn10300_serial_mask_ack(port->tm_irq); | |
| return 0; | |
| error3: | |
| free_irq(port->tx_irq, port); | |
| error2: | |
| free_irq(port->rx_irq, port); | |
| error: | |
| kfree(port->rx_buffer); | |
| port->rx_buffer = NULL; | |
| return -EBUSY; | |
| } | |
| /* | |
| * shutdown the port and release interrupts | |
| */ | |
| static void mn10300_serial_shutdown(struct uart_port *_port) | |
| { | |
| unsigned long flags; | |
| u16 x; | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| _enter("%s", port->name); | |
| spin_lock_irqsave(&_port->lock, flags); | |
| mn10300_serial_dis_tx_intr(port); | |
| *port->rx_icr = NUM2GxICR_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL); | |
| x = *port->rx_icr; | |
| port->tx_flags = 0; | |
| spin_unlock_irqrestore(&_port->lock, flags); | |
| /* disable the serial port and its baud rate timer */ | |
| *port->_control &= ~(SC01CTR_TXE | SC01CTR_RXE | SC01CTR_BKE); | |
| *port->_tmxmd = 0; | |
| if (port->rx_buffer) { | |
| void *buf = port->rx_buffer; | |
| port->rx_buffer = NULL; | |
| kfree(buf); | |
| } | |
| /* disable all intrs */ | |
| free_irq(port->tm_irq, port); | |
| free_irq(port->rx_irq, port); | |
| free_irq(port->tx_irq, port); | |
| mn10300_serial_int_tbl[port->tx_irq].port = NULL; | |
| mn10300_serial_int_tbl[port->rx_irq].port = NULL; | |
| } | |
| /* | |
| * this routine is called to set the UART divisor registers to match the | |
| * specified baud rate for a serial port. | |
| */ | |
| static void mn10300_serial_change_speed(struct mn10300_serial_port *port, | |
| struct ktermios *new, | |
| struct ktermios *old) | |
| { | |
| unsigned long flags; | |
| unsigned long ioclk = port->ioclk; | |
| unsigned cflag; | |
| int baud, bits, xdiv, tmp; | |
| u16 tmxbr, scxctr; | |
| u8 tmxmd, battempt; | |
| u8 div_timer = port->div_timer; | |
| _enter("%s{%lu}", port->name, ioclk); | |
| /* byte size and parity */ | |
| cflag = new->c_cflag; | |
| switch (cflag & CSIZE) { | |
| case CS7: scxctr = SC01CTR_CLN_7BIT; bits = 9; break; | |
| case CS8: scxctr = SC01CTR_CLN_8BIT; bits = 10; break; | |
| default: scxctr = SC01CTR_CLN_8BIT; bits = 10; break; | |
| } | |
| if (cflag & CSTOPB) { | |
| scxctr |= SC01CTR_STB_2BIT; | |
| bits++; | |
| } | |
| if (cflag & PARENB) { | |
| bits++; | |
| if (cflag & PARODD) | |
| scxctr |= SC01CTR_PB_ODD; | |
| #ifdef CMSPAR | |
| else if (cflag & CMSPAR) | |
| scxctr |= SC01CTR_PB_FIXED0; | |
| #endif | |
| else | |
| scxctr |= SC01CTR_PB_EVEN; | |
| } | |
| /* Determine divisor based on baud rate */ | |
| battempt = 0; | |
| switch (port->uart.line) { | |
| #ifdef CONFIG_MN10300_TTYSM0 | |
| case 0: /* ttySM0 */ | |
| #if defined(CONFIG_MN10300_TTYSM0_TIMER8) | |
| scxctr |= SC0CTR_CK_TM8UFLOW_8; | |
| #elif defined(CONFIG_MN10300_TTYSM0_TIMER0) | |
| scxctr |= SC0CTR_CK_TM0UFLOW_8; | |
| #elif defined(CONFIG_MN10300_TTYSM0_TIMER2) | |
| scxctr |= SC0CTR_CK_TM2UFLOW_8; | |
| #else | |
| #error "Unknown config for ttySM0" | |
| #endif | |
| break; | |
| #endif /* CONFIG_MN10300_TTYSM0 */ | |
| #ifdef CONFIG_MN10300_TTYSM1 | |
| case 1: /* ttySM1 */ | |
| #if defined(CONFIG_AM33_2) || defined(CONFIG_AM33_3) | |
| #if defined(CONFIG_MN10300_TTYSM1_TIMER9) | |
| scxctr |= SC1CTR_CK_TM9UFLOW_8; | |
| #elif defined(CONFIG_MN10300_TTYSM1_TIMER3) | |
| scxctr |= SC1CTR_CK_TM3UFLOW_8; | |
| #else | |
| #error "Unknown config for ttySM1" | |
| #endif | |
| #else /* CONFIG_AM33_2 || CONFIG_AM33_3 */ | |
| #if defined(CONFIG_MN10300_TTYSM1_TIMER12) | |
| scxctr |= SC1CTR_CK_TM12UFLOW_8; | |
| #else | |
| #error "Unknown config for ttySM1" | |
| #endif | |
| #endif /* CONFIG_AM33_2 || CONFIG_AM33_3 */ | |
| break; | |
| #endif /* CONFIG_MN10300_TTYSM1 */ | |
| #ifdef CONFIG_MN10300_TTYSM2 | |
| case 2: /* ttySM2 */ | |
| #if defined(CONFIG_AM33_2) | |
| #if defined(CONFIG_MN10300_TTYSM2_TIMER10) | |
| scxctr |= SC2CTR_CK_TM10UFLOW; | |
| #else | |
| #error "Unknown config for ttySM2" | |
| #endif | |
| #else /* CONFIG_AM33_2 */ | |
| #if defined(CONFIG_MN10300_TTYSM2_TIMER9) | |
| scxctr |= SC2CTR_CK_TM9UFLOW_8; | |
| #elif defined(CONFIG_MN10300_TTYSM2_TIMER1) | |
| scxctr |= SC2CTR_CK_TM1UFLOW_8; | |
| #elif defined(CONFIG_MN10300_TTYSM2_TIMER3) | |
| scxctr |= SC2CTR_CK_TM3UFLOW_8; | |
| #else | |
| #error "Unknown config for ttySM2" | |
| #endif | |
| #endif /* CONFIG_AM33_2 */ | |
| break; | |
| #endif /* CONFIG_MN10300_TTYSM2 */ | |
| default: | |
| break; | |
| } | |
| try_alternative: | |
| baud = uart_get_baud_rate(&port->uart, new, old, 0, | |
| port->ioclk / 8); | |
| _debug("ALT %d [baud %d]", battempt, baud); | |
| if (!baud) | |
| baud = 9600; /* B0 transition handled in rs_set_termios */ | |
| xdiv = 1; | |
| if (baud == 134) { | |
| baud = 269; /* 134 is really 134.5 */ | |
| xdiv = 2; | |
| } | |
| if (baud == 38400 && | |
| (port->uart.flags & UPF_SPD_MASK) == UPF_SPD_CUST | |
| ) { | |
| _debug("CUSTOM %u", port->uart.custom_divisor); | |
| if (div_timer == MNSCx_DIV_TIMER_16BIT) { | |
| if (port->uart.custom_divisor <= 65535) { | |
| tmxmd = TM8MD_SRC_IOCLK; | |
| tmxbr = port->uart.custom_divisor; | |
| port->uart.uartclk = ioclk; | |
| goto timer_okay; | |
| } | |
| if (port->uart.custom_divisor / 8 <= 65535) { | |
| tmxmd = TM8MD_SRC_IOCLK_8; | |
| tmxbr = port->uart.custom_divisor / 8; | |
| port->uart.custom_divisor = tmxbr * 8; | |
| port->uart.uartclk = ioclk / 8; | |
| goto timer_okay; | |
| } | |
| if (port->uart.custom_divisor / 32 <= 65535) { | |
| tmxmd = TM8MD_SRC_IOCLK_32; | |
| tmxbr = port->uart.custom_divisor / 32; | |
| port->uart.custom_divisor = tmxbr * 32; | |
| port->uart.uartclk = ioclk / 32; | |
| goto timer_okay; | |
| } | |
| } else if (div_timer == MNSCx_DIV_TIMER_8BIT) { | |
| if (port->uart.custom_divisor <= 255) { | |
| tmxmd = TM2MD_SRC_IOCLK; | |
| tmxbr = port->uart.custom_divisor; | |
| port->uart.uartclk = ioclk; | |
| goto timer_okay; | |
| } | |
| if (port->uart.custom_divisor / 8 <= 255) { | |
| tmxmd = TM2MD_SRC_IOCLK_8; | |
| tmxbr = port->uart.custom_divisor / 8; | |
| port->uart.custom_divisor = tmxbr * 8; | |
| port->uart.uartclk = ioclk / 8; | |
| goto timer_okay; | |
| } | |
| if (port->uart.custom_divisor / 32 <= 255) { | |
| tmxmd = TM2MD_SRC_IOCLK_32; | |
| tmxbr = port->uart.custom_divisor / 32; | |
| port->uart.custom_divisor = tmxbr * 32; | |
| port->uart.uartclk = ioclk / 32; | |
| goto timer_okay; | |
| } | |
| } | |
| } | |
| switch (div_timer) { | |
| case MNSCx_DIV_TIMER_16BIT: | |
| port->uart.uartclk = ioclk; | |
| tmxmd = TM8MD_SRC_IOCLK; | |
| tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; | |
| if (tmp > 0 && tmp <= 65535) | |
| goto timer_okay; | |
| port->uart.uartclk = ioclk / 8; | |
| tmxmd = TM8MD_SRC_IOCLK_8; | |
| tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; | |
| if (tmp > 0 && tmp <= 65535) | |
| goto timer_okay; | |
| port->uart.uartclk = ioclk / 32; | |
| tmxmd = TM8MD_SRC_IOCLK_32; | |
| tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; | |
| if (tmp > 0 && tmp <= 65535) | |
| goto timer_okay; | |
| break; | |
| case MNSCx_DIV_TIMER_8BIT: | |
| port->uart.uartclk = ioclk; | |
| tmxmd = TM2MD_SRC_IOCLK; | |
| tmxbr = tmp = (ioclk / (baud * xdiv) + 4) / 8 - 1; | |
| if (tmp > 0 && tmp <= 255) | |
| goto timer_okay; | |
| port->uart.uartclk = ioclk / 8; | |
| tmxmd = TM2MD_SRC_IOCLK_8; | |
| tmxbr = tmp = (ioclk / (baud * 8 * xdiv) + 4) / 8 - 1; | |
| if (tmp > 0 && tmp <= 255) | |
| goto timer_okay; | |
| port->uart.uartclk = ioclk / 32; | |
| tmxmd = TM2MD_SRC_IOCLK_32; | |
| tmxbr = tmp = (ioclk / (baud * 32 * xdiv) + 4) / 8 - 1; | |
| if (tmp > 0 && tmp <= 255) | |
| goto timer_okay; | |
| break; | |
| default: | |
| BUG(); | |
| return; | |
| } | |
| /* refuse to change to a baud rate we can't support */ | |
| _debug("CAN'T SUPPORT"); | |
| switch (battempt) { | |
| case 0: | |
| if (old) { | |
| new->c_cflag &= ~CBAUD; | |
| new->c_cflag |= (old->c_cflag & CBAUD); | |
| battempt = 1; | |
| goto try_alternative; | |
| } | |
| case 1: | |
| /* as a last resort, if the quotient is zero, default to 9600 | |
| * bps */ | |
| new->c_cflag &= ~CBAUD; | |
| new->c_cflag |= B9600; | |
| battempt = 2; | |
| goto try_alternative; | |
| default: | |
| /* hmmm... can't seem to support 9600 either | |
| * - we could try iterating through the speeds we know about to | |
| * find the lowest | |
| */ | |
| new->c_cflag &= ~CBAUD; | |
| new->c_cflag |= B0; | |
| if (div_timer == MNSCx_DIV_TIMER_16BIT) | |
| tmxmd = TM8MD_SRC_IOCLK_32; | |
| else if (div_timer == MNSCx_DIV_TIMER_8BIT) | |
| tmxmd = TM2MD_SRC_IOCLK_32; | |
| tmxbr = 1; | |
| port->uart.uartclk = ioclk / 32; | |
| break; | |
| } | |
| timer_okay: | |
| _debug("UARTCLK: %u / %hu", port->uart.uartclk, tmxbr); | |
| /* make the changes */ | |
| spin_lock_irqsave(&port->uart.lock, flags); | |
| uart_update_timeout(&port->uart, new->c_cflag, baud); | |
| /* set the timer to produce the required baud rate */ | |
| switch (div_timer) { | |
| case MNSCx_DIV_TIMER_16BIT: | |
| *port->_tmxmd = 0; | |
| *port->_tmxbr = tmxbr; | |
| *port->_tmxmd = TM8MD_INIT_COUNTER; | |
| *port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE; | |
| break; | |
| case MNSCx_DIV_TIMER_8BIT: | |
| *port->_tmxmd = 0; | |
| *(volatile u8 *) port->_tmxbr = (u8) tmxbr; | |
| *port->_tmxmd = TM2MD_INIT_COUNTER; | |
| *port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE; | |
| break; | |
| } | |
| /* CTS flow control flag and modem status interrupts */ | |
| scxctr &= ~(SC2CTR_TWE | SC2CTR_TWS); | |
| if (port->type == PORT_MN10300_CTS && cflag & CRTSCTS) { | |
| /* want to interrupt when CTS goes low if CTS is now | |
| * high and vice versa | |
| */ | |
| port->tx_cts = *port->_status; | |
| if (port->tx_cts & SC2STR_CTS) | |
| scxctr |= SC2CTR_TWE; | |
| else | |
| scxctr |= SC2CTR_TWE | SC2CTR_TWS; | |
| } | |
| /* set up parity check flag */ | |
| port->uart.read_status_mask = (1 << TTY_NORMAL) | (1 << TTY_OVERRUN); | |
| if (new->c_iflag & INPCK) | |
| port->uart.read_status_mask |= | |
| (1 << TTY_PARITY) | (1 << TTY_FRAME); | |
| if (new->c_iflag & (BRKINT | PARMRK)) | |
| port->uart.read_status_mask |= (1 << TTY_BREAK); | |
| /* characters to ignore */ | |
| port->uart.ignore_status_mask = 0; | |
| if (new->c_iflag & IGNPAR) | |
| port->uart.ignore_status_mask |= | |
| (1 << TTY_PARITY) | (1 << TTY_FRAME); | |
| if (new->c_iflag & IGNBRK) { | |
| port->uart.ignore_status_mask |= (1 << TTY_BREAK); | |
| /* | |
| * If we're ignoring parity and break indicators, | |
| * ignore overruns to (for real raw support). | |
| */ | |
| if (new->c_iflag & IGNPAR) | |
| port->uart.ignore_status_mask |= (1 << TTY_OVERRUN); | |
| } | |
| /* Ignore all characters if CREAD is not set */ | |
| if ((new->c_cflag & CREAD) == 0) | |
| port->uart.ignore_status_mask |= (1 << TTY_NORMAL); | |
| scxctr |= SC01CTR_TXE | SC01CTR_RXE; | |
| scxctr |= *port->_control & SC01CTR_BKE; | |
| *port->_control = scxctr; | |
| spin_unlock_irqrestore(&port->uart.lock, flags); | |
| } | |
| /* | |
| * set the terminal I/O parameters | |
| */ | |
| static void mn10300_serial_set_termios(struct uart_port *_port, | |
| struct ktermios *new, | |
| struct ktermios *old) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| _enter("%s,%p,%p", port->name, new, old); | |
| mn10300_serial_change_speed(port, new, old); | |
| /* handle turning off CRTSCTS */ | |
| if (!(new->c_cflag & CRTSCTS)) { | |
| u16 ctr = *port->_control; | |
| ctr &= ~SC2CTR_TWE; | |
| *port->_control = ctr; | |
| } | |
| /* change Transfer bit-order (LSB/MSB) */ | |
| if (new->c_cflag & CODMSB) | |
| *port->_control |= SC01CTR_OD_MSBFIRST; /* MSB MODE */ | |
| else | |
| *port->_control &= ~SC01CTR_OD_MSBFIRST; /* LSB MODE */ | |
| } | |
| /* | |
| * return description of port type | |
| */ | |
| static const char *mn10300_serial_type(struct uart_port *_port) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| if (port->uart.type == PORT_MN10300_CTS) | |
| return "MN10300 SIF_CTS"; | |
| return "MN10300 SIF"; | |
| } | |
| /* | |
| * release I/O and memory regions in use by port | |
| */ | |
| static void mn10300_serial_release_port(struct uart_port *_port) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| _enter("%s", port->name); | |
| release_mem_region((unsigned long) port->_iobase, 16); | |
| } | |
| /* | |
| * request I/O and memory regions for port | |
| */ | |
| static int mn10300_serial_request_port(struct uart_port *_port) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| _enter("%s", port->name); | |
| request_mem_region((unsigned long) port->_iobase, 16, port->name); | |
| return 0; | |
| } | |
| /* | |
| * configure the type and reserve the ports | |
| */ | |
| static void mn10300_serial_config_port(struct uart_port *_port, int type) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| _enter("%s", port->name); | |
| port->uart.type = PORT_MN10300; | |
| if (port->options & MNSCx_OPT_CTS) | |
| port->uart.type = PORT_MN10300_CTS; | |
| mn10300_serial_request_port(_port); | |
| } | |
| /* | |
| * verify serial parameters are suitable for this port type | |
| */ | |
| static int mn10300_serial_verify_port(struct uart_port *_port, | |
| struct serial_struct *ss) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| void *mapbase = (void *) (unsigned long) port->uart.mapbase; | |
| _enter("%s", port->name); | |
| /* these things may not be changed */ | |
| if (ss->irq != port->uart.irq || | |
| ss->port != port->uart.iobase || | |
| ss->io_type != port->uart.iotype || | |
| ss->iomem_base != mapbase || | |
| ss->iomem_reg_shift != port->uart.regshift || | |
| ss->hub6 != port->uart.hub6 || | |
| ss->xmit_fifo_size != port->uart.fifosize) | |
| return -EINVAL; | |
| /* type may be changed on a port that supports CTS */ | |
| if (ss->type != port->uart.type) { | |
| if (!(port->options & MNSCx_OPT_CTS)) | |
| return -EINVAL; | |
| if (ss->type != PORT_MN10300 && | |
| ss->type != PORT_MN10300_CTS) | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| /* | |
| * initialise the MN10300 on-chip UARTs | |
| */ | |
| static int __init mn10300_serial_init(void) | |
| { | |
| struct mn10300_serial_port *port; | |
| int ret, i; | |
| printk(KERN_INFO "%s version %s (%s)\n", | |
| serial_name, serial_version, serial_revdate); | |
| #if defined(CONFIG_MN10300_TTYSM2) && defined(CONFIG_AM33_2) | |
| { | |
| int tmp; | |
| SC2TIM = 8; /* make the baud base of timer 2 IOCLK/8 */ | |
| tmp = SC2TIM; | |
| } | |
| #endif | |
| set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_MN10300_SERIAL_IRQ_LEVEL), | |
| mn10300_serial_vdma_interrupt); | |
| ret = uart_register_driver(&mn10300_serial_driver); | |
| if (!ret) { | |
| for (i = 0 ; i < NR_PORTS ; i++) { | |
| port = mn10300_serial_ports[i]; | |
| if (!port || port->gdbstub) | |
| continue; | |
| switch (port->clock_src) { | |
| case MNSCx_CLOCK_SRC_IOCLK: | |
| port->ioclk = MN10300_IOCLK; | |
| break; | |
| #ifdef MN10300_IOBCLK | |
| case MNSCx_CLOCK_SRC_IOBCLK: | |
| port->ioclk = MN10300_IOBCLK; | |
| break; | |
| #endif | |
| default: | |
| BUG(); | |
| } | |
| ret = uart_add_one_port(&mn10300_serial_driver, | |
| &port->uart); | |
| if (ret < 0) { | |
| _debug("ERROR %d", -ret); | |
| break; | |
| } | |
| } | |
| if (ret) | |
| uart_unregister_driver(&mn10300_serial_driver); | |
| } | |
| return ret; | |
| } | |
| __initcall(mn10300_serial_init); | |
| #ifdef CONFIG_MN10300_TTYSM_CONSOLE | |
| /* | |
| * print a string to the serial port without disturbing the real user of the | |
| * port too much | |
| * - the console must be locked by the caller | |
| */ | |
| static void mn10300_serial_console_write(struct console *co, | |
| const char *s, unsigned count) | |
| { | |
| struct mn10300_serial_port *port; | |
| unsigned i; | |
| u16 scxctr; | |
| u8 tmxmd; | |
| unsigned long flags; | |
| int locked = 1; | |
| port = mn10300_serial_ports[co->index]; | |
| local_irq_save(flags); | |
| if (port->uart.sysrq) { | |
| /* mn10300_serial_interrupt() already took the lock */ | |
| locked = 0; | |
| } else if (oops_in_progress) { | |
| locked = spin_trylock(&port->uart.lock); | |
| } else | |
| spin_lock(&port->uart.lock); | |
| /* firstly hijack the serial port from the "virtual DMA" controller */ | |
| mn10300_serial_dis_tx_intr(port); | |
| /* the transmitter may be disabled */ | |
| scxctr = *port->_control; | |
| if (!(scxctr & SC01CTR_TXE)) { | |
| /* restart the UART clock */ | |
| tmxmd = *port->_tmxmd; | |
| switch (port->div_timer) { | |
| case MNSCx_DIV_TIMER_16BIT: | |
| *port->_tmxmd = 0; | |
| *port->_tmxmd = TM8MD_INIT_COUNTER; | |
| *port->_tmxmd = tmxmd | TM8MD_COUNT_ENABLE; | |
| break; | |
| case MNSCx_DIV_TIMER_8BIT: | |
| *port->_tmxmd = 0; | |
| *port->_tmxmd = TM2MD_INIT_COUNTER; | |
| *port->_tmxmd = tmxmd | TM2MD_COUNT_ENABLE; | |
| break; | |
| } | |
| /* enable the transmitter */ | |
| *port->_control = (scxctr & ~SC01CTR_BKE) | SC01CTR_TXE; | |
| } else if (scxctr & SC01CTR_BKE) { | |
| /* stop transmitting BREAK */ | |
| *port->_control = (scxctr & ~SC01CTR_BKE); | |
| } | |
| /* send the chars into the serial port (with LF -> LFCR conversion) */ | |
| for (i = 0; i < count; i++) { | |
| char ch = *s++; | |
| while (*port->_status & SC01STR_TBF) | |
| continue; | |
| *port->_txb = ch; | |
| if (ch == 0x0a) { | |
| while (*port->_status & SC01STR_TBF) | |
| continue; | |
| *port->_txb = 0xd; | |
| } | |
| } | |
| /* can't let the transmitter be turned off if it's actually | |
| * transmitting */ | |
| while (*port->_status & (SC01STR_TXF | SC01STR_TBF)) | |
| continue; | |
| /* disable the transmitter if we re-enabled it */ | |
| if (!(scxctr & SC01CTR_TXE)) | |
| *port->_control = scxctr; | |
| mn10300_serial_en_tx_intr(port); | |
| if (locked) | |
| spin_unlock(&port->uart.lock); | |
| local_irq_restore(flags); | |
| } | |
| /* | |
| * set up a serial port as a console | |
| * - construct a cflag setting for the first rs_open() | |
| * - initialize the serial port | |
| * - return non-zero if we didn't find a serial port. | |
| */ | |
| static int __init mn10300_serial_console_setup(struct console *co, | |
| char *options) | |
| { | |
| struct mn10300_serial_port *port; | |
| int i, parity = 'n', baud = 9600, bits = 8, flow = 0; | |
| for (i = 0 ; i < NR_PORTS ; i++) { | |
| port = mn10300_serial_ports[i]; | |
| if (port && !port->gdbstub && port->uart.line == co->index) | |
| goto found_device; | |
| } | |
| return -ENODEV; | |
| found_device: | |
| switch (port->clock_src) { | |
| case MNSCx_CLOCK_SRC_IOCLK: | |
| port->ioclk = MN10300_IOCLK; | |
| break; | |
| #ifdef MN10300_IOBCLK | |
| case MNSCx_CLOCK_SRC_IOBCLK: | |
| port->ioclk = MN10300_IOBCLK; | |
| break; | |
| #endif | |
| default: | |
| BUG(); | |
| } | |
| if (options) | |
| uart_parse_options(options, &baud, &parity, &bits, &flow); | |
| return uart_set_options(&port->uart, co, baud, parity, bits, flow); | |
| } | |
| /* | |
| * register console | |
| */ | |
| static int __init mn10300_serial_console_init(void) | |
| { | |
| register_console(&mn10300_serial_console); | |
| return 0; | |
| } | |
| console_initcall(mn10300_serial_console_init); | |
| #endif | |
| #ifdef CONFIG_CONSOLE_POLL | |
| /* | |
| * Polled character reception for the kernel debugger | |
| */ | |
| static int mn10300_serial_poll_get_char(struct uart_port *_port) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| unsigned ix; | |
| u8 st, ch; | |
| _enter("%s", port->name); | |
| if (mn10300_serial_int_tbl[port->rx_irq].port != NULL) { | |
| do { | |
| /* pull chars out of the hat */ | |
| ix = ACCESS_ONCE(port->rx_outp); | |
| if (CIRC_CNT(port->rx_inp, ix, MNSC_BUFFER_SIZE) == 0) | |
| return NO_POLL_CHAR; | |
| smp_read_barrier_depends(); | |
| ch = port->rx_buffer[ix++]; | |
| st = port->rx_buffer[ix++]; | |
| smp_mb(); | |
| port->rx_outp = ix & (MNSC_BUFFER_SIZE - 1); | |
| } while (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF)); | |
| } else { | |
| do { | |
| st = *port->_status; | |
| if (st & (SC01STR_FEF | SC01STR_PEF | SC01STR_OEF)) | |
| continue; | |
| } while (!(st & SC01STR_RBF)); | |
| ch = *port->_rxb; | |
| } | |
| return ch; | |
| } | |
| /* | |
| * Polled character transmission for the kernel debugger | |
| */ | |
| static void mn10300_serial_poll_put_char(struct uart_port *_port, | |
| unsigned char ch) | |
| { | |
| struct mn10300_serial_port *port = | |
| container_of(_port, struct mn10300_serial_port, uart); | |
| u8 intr, tmp; | |
| /* wait for the transmitter to finish anything it might be doing (and | |
| * this includes the virtual DMA handler, so it might take a while) */ | |
| while (*port->_status & (SC01STR_TBF | SC01STR_TXF)) | |
| continue; | |
| /* disable the Tx ready interrupt */ | |
| intr = *port->_intr; | |
| *port->_intr = intr & ~SC01ICR_TI; | |
| tmp = *port->_intr; | |
| if (ch == 0x0a) { | |
| *port->_txb = 0x0d; | |
| while (*port->_status & SC01STR_TBF) | |
| continue; | |
| } | |
| *port->_txb = ch; | |
| while (*port->_status & SC01STR_TBF) | |
| continue; | |
| /* restore the Tx interrupt flag */ | |
| *port->_intr = intr; | |
| tmp = *port->_intr; | |
| } | |
| #endif /* CONFIG_CONSOLE_POLL */ |