From: Linus Torvalds Date: Fri, 21 Jan 2011 00:39:23 +0000 (-0800) Subject: Merge branch 'tty-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh... X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=fc887b15d935ead2a00aef5779a18034e7c69ee1;p=GitHub%2Fmoto-9609%2Fandroid_kernel_motorola_exynos9610.git Merge branch 'tty-linus' of git://git./linux/kernel/git/gregkh/tty-2.6 * 'tty-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6: tty: update MAINTAINERS file due to driver movement tty: move drivers/serial/ to drivers/tty/serial/ tty: move hvc drivers to drivers/tty/hvc/ --- fc887b15d935ead2a00aef5779a18034e7c69ee1 diff --cc MAINTAINERS index 1af022e63668,fd05bb773494..2e14fd74d48c --- a/MAINTAINERS +++ b/MAINTAINERS @@@ -3527,15 -3505,8 +3527,15 @@@ JSM Neo PCI based serial car M: Breno Leitao L: linux-serial@vger.kernel.org S: Maintained - F: drivers/serial/jsm/ + F: drivers/tty/serial/jsm/ +K10TEMP HARDWARE MONITORING DRIVER +M: Clemens Ladisch +L: lm-sensors@lm-sensors.org +S: Maintained +F: Documentation/hwmon/k10temp +F: drivers/hwmon/k10temp.c + K8TEMP HARDWARE MONITORING DRIVER M: Rudolf Marek L: lm-sensors@lm-sensors.org diff --cc drivers/tty/serial/atmel_serial.c index 000000000000,3892666b5fbd..2a1d52fb4936 mode 000000,100644..100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@@ -1,0 -1,1808 +1,1813 @@@ + /* + * linux/drivers/char/atmel_serial.c + * + * Driver for Atmel AT91 / AT32 Serial ports + * Copyright (C) 2003 Rick Bronson + * + * Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd. + * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. + * + * DMA support added by Chip Coldwell. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + + #include + #include + + #ifdef CONFIG_ARM + #include + #include + #endif + + #define PDC_BUFFER_SIZE 512 + /* Revisit: We should calculate this based on the actual port settings */ + #define PDC_RX_TIMEOUT (3 * 10) /* 3 bytes */ + + #if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + #define SUPPORT_SYSRQ + #endif + + #include + + static void atmel_start_rx(struct uart_port *port); + static void atmel_stop_rx(struct uart_port *port); + + #ifdef CONFIG_SERIAL_ATMEL_TTYAT + + /* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we + * should coexist with the 8250 driver, such as if we have an external 16C550 + * UART. */ + #define SERIAL_ATMEL_MAJOR 204 + #define MINOR_START 154 + #define ATMEL_DEVICENAME "ttyAT" + + #else + + /* Use device name ttyS, major 4, minor 64-68. This is the usual serial port + * name, but it is legally reserved for the 8250 driver. */ + #define SERIAL_ATMEL_MAJOR TTY_MAJOR + #define MINOR_START 64 + #define ATMEL_DEVICENAME "ttyS" + + #endif + + #define ATMEL_ISR_PASS_LIMIT 256 + + /* UART registers. CR is write-only, hence no GET macro */ + #define UART_PUT_CR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_CR) + #define UART_GET_MR(port) __raw_readl((port)->membase + ATMEL_US_MR) + #define UART_PUT_MR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_MR) + #define UART_PUT_IER(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IER) + #define UART_PUT_IDR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IDR) + #define UART_GET_IMR(port) __raw_readl((port)->membase + ATMEL_US_IMR) + #define UART_GET_CSR(port) __raw_readl((port)->membase + ATMEL_US_CSR) + #define UART_GET_CHAR(port) __raw_readl((port)->membase + ATMEL_US_RHR) + #define UART_PUT_CHAR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_THR) + #define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR) + #define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR) + #define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR) + #define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR) + + /* PDC registers */ + #define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) + #define UART_GET_PTSR(port) __raw_readl((port)->membase + ATMEL_PDC_PTSR) + + #define UART_PUT_RPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RPR) + #define UART_GET_RPR(port) __raw_readl((port)->membase + ATMEL_PDC_RPR) + #define UART_PUT_RCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RCR) + #define UART_PUT_RNPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNPR) + #define UART_PUT_RNCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNCR) + + #define UART_PUT_TPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TPR) + #define UART_PUT_TCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TCR) + #define UART_GET_TCR(port) __raw_readl((port)->membase + ATMEL_PDC_TCR) + + static int (*atmel_open_hook)(struct uart_port *); + static void (*atmel_close_hook)(struct uart_port *); + + struct atmel_dma_buffer { + unsigned char *buf; + dma_addr_t dma_addr; + unsigned int dma_size; + unsigned int ofs; + }; + + struct atmel_uart_char { + u16 status; + u16 ch; + }; + + #define ATMEL_SERIAL_RINGSIZE 1024 + + /* + * We wrap our port structure around the generic uart_port. + */ + struct atmel_uart_port { + struct uart_port uart; /* uart */ + struct clk *clk; /* uart clock */ + int may_wakeup; /* cached value of device_may_wakeup for times we need to disable it */ + u32 backup_imr; /* IMR saved during suspend */ + int break_active; /* break being received */ + + short use_dma_rx; /* enable PDC receiver */ + short pdc_rx_idx; /* current PDC RX buffer */ + struct atmel_dma_buffer pdc_rx[2]; /* PDC receier */ + + short use_dma_tx; /* enable PDC transmitter */ + struct atmel_dma_buffer pdc_tx; /* PDC transmitter */ + + struct tasklet_struct tasklet; + unsigned int irq_status; + unsigned int irq_status_prev; + + struct circ_buf rx_ring; + + struct serial_rs485 rs485; /* rs485 settings */ + unsigned int tx_done_mask; + }; + + static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; + + #ifdef SUPPORT_SYSRQ + static struct console atmel_console; + #endif + + static inline struct atmel_uart_port * + to_atmel_uart_port(struct uart_port *uart) + { + return container_of(uart, struct atmel_uart_port, uart); + } + + #ifdef CONFIG_SERIAL_ATMEL_PDC + static bool atmel_use_dma_rx(struct uart_port *port) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + return atmel_port->use_dma_rx; + } + + static bool atmel_use_dma_tx(struct uart_port *port) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + return atmel_port->use_dma_tx; + } + #else + static bool atmel_use_dma_rx(struct uart_port *port) + { + return false; + } + + static bool atmel_use_dma_tx(struct uart_port *port) + { + return false; + } + #endif + + /* Enable or disable the rs485 support */ + void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int mode; + + spin_lock(&port->lock); + + /* Disable interrupts */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + + mode = UART_GET_MR(port); + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + atmel_port->rs485 = *rs485conf; + + if (rs485conf->flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND) + UART_PUT_TTGR(port, rs485conf->delay_rts_after_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + if (atmel_use_dma_tx(port)) + atmel_port->tx_done_mask = ATMEL_US_ENDTX | + ATMEL_US_TXBUFE; + else + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } + UART_PUT_MR(port, mode); + + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + + spin_unlock(&port->lock); + + } + + /* + * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. + */ + static u_int atmel_tx_empty(struct uart_port *port) + { + return (UART_GET_CSR(port) & ATMEL_US_TXEMPTY) ? TIOCSER_TEMT : 0; + } + + /* + * Set state of the modem control output lines + */ + static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) + { + unsigned int control = 0; + unsigned int mode; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + #ifdef CONFIG_ARCH_AT91RM9200 + if (cpu_is_at91rm9200()) { + /* + * AT91RM9200 Errata #39: RTS0 is not internally connected + * to PA21. We need to drive the pin manually. + */ + if (port->mapbase == AT91RM9200_BASE_US0) { + if (mctrl & TIOCM_RTS) + at91_set_gpio_value(AT91_PIN_PA21, 0); + else + at91_set_gpio_value(AT91_PIN_PA21, 1); + } + } + #endif + + if (mctrl & TIOCM_RTS) + control |= ATMEL_US_RTSEN; + else + control |= ATMEL_US_RTSDIS; + + if (mctrl & TIOCM_DTR) + control |= ATMEL_US_DTREN; + else + control |= ATMEL_US_DTRDIS; + + UART_PUT_CR(port, control); + + /* Local loopback mode? */ + mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE; + if (mctrl & TIOCM_LOOP) + mode |= ATMEL_US_CHMODE_LOC_LOOP; + else + mode |= ATMEL_US_CHMODE_NORMAL; + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND) + UART_PUT_TTGR(port, + atmel_port->rs485.delay_rts_after_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + } + UART_PUT_MR(port, mode); + } + + /* + * Get state of the modem control input lines + */ + static u_int atmel_get_mctrl(struct uart_port *port) + { + unsigned int status, ret = 0; + + status = UART_GET_CSR(port); + + /* + * The control signals are active low. + */ + if (!(status & ATMEL_US_DCD)) + ret |= TIOCM_CD; + if (!(status & ATMEL_US_CTS)) + ret |= TIOCM_CTS; + if (!(status & ATMEL_US_DSR)) + ret |= TIOCM_DSR; + if (!(status & ATMEL_US_RI)) + ret |= TIOCM_RI; + + return ret; + } + + /* + * Stop transmitting. + */ + static void atmel_stop_tx(struct uart_port *port) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_use_dma_tx(port)) { + /* disable PDC transmit */ + UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); + } + /* Disable interrupts */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_start_rx(port); + } + + /* + * Start transmitting. + */ + static void atmel_start_tx(struct uart_port *port) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_use_dma_tx(port)) { + if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN) + /* The transmitter is already running. Yes, we + really need this.*/ + return; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_stop_rx(port); + + /* re-enable PDC transmit */ + UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); + } + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + } + + /* + * start receiving - port is in process of being opened. + */ + static void atmel_start_rx(struct uart_port *port) + { + UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */ + + if (atmel_use_dma_rx(port)) { + /* enable PDC controller */ + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | + port->read_status_mask); + UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); + } else { + UART_PUT_IER(port, ATMEL_US_RXRDY); + } + } + + /* + * Stop receiving - port is in process of being closed. + */ + static void atmel_stop_rx(struct uart_port *port) + { + if (atmel_use_dma_rx(port)) { + /* disable PDC receive */ + UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS); + UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | + port->read_status_mask); + } else { + UART_PUT_IDR(port, ATMEL_US_RXRDY); + } + } + + /* + * Enable modem status interrupts + */ + static void atmel_enable_ms(struct uart_port *port) + { + UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC + | ATMEL_US_DCDIC | ATMEL_US_CTSIC); + } + + /* + * Control the transmission of a break signal + */ + static void atmel_break_ctl(struct uart_port *port, int break_state) + { + if (break_state != 0) + UART_PUT_CR(port, ATMEL_US_STTBRK); /* start break */ + else + UART_PUT_CR(port, ATMEL_US_STPBRK); /* stop break */ + } + + /* + * Stores the incoming character in the ring buffer + */ + static void + atmel_buffer_rx_char(struct uart_port *port, unsigned int status, + unsigned int ch) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct circ_buf *ring = &atmel_port->rx_ring; + struct atmel_uart_char *c; + + if (!CIRC_SPACE(ring->head, ring->tail, ATMEL_SERIAL_RINGSIZE)) + /* Buffer overflow, ignore char */ + return; + + c = &((struct atmel_uart_char *)ring->buf)[ring->head]; + c->status = status; + c->ch = ch; + + /* Make sure the character is stored before we update head. */ + smp_wmb(); + + ring->head = (ring->head + 1) & (ATMEL_SERIAL_RINGSIZE - 1); + } + + /* + * Deal with parity, framing and overrun errors. + */ + static void atmel_pdc_rxerr(struct uart_port *port, unsigned int status) + { + /* clear error */ + UART_PUT_CR(port, ATMEL_US_RSTSTA); + + if (status & ATMEL_US_RXBRK) { + /* ignore side-effect */ + status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); + port->icount.brk++; + } + if (status & ATMEL_US_PARE) + port->icount.parity++; + if (status & ATMEL_US_FRAME) + port->icount.frame++; + if (status & ATMEL_US_OVRE) + port->icount.overrun++; + } + + /* + * Characters received (called from interrupt handler) + */ + static void atmel_rx_chars(struct uart_port *port) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int status, ch; + + status = UART_GET_CSR(port); + while (status & ATMEL_US_RXRDY) { + ch = UART_GET_CHAR(port); + + /* + * note that the error handling code is + * out of the main execution path + */ + if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME + | ATMEL_US_OVRE | ATMEL_US_RXBRK) + || atmel_port->break_active)) { + + /* clear error */ + UART_PUT_CR(port, ATMEL_US_RSTSTA); + + if (status & ATMEL_US_RXBRK + && !atmel_port->break_active) { + atmel_port->break_active = 1; + UART_PUT_IER(port, ATMEL_US_RXBRK); + } else { + /* + * This is either the end-of-break + * condition or we've received at + * least one character without RXBRK + * being set. In both cases, the next + * RXBRK will indicate start-of-break. + */ + UART_PUT_IDR(port, ATMEL_US_RXBRK); + status &= ~ATMEL_US_RXBRK; + atmel_port->break_active = 0; + } + } + + atmel_buffer_rx_char(port, status, ch); + status = UART_GET_CSR(port); + } + + tasklet_schedule(&atmel_port->tasklet); + } + + /* + * Transmit characters (called from tasklet with TXRDY interrupt + * disabled) + */ + static void atmel_tx_chars(struct uart_port *port) + { + struct circ_buf *xmit = &port->state->xmit; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) { + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; + + while (UART_GET_CSR(port) & atmel_port->tx_done_mask) { + UART_PUT_CHAR(port, 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)) + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + } + + /* + * receive interrupt handler. + */ + static void + atmel_handle_receive(struct uart_port *port, unsigned int pending) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_use_dma_rx(port)) { + /* + * PDC receive. Just schedule the tasklet and let it + * figure out the details. + * + * TODO: We're not handling error flags correctly at + * the moment. + */ + if (pending & (ATMEL_US_ENDRX | ATMEL_US_TIMEOUT)) { + UART_PUT_IDR(port, (ATMEL_US_ENDRX + | ATMEL_US_TIMEOUT)); + tasklet_schedule(&atmel_port->tasklet); + } + + if (pending & (ATMEL_US_RXBRK | ATMEL_US_OVRE | + ATMEL_US_FRAME | ATMEL_US_PARE)) + atmel_pdc_rxerr(port, pending); + } + + /* Interrupt receive */ + if (pending & ATMEL_US_RXRDY) + atmel_rx_chars(port); + else if (pending & ATMEL_US_RXBRK) { + /* + * End of break detected. If it came along with a + * character, atmel_rx_chars will handle it. + */ + UART_PUT_CR(port, ATMEL_US_RSTSTA); + UART_PUT_IDR(port, ATMEL_US_RXBRK); + atmel_port->break_active = 0; + } + } + + /* + * transmit interrupt handler. (Transmit is IRQF_NODELAY safe) + */ + static void + atmel_handle_transmit(struct uart_port *port, unsigned int pending) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (pending & atmel_port->tx_done_mask) { + /* Either PDC or interrupt transmission */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + tasklet_schedule(&atmel_port->tasklet); + } + } + + /* + * status flags interrupt handler. + */ + static void + atmel_handle_status(struct uart_port *port, unsigned int pending, + unsigned int status) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC + | ATMEL_US_CTSIC)) { + atmel_port->irq_status = status; + tasklet_schedule(&atmel_port->tasklet); + } + } + + /* + * Interrupt handler + */ + static irqreturn_t atmel_interrupt(int irq, void *dev_id) + { + struct uart_port *port = dev_id; + unsigned int status, pending, pass_counter = 0; + + do { + status = UART_GET_CSR(port); + pending = status & UART_GET_IMR(port); + if (!pending) + break; + + atmel_handle_receive(port, pending); + atmel_handle_status(port, pending, status); + atmel_handle_transmit(port, pending); + } while (pass_counter++ < ATMEL_ISR_PASS_LIMIT); + + return pass_counter ? IRQ_HANDLED : IRQ_NONE; + } + + /* + * Called from tasklet with ENDTX and TXBUFE interrupts disabled. + */ + static void atmel_tx_dma(struct uart_port *port) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct circ_buf *xmit = &port->state->xmit; + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + int count; + + /* nothing left to transmit? */ + if (UART_GET_TCR(port)) + return; + + xmit->tail += pdc->ofs; + xmit->tail &= UART_XMIT_SIZE - 1; + + port->icount.tx += pdc->ofs; + pdc->ofs = 0; + + /* more to transmit - setup next transfer */ + + /* disable PDC transmit */ + UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); + + if (!uart_circ_empty(xmit) && !uart_tx_stopped(port)) { + dma_sync_single_for_device(port->dev, + pdc->dma_addr, + pdc->dma_size, + DMA_TO_DEVICE); + + count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + pdc->ofs = count; + + UART_PUT_TPR(port, pdc->dma_addr + xmit->tail); + UART_PUT_TCR(port, count); + /* re-enable PDC transmit */ + UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + } else { + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + /* DMA done, stop TX, start RX for RS485 */ + atmel_start_rx(port); + } + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + } + + static void atmel_rx_from_ring(struct uart_port *port) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct circ_buf *ring = &atmel_port->rx_ring; + unsigned int flg; + unsigned int status; + + while (ring->head != ring->tail) { + struct atmel_uart_char c; + + /* Make sure c is loaded after head. */ + smp_rmb(); + + c = ((struct atmel_uart_char *)ring->buf)[ring->tail]; + + ring->tail = (ring->tail + 1) & (ATMEL_SERIAL_RINGSIZE - 1); + + port->icount.rx++; + status = c.status; + flg = TTY_NORMAL; + + /* + * note that the error handling code is + * out of the main execution path + */ + if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME + | ATMEL_US_OVRE | ATMEL_US_RXBRK))) { + if (status & ATMEL_US_RXBRK) { + /* ignore side-effect */ + status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); + + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } + if (status & ATMEL_US_PARE) + port->icount.parity++; + if (status & ATMEL_US_FRAME) + port->icount.frame++; + if (status & ATMEL_US_OVRE) + port->icount.overrun++; + + status &= port->read_status_mask; + + if (status & ATMEL_US_RXBRK) + flg = TTY_BREAK; + else if (status & ATMEL_US_PARE) + flg = TTY_PARITY; + else if (status & ATMEL_US_FRAME) + flg = TTY_FRAME; + } + + + if (uart_handle_sysrq_char(port, c.ch)) + continue; + + uart_insert_char(port, status, ATMEL_US_OVRE, c.ch, flg); + } + + /* + * Drop the lock here since it might end up calling + * uart_start(), which takes the lock. + */ + spin_unlock(&port->lock); + tty_flip_buffer_push(port->state->port.tty); + spin_lock(&port->lock); + } + + static void atmel_rx_from_dma(struct uart_port *port) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct tty_struct *tty = port->state->port.tty; + struct atmel_dma_buffer *pdc; + int rx_idx = atmel_port->pdc_rx_idx; + unsigned int head; + unsigned int tail; + unsigned int count; + + do { + /* Reset the UART timeout early so that we don't miss one */ + UART_PUT_CR(port, ATMEL_US_STTTO); + + pdc = &atmel_port->pdc_rx[rx_idx]; + head = UART_GET_RPR(port) - pdc->dma_addr; + tail = pdc->ofs; + + /* If the PDC has switched buffers, RPR won't contain + * any address within the current buffer. Since head + * is unsigned, we just need a one-way comparison to + * find out. + * + * In this case, we just need to consume the entire + * buffer and resubmit it for DMA. This will clear the + * ENDRX bit as well, so that we can safely re-enable + * all interrupts below. + */ + head = min(head, pdc->dma_size); + + if (likely(head != tail)) { + dma_sync_single_for_cpu(port->dev, pdc->dma_addr, + pdc->dma_size, DMA_FROM_DEVICE); + + /* + * head will only wrap around when we recycle + * the DMA buffer, and when that happens, we + * explicitly set tail to 0. So head will + * always be greater than tail. + */ + count = head - tail; + + tty_insert_flip_string(tty, pdc->buf + pdc->ofs, count); + + dma_sync_single_for_device(port->dev, pdc->dma_addr, + pdc->dma_size, DMA_FROM_DEVICE); + + port->icount.rx += count; + pdc->ofs = head; + } + + /* + * If the current buffer is full, we need to check if + * the next one contains any additional data. + */ + if (head >= pdc->dma_size) { + pdc->ofs = 0; + UART_PUT_RNPR(port, pdc->dma_addr); + UART_PUT_RNCR(port, pdc->dma_size); + + rx_idx = !rx_idx; + atmel_port->pdc_rx_idx = rx_idx; + } + } while (head >= pdc->dma_size); + + /* + * Drop the lock here since it might end up calling + * uart_start(), which takes the lock. + */ + spin_unlock(&port->lock); + tty_flip_buffer_push(tty); + spin_lock(&port->lock); + + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); + } + + /* + * tasklet handling tty stuff outside the interrupt handler. + */ + static void atmel_tasklet_func(unsigned long data) + { + struct uart_port *port = (struct uart_port *)data; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int status; + unsigned int status_change; + + /* The interrupt handler does not take the lock */ + spin_lock(&port->lock); + + if (atmel_use_dma_tx(port)) + atmel_tx_dma(port); + else + atmel_tx_chars(port); + + status = atmel_port->irq_status; + status_change = status ^ atmel_port->irq_status_prev; + + if (status_change & (ATMEL_US_RI | ATMEL_US_DSR + | ATMEL_US_DCD | ATMEL_US_CTS)) { + /* TODO: All reads to CSR will clear these interrupts! */ + if (status_change & ATMEL_US_RI) + port->icount.rng++; + if (status_change & ATMEL_US_DSR) + port->icount.dsr++; + if (status_change & ATMEL_US_DCD) + uart_handle_dcd_change(port, !(status & ATMEL_US_DCD)); + if (status_change & ATMEL_US_CTS) + uart_handle_cts_change(port, !(status & ATMEL_US_CTS)); + + wake_up_interruptible(&port->state->port.delta_msr_wait); + + atmel_port->irq_status_prev = status; + } + + if (atmel_use_dma_rx(port)) + atmel_rx_from_dma(port); + else + atmel_rx_from_ring(port); + + spin_unlock(&port->lock); + } + + /* + * Perform initialization and enable port for reception + */ + static int atmel_startup(struct uart_port *port) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + struct tty_struct *tty = port->state->port.tty; + int retval; + + /* + * Ensure that no interrupts are enabled otherwise when + * request_irq() is called we could get stuck trying to + * handle an unexpected interrupt + */ + UART_PUT_IDR(port, -1); + + /* + * Allocate the IRQ + */ + retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED, + tty ? tty->name : "atmel_serial", port); + if (retval) { + printk("atmel_serial: atmel_startup - Can't get irq\n"); + return retval; + } + + /* + * Initialize DMA (if necessary) + */ + if (atmel_use_dma_rx(port)) { + int i; + + for (i = 0; i < 2; i++) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; + + pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL); + if (pdc->buf == NULL) { + if (i != 0) { + dma_unmap_single(port->dev, + atmel_port->pdc_rx[0].dma_addr, + PDC_BUFFER_SIZE, + DMA_FROM_DEVICE); + kfree(atmel_port->pdc_rx[0].buf); + } + free_irq(port->irq, port); + return -ENOMEM; + } + pdc->dma_addr = dma_map_single(port->dev, + pdc->buf, + PDC_BUFFER_SIZE, + DMA_FROM_DEVICE); + pdc->dma_size = PDC_BUFFER_SIZE; + pdc->ofs = 0; + } + + atmel_port->pdc_rx_idx = 0; + + UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr); + UART_PUT_RCR(port, PDC_BUFFER_SIZE); + + UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr); + UART_PUT_RNCR(port, PDC_BUFFER_SIZE); + } + if (atmel_use_dma_tx(port)) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + struct circ_buf *xmit = &port->state->xmit; + + pdc->buf = xmit->buf; + pdc->dma_addr = dma_map_single(port->dev, + pdc->buf, + UART_XMIT_SIZE, + DMA_TO_DEVICE); + pdc->dma_size = UART_XMIT_SIZE; + pdc->ofs = 0; + } + + /* + * If there is a specific "open" function (to register + * control line interrupts) + */ + if (atmel_open_hook) { + retval = atmel_open_hook(port); + if (retval) { + free_irq(port->irq, port); + return retval; + } + } + + /* Save current CSR for comparison in atmel_tasklet_func() */ + atmel_port->irq_status_prev = UART_GET_CSR(port); + atmel_port->irq_status = atmel_port->irq_status_prev; + + /* + * Finally, enable the serial port + */ + UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); + /* enable xmit & rcvr */ + UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); + + if (atmel_use_dma_rx(port)) { + /* set UART timeout */ + UART_PUT_RTOR(port, PDC_RX_TIMEOUT); + UART_PUT_CR(port, ATMEL_US_STTTO); + + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); + /* enable PDC controller */ + UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); + } else { + /* enable receive only */ + UART_PUT_IER(port, ATMEL_US_RXRDY); + } + + return 0; + } + + /* + * Disable the port + */ + static void atmel_shutdown(struct uart_port *port) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + /* + * Ensure everything is stopped. + */ + atmel_stop_rx(port); + atmel_stop_tx(port); + + /* + * Shut-down the DMA. + */ + if (atmel_use_dma_rx(port)) { + int i; + + for (i = 0; i < 2; i++) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; + + dma_unmap_single(port->dev, + pdc->dma_addr, + pdc->dma_size, + DMA_FROM_DEVICE); + kfree(pdc->buf); + } + } + if (atmel_use_dma_tx(port)) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + + dma_unmap_single(port->dev, + pdc->dma_addr, + pdc->dma_size, + DMA_TO_DEVICE); + } + + /* + * Disable all interrupts, port and break condition. + */ + UART_PUT_CR(port, ATMEL_US_RSTSTA); + UART_PUT_IDR(port, -1); + + /* + * Free the interrupt + */ + free_irq(port->irq, port); + + /* + * If there is a specific "close" function (to unregister + * control line interrupts) + */ + if (atmel_close_hook) + atmel_close_hook(port); + } + + /* + * Flush any TX data submitted for DMA. Called when the TX circular + * buffer is reset. + */ + static void atmel_flush_buffer(struct uart_port *port) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_use_dma_tx(port)) { + UART_PUT_TCR(port, 0); + atmel_port->pdc_tx.ofs = 0; + } + } + + /* + * Power / Clock management. + */ + static void atmel_serial_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) + { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + switch (state) { + case 0: + /* + * Enable the peripheral clock for this serial port. + * This is called on uart_open() or a resume event. + */ + clk_enable(atmel_port->clk); + + /* re-enable interrupts if we disabled some on suspend */ + UART_PUT_IER(port, atmel_port->backup_imr); + break; + case 3: + /* Back up the interrupt mask and disable all interrupts */ + atmel_port->backup_imr = UART_GET_IMR(port); + UART_PUT_IDR(port, -1); + + /* + * Disable the peripheral clock for this serial port. + * This is called on uart_close() or a suspend event. + */ + clk_disable(atmel_port->clk); + break; + default: + printk(KERN_ERR "atmel_serial: unknown pm %d\n", state); + } + } + + /* + * Change the port parameters + */ + static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) + { + unsigned long flags; + unsigned int mode, imr, quot, baud; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + /* Get current mode register */ + mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL + | ATMEL_US_NBSTOP | ATMEL_US_PAR + | ATMEL_US_USMODE); + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + quot = uart_get_divisor(port, baud); + + if (quot > 65535) { /* BRGR is 16-bit, so switch to slower clock */ + quot /= 8; + mode |= ATMEL_US_USCLKS_MCK_DIV8; + } + + /* byte size */ + switch (termios->c_cflag & CSIZE) { + case CS5: + mode |= ATMEL_US_CHRL_5; + break; + case CS6: + mode |= ATMEL_US_CHRL_6; + break; + case CS7: + mode |= ATMEL_US_CHRL_7; + break; + default: + mode |= ATMEL_US_CHRL_8; + break; + } + + /* stop bits */ + if (termios->c_cflag & CSTOPB) + mode |= ATMEL_US_NBSTOP_2; + + /* parity */ + if (termios->c_cflag & PARENB) { + /* Mark or Space parity */ + if (termios->c_cflag & CMSPAR) { + if (termios->c_cflag & PARODD) + mode |= ATMEL_US_PAR_MARK; + else + mode |= ATMEL_US_PAR_SPACE; + } else if (termios->c_cflag & PARODD) + mode |= ATMEL_US_PAR_ODD; + else + mode |= ATMEL_US_PAR_EVEN; + } else + mode |= ATMEL_US_PAR_NONE; + + /* hardware handshake (RTS/CTS) */ + if (termios->c_cflag & CRTSCTS) + mode |= ATMEL_US_USMODE_HWHS; + else + mode |= ATMEL_US_USMODE_NORMAL; + + spin_lock_irqsave(&port->lock, flags); + + port->read_status_mask = ATMEL_US_OVRE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= ATMEL_US_RXBRK; + + if (atmel_use_dma_rx(port)) + /* need to enable error interrupts */ + UART_PUT_IER(port, port->read_status_mask); + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= (ATMEL_US_FRAME | ATMEL_US_PARE); + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= ATMEL_US_RXBRK; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= ATMEL_US_OVRE; + } + /* TODO: Ignore all characters if CREAD is set.*/ + + /* update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * save/disable interrupts. The tty layer will ensure that the + * transmitter is empty if requested by the caller, so there's + * no need to wait for it here. + */ + imr = UART_GET_IMR(port); + UART_PUT_IDR(port, -1); + + /* disable receiver and transmitter */ + UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS); + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND) + UART_PUT_TTGR(port, + atmel_port->rs485.delay_rts_after_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + } + + /* set the parity, stop bits and data size */ + UART_PUT_MR(port, mode); + + /* set the baud rate */ + UART_PUT_BRGR(port, quot); + UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); + UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); + + /* restore interrupts */ + UART_PUT_IER(port, imr); + + /* CTS flow-control and modem-status interrupts */ + if (UART_ENABLE_MS(port, termios->c_cflag)) + port->ops->enable_ms(port); + + spin_unlock_irqrestore(&port->lock, flags); + } + + /* + * Return string describing the specified port + */ + static const char *atmel_type(struct uart_port *port) + { + return (port->type == PORT_ATMEL) ? "ATMEL_SERIAL" : NULL; + } + + /* + * Release the memory region(s) being used by 'port'. + */ + static void atmel_release_port(struct uart_port *port) + { + struct platform_device *pdev = to_platform_device(port->dev); + int size = pdev->resource[0].end - pdev->resource[0].start + 1; + + release_mem_region(port->mapbase, size); + + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } + } + + /* + * Request the memory region(s) being used by 'port'. + */ + static int atmel_request_port(struct uart_port *port) + { + struct platform_device *pdev = to_platform_device(port->dev); + int size = pdev->resource[0].end - pdev->resource[0].start + 1; + + if (!request_mem_region(port->mapbase, size, "atmel_serial")) + return -EBUSY; + + if (port->flags & UPF_IOREMAP) { + port->membase = ioremap(port->mapbase, size); + if (port->membase == NULL) { + release_mem_region(port->mapbase, size); + return -ENOMEM; + } + } + + return 0; + } + + /* + * Configure/autoconfigure the port. + */ + static void atmel_config_port(struct uart_port *port, int flags) + { + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_ATMEL; + atmel_request_port(port); + } + } + + /* + * Verify the new serial_struct (for TIOCSSERIAL). + */ + static int atmel_verify_port(struct uart_port *port, struct serial_struct *ser) + { + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_ATMEL) + ret = -EINVAL; + if (port->irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != SERIAL_IO_MEM) + ret = -EINVAL; + if (port->uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)port->mapbase != ser->iomem_base) + ret = -EINVAL; + if (port->iobase != ser->port) + ret = -EINVAL; + if (ser->hub6 != 0) + ret = -EINVAL; + return ret; + } + + #ifdef CONFIG_CONSOLE_POLL + static int atmel_poll_get_char(struct uart_port *port) + { + while (!(UART_GET_CSR(port) & ATMEL_US_RXRDY)) + cpu_relax(); + + return UART_GET_CHAR(port); + } + + static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) + { + while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY)) + cpu_relax(); + + UART_PUT_CHAR(port, ch); + } + #endif + + static int + atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) + { + struct serial_rs485 rs485conf; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, + sizeof(rs485conf))) + return -EFAULT; + + atmel_config_rs485(port, &rs485conf); + break; + + case TIOCGRS485: + if (copy_to_user((struct serial_rs485 *) arg, + &(to_atmel_uart_port(port)->rs485), + sizeof(rs485conf))) + return -EFAULT; + break; + + default: + return -ENOIOCTLCMD; + } + return 0; + } + + + + static struct uart_ops atmel_pops = { + .tx_empty = atmel_tx_empty, + .set_mctrl = atmel_set_mctrl, + .get_mctrl = atmel_get_mctrl, + .stop_tx = atmel_stop_tx, + .start_tx = atmel_start_tx, + .stop_rx = atmel_stop_rx, + .enable_ms = atmel_enable_ms, + .break_ctl = atmel_break_ctl, + .startup = atmel_startup, + .shutdown = atmel_shutdown, + .flush_buffer = atmel_flush_buffer, + .set_termios = atmel_set_termios, + .type = atmel_type, + .release_port = atmel_release_port, + .request_port = atmel_request_port, + .config_port = atmel_config_port, + .verify_port = atmel_verify_port, + .pm = atmel_serial_pm, + .ioctl = atmel_ioctl, + #ifdef CONFIG_CONSOLE_POLL + .poll_get_char = atmel_poll_get_char, + .poll_put_char = atmel_poll_put_char, + #endif + }; + + /* + * Configure the port from the platform device resource info. + */ + static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, + struct platform_device *pdev) + { + struct uart_port *port = &atmel_port->uart; + struct atmel_uart_data *data = pdev->dev.platform_data; + + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &atmel_pops; + port->fifosize = 1; + port->line = pdev->id; + port->dev = &pdev->dev; + port->mapbase = pdev->resource[0].start; + port->irq = pdev->resource[1].start; + + tasklet_init(&atmel_port->tasklet, atmel_tasklet_func, + (unsigned long)port); + + memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring)); + + if (data->regs) + /* Already mapped by setup code */ + port->membase = data->regs; + else { + port->flags |= UPF_IOREMAP; + port->membase = NULL; + } + + /* for console, the clock could already be configured */ + if (!atmel_port->clk) { + atmel_port->clk = clk_get(&pdev->dev, "usart"); + clk_enable(atmel_port->clk); + port->uartclk = clk_get_rate(atmel_port->clk); + clk_disable(atmel_port->clk); + /* only enable clock when USART is in use */ + } + + atmel_port->use_dma_rx = data->use_dma_rx; + atmel_port->use_dma_tx = data->use_dma_tx; + atmel_port->rs485 = data->rs485; + /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + else if (atmel_use_dma_tx(port)) { + port->fifosize = PDC_BUFFER_SIZE; + atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE; + } else { + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } + } + + /* + * Register board-specific modem-control line handlers. + */ + void __init atmel_register_uart_fns(struct atmel_port_fns *fns) + { + if (fns->enable_ms) + atmel_pops.enable_ms = fns->enable_ms; + if (fns->get_mctrl) + atmel_pops.get_mctrl = fns->get_mctrl; + if (fns->set_mctrl) + atmel_pops.set_mctrl = fns->set_mctrl; + atmel_open_hook = fns->open; + atmel_close_hook = fns->close; + atmel_pops.pm = fns->pm; + atmel_pops.set_wake = fns->set_wake; + } + + #ifdef CONFIG_SERIAL_ATMEL_CONSOLE + static void atmel_console_putchar(struct uart_port *port, int ch) + { + while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY)) + cpu_relax(); + UART_PUT_CHAR(port, ch); + } + + /* + * Interrupts are disabled on entering + */ + static void atmel_console_write(struct console *co, const char *s, u_int count) + { + struct uart_port *port = &atmel_ports[co->index].uart; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int status, imr; + unsigned int pdc_tx; + + /* + * First, save IMR and then disable interrupts + */ + imr = UART_GET_IMR(port); + UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask); + + /* Store PDC transmit status and disable it */ + pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN; + UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); + + uart_console_write(port, s, count, atmel_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore IMR + */ + do { + status = UART_GET_CSR(port); + } while (!(status & ATMEL_US_TXRDY)); + + /* Restore PDC transmit status */ + if (pdc_tx) + UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); + + /* set interrupts back the way they were */ + UART_PUT_IER(port, imr); + } + + /* + * If the port was already initialised (eg, by a boot loader), + * try to determine the current setup. + */ + static void __init atmel_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) + { + unsigned int mr, quot; + + /* + * If the baud rate generator isn't running, the port wasn't + * initialized by the boot loader. + */ + quot = UART_GET_BRGR(port) & ATMEL_US_CD; + if (!quot) + return; + + mr = UART_GET_MR(port) & ATMEL_US_CHRL; + if (mr == ATMEL_US_CHRL_8) + *bits = 8; + else + *bits = 7; + + mr = UART_GET_MR(port) & ATMEL_US_PAR; + if (mr == ATMEL_US_PAR_EVEN) + *parity = 'e'; + else if (mr == ATMEL_US_PAR_ODD) + *parity = 'o'; + + /* + * The serial core only rounds down when matching this to a + * supported baud rate. Make sure we don't end up slightly + * lower than one of those, as it would make us fall through + * to a much lower baud rate than we really want. + */ + *baud = port->uartclk / (16 * (quot - 1)); + } + + static int __init atmel_console_setup(struct console *co, char *options) + { + struct uart_port *port = &atmel_ports[co->index].uart; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (port->membase == NULL) { + /* Port not initialized yet - delay setup */ + return -ENODEV; + } + + clk_enable(atmel_ports[co->index].clk); + + UART_PUT_IDR(port, -1); + UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); + UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + atmel_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); + } + + static struct uart_driver atmel_uart; + + static struct console atmel_console = { + .name = ATMEL_DEVICENAME, + .write = atmel_console_write, + .device = uart_console_device, + .setup = atmel_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &atmel_uart, + }; + + #define ATMEL_CONSOLE_DEVICE (&atmel_console) + + /* + * Early console initialization (before VM subsystem initialized). + */ + static int __init atmel_console_init(void) + { + if (atmel_default_console_device) { + add_preferred_console(ATMEL_DEVICENAME, + atmel_default_console_device->id, NULL); + atmel_init_port(&atmel_ports[atmel_default_console_device->id], + atmel_default_console_device); + register_console(&atmel_console); + } + + return 0; + } + + console_initcall(atmel_console_init); + + /* + * Late console initialization. + */ + static int __init atmel_late_console_init(void) + { + if (atmel_default_console_device + && !(atmel_console.flags & CON_ENABLED)) + register_console(&atmel_console); + + return 0; + } + + core_initcall(atmel_late_console_init); + + static inline bool atmel_is_console_port(struct uart_port *port) + { + return port->cons && port->cons->index == port->line; + } + + #else + #define ATMEL_CONSOLE_DEVICE NULL + + static inline bool atmel_is_console_port(struct uart_port *port) + { + return false; + } + #endif + + static struct uart_driver atmel_uart = { + .owner = THIS_MODULE, + .driver_name = "atmel_serial", + .dev_name = ATMEL_DEVICENAME, + .major = SERIAL_ATMEL_MAJOR, + .minor = MINOR_START, + .nr = ATMEL_MAX_UART, + .cons = ATMEL_CONSOLE_DEVICE, + }; + + #ifdef CONFIG_PM + static bool atmel_serial_clk_will_stop(void) + { + #ifdef CONFIG_ARCH_AT91 + return at91_suspend_entering_slow_clock(); + #else + return false; + #endif + } + + static int atmel_serial_suspend(struct platform_device *pdev, + pm_message_t state) + { + struct uart_port *port = platform_get_drvdata(pdev); + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + if (atmel_is_console_port(port) && console_suspend_enabled) { + /* Drain the TX shifter */ + while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY)) + cpu_relax(); + } + + /* we can not wake up if we're running on slow clock */ + atmel_port->may_wakeup = device_may_wakeup(&pdev->dev); + if (atmel_serial_clk_will_stop()) + device_set_wakeup_enable(&pdev->dev, 0); + + uart_suspend_port(&atmel_uart, port); + + return 0; + } + + static int atmel_serial_resume(struct platform_device *pdev) + { + struct uart_port *port = platform_get_drvdata(pdev); + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + uart_resume_port(&atmel_uart, port); + device_set_wakeup_enable(&pdev->dev, atmel_port->may_wakeup); + + return 0; + } + #else + #define atmel_serial_suspend NULL + #define atmel_serial_resume NULL + #endif + + static int __devinit atmel_serial_probe(struct platform_device *pdev) + { + struct atmel_uart_port *port; + void *data; + int ret; + + BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); + + port = &atmel_ports[pdev->id]; + port->backup_imr = 0; + + atmel_init_port(port, pdev); + + if (!atmel_use_dma_rx(&port->uart)) { + ret = -ENOMEM; + data = kmalloc(sizeof(struct atmel_uart_char) + * ATMEL_SERIAL_RINGSIZE, GFP_KERNEL); + if (!data) + goto err_alloc_ring; + port->rx_ring.buf = data; + } + + ret = uart_add_one_port(&atmel_uart, &port->uart); + if (ret) + goto err_add_port; + + #ifdef CONFIG_SERIAL_ATMEL_CONSOLE + if (atmel_is_console_port(&port->uart) + && ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) { + /* + * The serial core enabled the clock for us, so undo + * the clk_enable() in atmel_console_setup() + */ + clk_disable(port->clk); + } + #endif + + device_init_wakeup(&pdev->dev, 1); + platform_set_drvdata(pdev, port); + ++ if (port->rs485.flags & SER_RS485_ENABLED) { ++ UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL); ++ UART_PUT_CR(&port->uart, ATMEL_US_RTSEN); ++ } ++ + return 0; + + err_add_port: + kfree(port->rx_ring.buf); + port->rx_ring.buf = NULL; + err_alloc_ring: + if (!atmel_is_console_port(&port->uart)) { + clk_put(port->clk); + port->clk = NULL; + } + + return ret; + } + + static int __devexit atmel_serial_remove(struct platform_device *pdev) + { + struct uart_port *port = platform_get_drvdata(pdev); + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + int ret = 0; + + device_init_wakeup(&pdev->dev, 0); + platform_set_drvdata(pdev, NULL); + + ret = uart_remove_one_port(&atmel_uart, port); + + tasklet_kill(&atmel_port->tasklet); + kfree(atmel_port->rx_ring.buf); + + /* "port" is allocated statically, so we shouldn't free it */ + + clk_put(atmel_port->clk); + + return ret; + } + + static struct platform_driver atmel_serial_driver = { + .probe = atmel_serial_probe, + .remove = __devexit_p(atmel_serial_remove), + .suspend = atmel_serial_suspend, + .resume = atmel_serial_resume, + .driver = { + .name = "atmel_usart", + .owner = THIS_MODULE, + }, + }; + + static int __init atmel_serial_init(void) + { + int ret; + + ret = uart_register_driver(&atmel_uart); + if (ret) + return ret; + + ret = platform_driver_register(&atmel_serial_driver); + if (ret) + uart_unregister_driver(&atmel_uart); + + return ret; + } + + static void __exit atmel_serial_exit(void) + { + platform_driver_unregister(&atmel_serial_driver); + uart_unregister_driver(&atmel_uart); + } + + module_init(atmel_serial_init); + module_exit(atmel_serial_exit); + + MODULE_AUTHOR("Rick Bronson"); + MODULE_DESCRIPTION("Atmel AT91 / AT32 serial port driver"); + MODULE_LICENSE("GPL"); + MODULE_ALIAS("platform:atmel_usart"); diff --cc drivers/tty/serial/samsung.c index 000000000000,7ac2bf5167cd..2335edafe903 mode 000000,100644..100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@@ -1,0 -1,1487 +1,1487 @@@ + /* linux/drivers/serial/samsuing.c + * + * Driver core for Samsung SoC onboard UARTs. + * + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + /* Hote on 2410 error handling + * + * The s3c2410 manual has a love/hate affair with the contents of the + * UERSTAT register in the UART blocks, and keeps marking some of the + * error bits as reserved. Having checked with the s3c2410x01, + * it copes with BREAKs properly, so I am happy to ignore the RESERVED + * feature from the latter versions of the manual. + * + * If it becomes aparrent that latter versions of the 2410 remove these + * bits, then action will have to be taken to differentiate the versions + * and change the policy on BREAK + * + * BJD, 04-Nov-2004 + */ + + #if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + #define SUPPORT_SYSRQ + #endif + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + + #include + #include + + #include + + #include "samsung.h" + + /* UART name and device definitions */ + + #define S3C24XX_SERIAL_NAME "ttySAC" + #define S3C24XX_SERIAL_MAJOR 204 + #define S3C24XX_SERIAL_MINOR 64 + + /* macros to change one thing to another */ + + #define tx_enabled(port) ((port)->unused[0]) + #define rx_enabled(port) ((port)->unused[1]) + + /* flag to ignore all characters comming in */ + #define RXSTAT_DUMMY_READ (0x10000000) + + static inline struct s3c24xx_uart_port *to_ourport(struct uart_port *port) + { + return container_of(port, struct s3c24xx_uart_port, port); + } + + /* translate a port to the device name */ + + static inline const char *s3c24xx_serial_portname(struct uart_port *port) + { + return to_platform_device(port->dev)->name; + } + + static int s3c24xx_serial_txempty_nofifo(struct uart_port *port) + { + return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE); + } + + static void s3c24xx_serial_rx_enable(struct uart_port *port) + { + unsigned long flags; + unsigned int ucon, ufcon; + int count = 10000; + + spin_lock_irqsave(&port->lock, flags); + + while (--count && !s3c24xx_serial_txempty_nofifo(port)) + udelay(100); + + ufcon = rd_regl(port, S3C2410_UFCON); + ufcon |= S3C2410_UFCON_RESETRX; + wr_regl(port, S3C2410_UFCON, ufcon); + + ucon = rd_regl(port, S3C2410_UCON); + ucon |= S3C2410_UCON_RXIRQMODE; + wr_regl(port, S3C2410_UCON, ucon); + + rx_enabled(port) = 1; + spin_unlock_irqrestore(&port->lock, flags); + } + + static void s3c24xx_serial_rx_disable(struct uart_port *port) + { + unsigned long flags; + unsigned int ucon; + + spin_lock_irqsave(&port->lock, flags); + + ucon = rd_regl(port, S3C2410_UCON); + ucon &= ~S3C2410_UCON_RXIRQMODE; + wr_regl(port, S3C2410_UCON, ucon); + + rx_enabled(port) = 0; + spin_unlock_irqrestore(&port->lock, flags); + } + + static void s3c24xx_serial_stop_tx(struct uart_port *port) + { + struct s3c24xx_uart_port *ourport = to_ourport(port); + + if (tx_enabled(port)) { + disable_irq_nosync(ourport->tx_irq); + tx_enabled(port) = 0; + if (port->flags & UPF_CONS_FLOW) + s3c24xx_serial_rx_enable(port); + } + } + + static void s3c24xx_serial_start_tx(struct uart_port *port) + { + struct s3c24xx_uart_port *ourport = to_ourport(port); + + if (!tx_enabled(port)) { + if (port->flags & UPF_CONS_FLOW) + s3c24xx_serial_rx_disable(port); + + enable_irq(ourport->tx_irq); + tx_enabled(port) = 1; + } + } + + + static void s3c24xx_serial_stop_rx(struct uart_port *port) + { + struct s3c24xx_uart_port *ourport = to_ourport(port); + + if (rx_enabled(port)) { + dbg("s3c24xx_serial_stop_rx: port=%p\n", port); + disable_irq_nosync(ourport->rx_irq); + rx_enabled(port) = 0; + } + } + + static void s3c24xx_serial_enable_ms(struct uart_port *port) + { + } + + static inline struct s3c24xx_uart_info *s3c24xx_port_to_info(struct uart_port *port) + { + return to_ourport(port)->info; + } + + static inline struct s3c2410_uartcfg *s3c24xx_port_to_cfg(struct uart_port *port) + { + if (port->dev == NULL) + return NULL; + + return (struct s3c2410_uartcfg *)port->dev->platform_data; + } + + static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport, + unsigned long ufstat) + { + struct s3c24xx_uart_info *info = ourport->info; + + if (ufstat & info->rx_fifofull) + return info->fifosize; + + return (ufstat & info->rx_fifomask) >> info->rx_fifoshift; + } + + + /* ? - where has parity gone?? */ + #define S3C2410_UERSTAT_PARITY (0x1000) + + static irqreturn_t + s3c24xx_serial_rx_chars(int irq, void *dev_id) + { + struct s3c24xx_uart_port *ourport = dev_id; + struct uart_port *port = &ourport->port; + struct tty_struct *tty = port->state->port.tty; + unsigned int ufcon, ch, flag, ufstat, uerstat; + int max_count = 64; + + while (max_count-- > 0) { + ufcon = rd_regl(port, S3C2410_UFCON); + ufstat = rd_regl(port, S3C2410_UFSTAT); + + if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0) + break; + + uerstat = rd_regl(port, S3C2410_UERSTAT); + ch = rd_regb(port, S3C2410_URXH); + + if (port->flags & UPF_CONS_FLOW) { + int txe = s3c24xx_serial_txempty_nofifo(port); + + if (rx_enabled(port)) { + if (!txe) { + rx_enabled(port) = 0; + continue; + } + } else { + if (txe) { + ufcon |= S3C2410_UFCON_RESETRX; + wr_regl(port, S3C2410_UFCON, ufcon); + rx_enabled(port) = 1; + goto out; + } + continue; + } + } + + /* insert the character into the buffer */ + + flag = TTY_NORMAL; + port->icount.rx++; + + if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) { + dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n", + ch, uerstat); + + /* check for break */ + if (uerstat & S3C2410_UERSTAT_BREAK) { + dbg("break!\n"); + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore_char; + } + + if (uerstat & S3C2410_UERSTAT_FRAME) + port->icount.frame++; + if (uerstat & S3C2410_UERSTAT_OVERRUN) + port->icount.overrun++; + + uerstat &= port->read_status_mask; + + if (uerstat & S3C2410_UERSTAT_BREAK) + flag = TTY_BREAK; + else if (uerstat & S3C2410_UERSTAT_PARITY) + flag = TTY_PARITY; + else if (uerstat & (S3C2410_UERSTAT_FRAME | + S3C2410_UERSTAT_OVERRUN)) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, + ch, flag); + + ignore_char: + continue; + } + tty_flip_buffer_push(tty); + + out: + return IRQ_HANDLED; + } + + static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) + { + struct s3c24xx_uart_port *ourport = id; + struct uart_port *port = &ourport->port; + struct circ_buf *xmit = &port->state->xmit; + int count = 256; + + if (port->x_char) { + wr_regb(port, S3C2410_UTXH, port->x_char); + port->icount.tx++; + port->x_char = 0; + goto out; + } + + /* if there isnt anything more to transmit, or the uart is now + * stopped, disable the uart and exit + */ + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + s3c24xx_serial_stop_tx(port); + goto out; + } + + /* try and drain the buffer... */ + + while (!uart_circ_empty(xmit) && count-- > 0) { + if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull) + break; + + wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + s3c24xx_serial_stop_tx(port); + + out: + return IRQ_HANDLED; + } + + static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port) + { + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT); + unsigned long ufcon = rd_regl(port, S3C2410_UFCON); + + if (ufcon & S3C2410_UFCON_FIFOMODE) { + if ((ufstat & info->tx_fifomask) != 0 || + (ufstat & info->tx_fifofull)) + return 0; + + return 1; + } + + return s3c24xx_serial_txempty_nofifo(port); + } + + /* no modem control lines */ + static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port) + { + unsigned int umstat = rd_regb(port, S3C2410_UMSTAT); + + if (umstat & S3C2410_UMSTAT_CTS) + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; + else + return TIOCM_CAR | TIOCM_DSR; + } + + static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl) + { + /* todo - possibly remove AFC and do manual CTS */ + } + + static void s3c24xx_serial_break_ctl(struct uart_port *port, int break_state) + { + unsigned long flags; + unsigned int ucon; + + spin_lock_irqsave(&port->lock, flags); + + ucon = rd_regl(port, S3C2410_UCON); + + if (break_state) + ucon |= S3C2410_UCON_SBREAK; + else + ucon &= ~S3C2410_UCON_SBREAK; + + wr_regl(port, S3C2410_UCON, ucon); + + spin_unlock_irqrestore(&port->lock, flags); + } + + static void s3c24xx_serial_shutdown(struct uart_port *port) + { + struct s3c24xx_uart_port *ourport = to_ourport(port); + + if (ourport->tx_claimed) { + free_irq(ourport->tx_irq, ourport); + tx_enabled(port) = 0; + ourport->tx_claimed = 0; + } + + if (ourport->rx_claimed) { + free_irq(ourport->rx_irq, ourport); + ourport->rx_claimed = 0; + rx_enabled(port) = 0; + } + } + + + static int s3c24xx_serial_startup(struct uart_port *port) + { + struct s3c24xx_uart_port *ourport = to_ourport(port); + int ret; + + dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n", + port->mapbase, port->membase); + + rx_enabled(port) = 1; + + ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0, + s3c24xx_serial_portname(port), ourport); + + if (ret != 0) { + printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq); + return ret; + } + + ourport->rx_claimed = 1; + + dbg("requesting tx irq...\n"); + + tx_enabled(port) = 1; + + ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, + s3c24xx_serial_portname(port), ourport); + + if (ret) { + printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq); + goto err; + } + + ourport->tx_claimed = 1; + + dbg("s3c24xx_serial_startup ok\n"); + + /* the port reset code should have done the correct + * register setup for the port controls */ + + return ret; + + err: + s3c24xx_serial_shutdown(port); + return ret; + } + + /* power power management control */ + + static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level, + unsigned int old) + { + struct s3c24xx_uart_port *ourport = to_ourport(port); + + ourport->pm_level = level; + + switch (level) { + case 3: + if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) + clk_disable(ourport->baudclk); + + clk_disable(ourport->clk); + break; + + case 0: + clk_enable(ourport->clk); + + if (!IS_ERR(ourport->baudclk) && ourport->baudclk != NULL) + clk_enable(ourport->baudclk); + + break; + default: + printk(KERN_ERR "s3c24xx_serial: unknown pm %d\n", level); + } + } + + /* baud rate calculation + * + * The UARTs on the S3C2410/S3C2440 can take their clocks from a number + * of different sources, including the peripheral clock ("pclk") and an + * external clock ("uclk"). The S3C2440 also adds the core clock ("fclk") + * with a programmable extra divisor. + * + * The following code goes through the clock sources, and calculates the + * baud clocks (and the resultant actual baud rates) and then tries to + * pick the closest one and select that. + * + */ + + + #define MAX_CLKS (8) + + static struct s3c24xx_uart_clksrc tmp_clksrc = { + .name = "pclk", + .min_baud = 0, + .max_baud = 0, + .divisor = 1, + }; + + static inline int + s3c24xx_serial_getsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c) + { + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + + return (info->get_clksrc)(port, c); + } + + static inline int + s3c24xx_serial_setsource(struct uart_port *port, struct s3c24xx_uart_clksrc *c) + { + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + + return (info->set_clksrc)(port, c); + } + + struct baud_calc { + struct s3c24xx_uart_clksrc *clksrc; + unsigned int calc; + unsigned int divslot; + unsigned int quot; + struct clk *src; + }; + + static int s3c24xx_serial_calcbaud(struct baud_calc *calc, + struct uart_port *port, + struct s3c24xx_uart_clksrc *clksrc, + unsigned int baud) + { + struct s3c24xx_uart_port *ourport = to_ourport(port); + unsigned long rate; + + calc->src = clk_get(port->dev, clksrc->name); + if (calc->src == NULL || IS_ERR(calc->src)) + return 0; + + rate = clk_get_rate(calc->src); + rate /= clksrc->divisor; + + calc->clksrc = clksrc; + + if (ourport->info->has_divslot) { + unsigned long div = rate / baud; + + /* The UDIVSLOT register on the newer UARTs allows us to + * get a divisor adjustment of 1/16th on the baud clock. + * + * We don't keep the UDIVSLOT value (the 16ths we calculated + * by not multiplying the baud by 16) as it is easy enough + * to recalculate. + */ + + calc->quot = div / 16; + calc->calc = rate / div; + } else { + calc->quot = (rate + (8 * baud)) / (16 * baud); + calc->calc = (rate / (calc->quot * 16)); + } + + calc->quot--; + return 1; + } + + static unsigned int s3c24xx_serial_getclk(struct uart_port *port, + struct s3c24xx_uart_clksrc **clksrc, + struct clk **clk, + unsigned int baud) + { + struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); + struct s3c24xx_uart_clksrc *clkp; + struct baud_calc res[MAX_CLKS]; + struct baud_calc *resptr, *best, *sptr; + int i; + + clkp = cfg->clocks; + best = NULL; + + if (cfg->clocks_size < 2) { + if (cfg->clocks_size == 0) + clkp = &tmp_clksrc; + + /* check to see if we're sourcing fclk, and if so we're + * going to have to update the clock source + */ + + if (strcmp(clkp->name, "fclk") == 0) { + struct s3c24xx_uart_clksrc src; + + s3c24xx_serial_getsource(port, &src); + + /* check that the port already using fclk, and if + * not, then re-select fclk + */ + + if (strcmp(src.name, clkp->name) == 0) { + s3c24xx_serial_setsource(port, clkp); + s3c24xx_serial_getsource(port, &src); + } + + clkp->divisor = src.divisor; + } + + s3c24xx_serial_calcbaud(res, port, clkp, baud); + best = res; + resptr = best + 1; + } else { + resptr = res; + + for (i = 0; i < cfg->clocks_size; i++, clkp++) { + if (s3c24xx_serial_calcbaud(resptr, port, clkp, baud)) + resptr++; + } + } + + /* ok, we now need to select the best clock we found */ + + if (!best) { + unsigned int deviation = (1<<30)|((1<<30)-1); + int calc_deviation; + + for (sptr = res; sptr < resptr; sptr++) { + calc_deviation = baud - sptr->calc; + if (calc_deviation < 0) + calc_deviation = -calc_deviation; + + if (calc_deviation < deviation) { + best = sptr; + deviation = calc_deviation; + } + } + } + + /* store results to pass back */ + + *clksrc = best->clksrc; + *clk = best->src; + + return best->quot; + } + + /* udivslot_table[] + * + * This table takes the fractional value of the baud divisor and gives + * the recommended setting for the UDIVSLOT register. + */ + static u16 udivslot_table[16] = { + [0] = 0x0000, + [1] = 0x0080, + [2] = 0x0808, + [3] = 0x0888, + [4] = 0x2222, + [5] = 0x4924, + [6] = 0x4A52, + [7] = 0x54AA, + [8] = 0x5555, + [9] = 0xD555, + [10] = 0xD5D5, + [11] = 0xDDD5, + [12] = 0xDDDD, + [13] = 0xDFDD, + [14] = 0xDFDF, + [15] = 0xFFDF, + }; + + static void s3c24xx_serial_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) + { + struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port); + struct s3c24xx_uart_port *ourport = to_ourport(port); + struct s3c24xx_uart_clksrc *clksrc = NULL; + struct clk *clk = NULL; + unsigned long flags; + unsigned int baud, quot; + unsigned int ulcon; + unsigned int umcon; + unsigned int udivslot = 0; + + /* + * We don't support modem control lines. + */ + termios->c_cflag &= ~(HUPCL | CMSPAR); + termios->c_cflag |= CLOCAL; + + /* + * Ask the core to calculate the divisor for us. + */ + + baud = uart_get_baud_rate(port, termios, old, 0, 115200*8); + + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) + quot = port->custom_divisor; + else + quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud); + + /* check to see if we need to change clock source */ + + if (ourport->clksrc != clksrc || ourport->baudclk != clk) { + dbg("selecting clock %p\n", clk); + s3c24xx_serial_setsource(port, clksrc); + + if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) { + clk_disable(ourport->baudclk); + ourport->baudclk = NULL; + } + + clk_enable(clk); + + ourport->clksrc = clksrc; + ourport->baudclk = clk; + ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; + } + + if (ourport->info->has_divslot) { + unsigned int div = ourport->baudclk_rate / baud; + + if (cfg->has_fracval) { + udivslot = (div & 15); + dbg("fracval = %04x\n", udivslot); + } else { + udivslot = udivslot_table[div & 15]; + dbg("udivslot = %04x (div %d)\n", udivslot, div & 15); + } + } + + switch (termios->c_cflag & CSIZE) { + case CS5: + dbg("config: 5bits/char\n"); + ulcon = S3C2410_LCON_CS5; + break; + case CS6: + dbg("config: 6bits/char\n"); + ulcon = S3C2410_LCON_CS6; + break; + case CS7: + dbg("config: 7bits/char\n"); + ulcon = S3C2410_LCON_CS7; + break; + case CS8: + default: + dbg("config: 8bits/char\n"); + ulcon = S3C2410_LCON_CS8; + break; + } + + /* preserve original lcon IR settings */ + ulcon |= (cfg->ulcon & S3C2410_LCON_IRM); + + if (termios->c_cflag & CSTOPB) + ulcon |= S3C2410_LCON_STOPB; + + umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0; + + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & PARODD) + ulcon |= S3C2410_LCON_PODD; + else + ulcon |= S3C2410_LCON_PEVEN; + } else { + ulcon |= S3C2410_LCON_PNONE; + } + + spin_lock_irqsave(&port->lock, flags); + + dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n", + ulcon, quot, udivslot); + + wr_regl(port, S3C2410_ULCON, ulcon); + wr_regl(port, S3C2410_UBRDIV, quot); + wr_regl(port, S3C2410_UMCON, umcon); + + if (ourport->info->has_divslot) + wr_regl(port, S3C2443_DIVSLOT, udivslot); + + dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n", + rd_regl(port, S3C2410_ULCON), + rd_regl(port, S3C2410_UCON), + rd_regl(port, S3C2410_UFCON)); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + /* + * Which character status flags are we interested in? + */ + port->read_status_mask = S3C2410_UERSTAT_OVERRUN; + if (termios->c_iflag & INPCK) + port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY; + + /* + * Which character status flags should we ignore? + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN; + if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR) + port->ignore_status_mask |= S3C2410_UERSTAT_FRAME; + + /* + * Ignore all characters if CREAD is not set. + */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= RXSTAT_DUMMY_READ; + + spin_unlock_irqrestore(&port->lock, flags); + } + + static const char *s3c24xx_serial_type(struct uart_port *port) + { + switch (port->type) { + case PORT_S3C2410: + return "S3C2410"; + case PORT_S3C2440: + return "S3C2440"; + case PORT_S3C2412: + return "S3C2412"; + case PORT_S3C6400: + return "S3C6400/10"; + default: + return NULL; + } + } + + #define MAP_SIZE (0x100) + + static void s3c24xx_serial_release_port(struct uart_port *port) + { + release_mem_region(port->mapbase, MAP_SIZE); + } + + static int s3c24xx_serial_request_port(struct uart_port *port) + { + const char *name = s3c24xx_serial_portname(port); + return request_mem_region(port->mapbase, MAP_SIZE, name) ? 0 : -EBUSY; + } + + static void s3c24xx_serial_config_port(struct uart_port *port, int flags) + { + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + + if (flags & UART_CONFIG_TYPE && + s3c24xx_serial_request_port(port) == 0) + port->type = info->type; + } + + /* + * verify the new serial_struct (for TIOCSSERIAL). + */ + static int + s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser) + { + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + + if (ser->type != PORT_UNKNOWN && ser->type != info->type) + return -EINVAL; + + return 0; + } + + + #ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE + + static struct console s3c24xx_serial_console; + + #define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console + #else + #define S3C24XX_SERIAL_CONSOLE NULL + #endif + + static struct uart_ops s3c24xx_serial_ops = { + .pm = s3c24xx_serial_pm, + .tx_empty = s3c24xx_serial_tx_empty, + .get_mctrl = s3c24xx_serial_get_mctrl, + .set_mctrl = s3c24xx_serial_set_mctrl, + .stop_tx = s3c24xx_serial_stop_tx, + .start_tx = s3c24xx_serial_start_tx, + .stop_rx = s3c24xx_serial_stop_rx, + .enable_ms = s3c24xx_serial_enable_ms, + .break_ctl = s3c24xx_serial_break_ctl, + .startup = s3c24xx_serial_startup, + .shutdown = s3c24xx_serial_shutdown, + .set_termios = s3c24xx_serial_set_termios, + .type = s3c24xx_serial_type, + .release_port = s3c24xx_serial_release_port, + .request_port = s3c24xx_serial_request_port, + .config_port = s3c24xx_serial_config_port, + .verify_port = s3c24xx_serial_verify_port, + }; + + + static struct uart_driver s3c24xx_uart_drv = { + .owner = THIS_MODULE, - .dev_name = "s3c2410_serial", ++ .driver_name = "s3c2410_serial", + .nr = CONFIG_SERIAL_SAMSUNG_UARTS, + .cons = S3C24XX_SERIAL_CONSOLE, - .driver_name = S3C24XX_SERIAL_NAME, ++ .dev_name = S3C24XX_SERIAL_NAME, + .major = S3C24XX_SERIAL_MAJOR, + .minor = S3C24XX_SERIAL_MINOR, + }; + + static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = { + [0] = { + .port = { + .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock), + .iotype = UPIO_MEM, + .irq = IRQ_S3CUART_RX0, + .uartclk = 0, + .fifosize = 16, + .ops = &s3c24xx_serial_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 0, + } + }, + [1] = { + .port = { + .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock), + .iotype = UPIO_MEM, + .irq = IRQ_S3CUART_RX1, + .uartclk = 0, + .fifosize = 16, + .ops = &s3c24xx_serial_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 1, + } + }, + #if CONFIG_SERIAL_SAMSUNG_UARTS > 2 + + [2] = { + .port = { + .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock), + .iotype = UPIO_MEM, + .irq = IRQ_S3CUART_RX2, + .uartclk = 0, + .fifosize = 16, + .ops = &s3c24xx_serial_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 2, + } + }, + #endif + #if CONFIG_SERIAL_SAMSUNG_UARTS > 3 + [3] = { + .port = { + .lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock), + .iotype = UPIO_MEM, + .irq = IRQ_S3CUART_RX3, + .uartclk = 0, + .fifosize = 16, + .ops = &s3c24xx_serial_ops, + .flags = UPF_BOOT_AUTOCONF, + .line = 3, + } + } + #endif + }; + + /* s3c24xx_serial_resetport + * + * wrapper to call the specific reset for this port (reset the fifos + * and the settings) + */ + + static inline int s3c24xx_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) + { + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + + return (info->reset_port)(port, cfg); + } + + + #ifdef CONFIG_CPU_FREQ + + static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) + { + struct s3c24xx_uart_port *port; + struct uart_port *uport; + + port = container_of(nb, struct s3c24xx_uart_port, freq_transition); + uport = &port->port; + + /* check to see if port is enabled */ + + if (port->pm_level != 0) + return 0; + + /* try and work out if the baudrate is changing, we can detect + * a change in rate, but we do not have support for detecting + * a disturbance in the clock-rate over the change. + */ + + if (IS_ERR(port->clk)) + goto exit; + + if (port->baudclk_rate == clk_get_rate(port->clk)) + goto exit; + + if (val == CPUFREQ_PRECHANGE) { + /* we should really shut the port down whilst the + * frequency change is in progress. */ + + } else if (val == CPUFREQ_POSTCHANGE) { + struct ktermios *termios; + struct tty_struct *tty; + + if (uport->state == NULL) + goto exit; + + tty = uport->state->port.tty; + + if (tty == NULL) + goto exit; + + termios = tty->termios; + + if (termios == NULL) { + printk(KERN_WARNING "%s: no termios?\n", __func__); + goto exit; + } + + s3c24xx_serial_set_termios(uport, termios, NULL); + } + + exit: + return 0; + } + + static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) + { + port->freq_transition.notifier_call = s3c24xx_serial_cpufreq_transition; + + return cpufreq_register_notifier(&port->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); + } + + static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) + { + cpufreq_unregister_notifier(&port->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); + } + + #else + static inline int s3c24xx_serial_cpufreq_register(struct s3c24xx_uart_port *port) + { + return 0; + } + + static inline void s3c24xx_serial_cpufreq_deregister(struct s3c24xx_uart_port *port) + { + } + #endif + + /* s3c24xx_serial_init_port + * + * initialise a single serial port from the platform device given + */ + + static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, + struct s3c24xx_uart_info *info, + struct platform_device *platdev) + { + struct uart_port *port = &ourport->port; + struct s3c2410_uartcfg *cfg; + struct resource *res; + int ret; + + dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev); + + if (platdev == NULL) + return -ENODEV; + + cfg = s3c24xx_dev_to_cfg(&platdev->dev); + + if (port->mapbase != 0) + return 0; + + if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) { + printk(KERN_ERR "%s: port %d bigger than %d\n", __func__, + cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS); + return -ERANGE; + } + + /* setup info for port */ + port->dev = &platdev->dev; + ourport->info = info; + + /* copy the info in from provided structure */ + ourport->port.fifosize = info->fifosize; + + dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport); + + port->uartclk = 1; + + if (cfg->uart_flags & UPF_CONS_FLOW) { + dbg("s3c24xx_serial_init_port: enabling flow control\n"); + port->flags |= UPF_CONS_FLOW; + } + + /* sort our the physical and virtual addresses for each UART */ + + res = platform_get_resource(platdev, IORESOURCE_MEM, 0); + if (res == NULL) { + printk(KERN_ERR "failed to find memory resource for uart\n"); + return -EINVAL; + } + + dbg("resource %p (%lx..%lx)\n", res, res->start, res->end); + + port->mapbase = res->start; + port->membase = S3C_VA_UART + (res->start & 0xfffff); + ret = platform_get_irq(platdev, 0); + if (ret < 0) + port->irq = 0; + else { + port->irq = ret; + ourport->rx_irq = ret; + ourport->tx_irq = ret + 1; + } + + ret = platform_get_irq(platdev, 1); + if (ret > 0) + ourport->tx_irq = ret; + + ourport->clk = clk_get(&platdev->dev, "uart"); + + dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n", + port->mapbase, port->membase, port->irq, + ourport->rx_irq, ourport->tx_irq, port->uartclk); + + /* reset the fifos (and setup the uart) */ + s3c24xx_serial_resetport(port, cfg); + return 0; + } + + static ssize_t s3c24xx_serial_show_clksrc(struct device *dev, + struct device_attribute *attr, + char *buf) + { + struct uart_port *port = s3c24xx_dev_to_port(dev); + struct s3c24xx_uart_port *ourport = to_ourport(port); + + return snprintf(buf, PAGE_SIZE, "* %s\n", ourport->clksrc->name); + } + + static DEVICE_ATTR(clock_source, S_IRUGO, s3c24xx_serial_show_clksrc, NULL); + + /* Device driver serial port probe */ + + static int probe_index; + + int s3c24xx_serial_probe(struct platform_device *dev, + struct s3c24xx_uart_info *info) + { + struct s3c24xx_uart_port *ourport; + int ret; + + dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index); + + ourport = &s3c24xx_serial_ports[probe_index]; + probe_index++; + + dbg("%s: initialising port %p...\n", __func__, ourport); + + ret = s3c24xx_serial_init_port(ourport, info, dev); + if (ret < 0) + goto probe_err; + + dbg("%s: adding port\n", __func__); + uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); + platform_set_drvdata(dev, &ourport->port); + + ret = device_create_file(&dev->dev, &dev_attr_clock_source); + if (ret < 0) + printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__); + + ret = s3c24xx_serial_cpufreq_register(ourport); + if (ret < 0) + dev_err(&dev->dev, "failed to add cpufreq notifier\n"); + + return 0; + + probe_err: + return ret; + } + + EXPORT_SYMBOL_GPL(s3c24xx_serial_probe); + + int __devexit s3c24xx_serial_remove(struct platform_device *dev) + { + struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); + + if (port) { + s3c24xx_serial_cpufreq_deregister(to_ourport(port)); + device_remove_file(&dev->dev, &dev_attr_clock_source); + uart_remove_one_port(&s3c24xx_uart_drv, port); + } + + return 0; + } + + EXPORT_SYMBOL_GPL(s3c24xx_serial_remove); + + /* UART power management code */ + + #ifdef CONFIG_PM + + static int s3c24xx_serial_suspend(struct platform_device *dev, pm_message_t state) + { + struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); + + if (port) + uart_suspend_port(&s3c24xx_uart_drv, port); + + return 0; + } + + static int s3c24xx_serial_resume(struct platform_device *dev) + { + struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); + struct s3c24xx_uart_port *ourport = to_ourport(port); + + if (port) { + clk_enable(ourport->clk); + s3c24xx_serial_resetport(port, s3c24xx_port_to_cfg(port)); + clk_disable(ourport->clk); + + uart_resume_port(&s3c24xx_uart_drv, port); + } + + return 0; + } + #endif + + int s3c24xx_serial_init(struct platform_driver *drv, + struct s3c24xx_uart_info *info) + { + dbg("s3c24xx_serial_init(%p,%p)\n", drv, info); + + #ifdef CONFIG_PM + drv->suspend = s3c24xx_serial_suspend; + drv->resume = s3c24xx_serial_resume; + #endif + + return platform_driver_register(drv); + } + + EXPORT_SYMBOL_GPL(s3c24xx_serial_init); + + /* module initialisation code */ + + static int __init s3c24xx_serial_modinit(void) + { + int ret; + + ret = uart_register_driver(&s3c24xx_uart_drv); + if (ret < 0) { + printk(KERN_ERR "failed to register UART driver\n"); + return -1; + } + + return 0; + } + + static void __exit s3c24xx_serial_modexit(void) + { + uart_unregister_driver(&s3c24xx_uart_drv); + } + + module_init(s3c24xx_serial_modinit); + module_exit(s3c24xx_serial_modexit); + + /* Console code */ + + #ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE + + static struct uart_port *cons_uart; + + static int + s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon) + { + struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port); + unsigned long ufstat, utrstat; + + if (ufcon & S3C2410_UFCON_FIFOMODE) { + /* fifo mode - check amount of data in fifo registers... */ + + ufstat = rd_regl(port, S3C2410_UFSTAT); + return (ufstat & info->tx_fifofull) ? 0 : 1; + } + + /* in non-fifo mode, we go and use the tx buffer empty */ + + utrstat = rd_regl(port, S3C2410_UTRSTAT); + return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0; + } + + static void + s3c24xx_serial_console_putchar(struct uart_port *port, int ch) + { + unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON); + while (!s3c24xx_serial_console_txrdy(port, ufcon)) + barrier(); + wr_regb(cons_uart, S3C2410_UTXH, ch); + } + + static void + s3c24xx_serial_console_write(struct console *co, const char *s, + unsigned int count) + { + uart_console_write(cons_uart, s, count, s3c24xx_serial_console_putchar); + } + + static void __init + s3c24xx_serial_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) + { + struct s3c24xx_uart_clksrc clksrc; + struct clk *clk; + unsigned int ulcon; + unsigned int ucon; + unsigned int ubrdiv; + unsigned long rate; + + ulcon = rd_regl(port, S3C2410_ULCON); + ucon = rd_regl(port, S3C2410_UCON); + ubrdiv = rd_regl(port, S3C2410_UBRDIV); + + dbg("s3c24xx_serial_get_options: port=%p\n" + "registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n", + port, ulcon, ucon, ubrdiv); + + if ((ucon & 0xf) != 0) { + /* consider the serial port configured if the tx/rx mode set */ + + switch (ulcon & S3C2410_LCON_CSMASK) { + case S3C2410_LCON_CS5: + *bits = 5; + break; + case S3C2410_LCON_CS6: + *bits = 6; + break; + case S3C2410_LCON_CS7: + *bits = 7; + break; + default: + case S3C2410_LCON_CS8: + *bits = 8; + break; + } + + switch (ulcon & S3C2410_LCON_PMASK) { + case S3C2410_LCON_PEVEN: + *parity = 'e'; + break; + + case S3C2410_LCON_PODD: + *parity = 'o'; + break; + + case S3C2410_LCON_PNONE: + default: + *parity = 'n'; + } + + /* now calculate the baud rate */ + + s3c24xx_serial_getsource(port, &clksrc); + + clk = clk_get(port->dev, clksrc.name); + if (!IS_ERR(clk) && clk != NULL) + rate = clk_get_rate(clk) / clksrc.divisor; + else + rate = 1; + + + *baud = rate / (16 * (ubrdiv + 1)); + dbg("calculated baud %d\n", *baud); + } + + } + + /* s3c24xx_serial_init_ports + * + * initialise the serial ports from the machine provided initialisation + * data. + */ + + static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info **info) + { + struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports; + struct platform_device **platdev_ptr; + int i; + + dbg("s3c24xx_serial_init_ports: initialising ports...\n"); + + platdev_ptr = s3c24xx_uart_devs; + + for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) { + s3c24xx_serial_init_port(ptr, info[i], *platdev_ptr); + } + + return 0; + } + + static int __init + s3c24xx_serial_console_setup(struct console *co, char *options) + { + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + dbg("s3c24xx_serial_console_setup: co=%p (%d), %s\n", + co, co->index, options); + + /* is this a valid port */ + + if (co->index == -1 || co->index >= CONFIG_SERIAL_SAMSUNG_UARTS) + co->index = 0; + + port = &s3c24xx_serial_ports[co->index].port; + + /* is the port configured? */ + + if (port->mapbase == 0x0) { + co->index = 0; + port = &s3c24xx_serial_ports[co->index].port; + } + + cons_uart = port; + + dbg("s3c24xx_serial_console_setup: port=%p (%d)\n", port, co->index); + + /* + * 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); + else + s3c24xx_serial_get_options(port, &baud, &parity, &bits); + + dbg("s3c24xx_serial_console_setup: baud %d\n", baud); + + return uart_set_options(port, co, baud, parity, bits, flow); + } + + /* s3c24xx_serial_initconsole + * + * initialise the console from one of the uart drivers + */ + + static struct console s3c24xx_serial_console = { + .name = S3C24XX_SERIAL_NAME, + .device = uart_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, + .write = s3c24xx_serial_console_write, + .setup = s3c24xx_serial_console_setup + }; + + int s3c24xx_serial_initconsole(struct platform_driver *drv, + struct s3c24xx_uart_info **info) + + { + struct platform_device *dev = s3c24xx_uart_devs[0]; + + dbg("s3c24xx_serial_initconsole\n"); + + /* select driver based on the cpu */ + + if (dev == NULL) { + printk(KERN_ERR "s3c24xx: no devices for console init\n"); + return 0; + } + + if (strcmp(dev->name, drv->driver.name) != 0) + return 0; + + s3c24xx_serial_console.data = &s3c24xx_uart_drv; + s3c24xx_serial_init_ports(info); + + register_console(&s3c24xx_serial_console); + return 0; + } + + #endif /* CONFIG_SERIAL_SAMSUNG_CONSOLE */ + + MODULE_DESCRIPTION("Samsung SoC Serial port driver"); + MODULE_AUTHOR("Ben Dooks "); + MODULE_LICENSE("GPL v2"); diff --cc drivers/tty/serial/sh-sci.c index 000000000000,c291b3add1d2..92c91c83edde mode 000000,100644..100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@@ -1,0 -1,2027 +1,2079 @@@ + /* + * drivers/serial/sh-sci.c + * + * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) + * - * Copyright (C) 2002 - 2008 Paul Mundt ++ * Copyright (C) 2002 - 2011 Paul Mundt + * Modified to support SH7720 SCIF. Markus Brunner, Mark Jonas (Jul 2007). + * + * based off of the old drivers/char/sh-sci.c by: + * + * Copyright (C) 1999, 2000 Niibe Yutaka + * Copyright (C) 2000 Sugioka Toshinobu + * Modified to support multiple serial ports. Stuart Menefy (May 2000). + * Modified to support SecureEdge. David McCullough (2002) + * Modified to support SH7300 SCIF. Takashi Kusuda (Jun 2003). + * Removed SH7300 support (Jul 2007). + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + #if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + #define SUPPORT_SYSRQ + #endif + + #undef DEBUG + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifdef CONFIG_SUPERH + #include + #endif + + #ifdef CONFIG_H8300 + #include + #endif + + #include "sh-sci.h" + + struct sci_port { + struct uart_port port; + + /* Port type */ + unsigned int type; + + /* Port IRQs: ERI, RXI, TXI, BRI (optional) */ + unsigned int irqs[SCIx_NR_IRQS]; + + /* Port enable callback */ + void (*enable)(struct uart_port *port); + + /* Port disable callback */ + void (*disable)(struct uart_port *port); + + /* Break timer */ + struct timer_list break_timer; + int break_flag; + ++ /* SCSCR initialization */ ++ unsigned int scscr; ++ ++ /* SCBRR calculation algo */ ++ unsigned int scbrr_algo_id; ++ + /* Interface clock */ + struct clk *iclk; + /* Function clock */ + struct clk *fclk; + + struct list_head node; ++ + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; ++ + #ifdef CONFIG_SERIAL_SH_SCI_DMA + struct device *dma_dev; + unsigned int slave_tx; + unsigned int slave_rx; + struct dma_async_tx_descriptor *desc_tx; + struct dma_async_tx_descriptor *desc_rx[2]; + dma_cookie_t cookie_tx; + dma_cookie_t cookie_rx[2]; + dma_cookie_t active_rx; + struct scatterlist sg_tx; + unsigned int sg_len_tx; + struct scatterlist sg_rx[2]; + size_t buf_len_rx; + struct sh_dmae_slave param_tx; + struct sh_dmae_slave param_rx; + struct work_struct work_tx; + struct work_struct work_rx; + struct timer_list rx_timer; + unsigned int rx_timeout; + #endif + }; + + struct sh_sci_priv { + spinlock_t lock; + struct list_head ports; + struct notifier_block clk_nb; + }; + + /* Function prototypes */ + static void sci_stop_tx(struct uart_port *port); + + #define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS + + static struct sci_port sci_ports[SCI_NPORTS]; + static struct uart_driver sci_uart_driver; + + static inline struct sci_port * + to_sci_port(struct uart_port *uart) + { + return container_of(uart, struct sci_port, port); + } + + #if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) + + #ifdef CONFIG_CONSOLE_POLL + static inline void handle_error(struct uart_port *port) + { + /* Clear error flags */ + sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); + } + + static int sci_poll_get_char(struct uart_port *port) + { + unsigned short status; + int c; + + do { + status = sci_in(port, SCxSR); + if (status & SCxSR_ERRORS(port)) { + handle_error(port); + continue; + } + break; + } while (1); + + if (!(status & SCxSR_RDxF(port))) + return NO_POLL_CHAR; + + c = sci_in(port, SCxRDR); + + /* Dummy read */ + sci_in(port, SCxSR); + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + + return c; + } + #endif + + static void sci_poll_put_char(struct uart_port *port, unsigned char c) + { + unsigned short status; + + do { + status = sci_in(port, SCxSR); + } while (!(status & SCxSR_TDxE(port))); + + sci_out(port, SCxTDR, c); + sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); + } + #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ + + #if defined(__H8300H__) || defined(__H8300S__) + static void sci_init_pins(struct uart_port *port, unsigned int cflag) + { + int ch = (port->mapbase - SMR0) >> 3; + + /* set DDR regs */ + H8300_GPIO_DDR(h8300_sci_pins[ch].port, + h8300_sci_pins[ch].rx, + H8300_GPIO_INPUT); + H8300_GPIO_DDR(h8300_sci_pins[ch].port, + h8300_sci_pins[ch].tx, + H8300_GPIO_OUTPUT); + + /* tx mark output*/ + H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx; + } + #elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) + static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) + { + if (port->mapbase == 0xA4400000) { + __raw_writew(__raw_readw(PACR) & 0xffc0, PACR); + __raw_writew(__raw_readw(PBCR) & 0x0fff, PBCR); + } else if (port->mapbase == 0xA4410000) + __raw_writew(__raw_readw(PBCR) & 0xf003, PBCR); + } + #elif defined(CONFIG_CPU_SUBTYPE_SH7720) || defined(CONFIG_CPU_SUBTYPE_SH7721) + static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) + { + unsigned short data; + + if (cflag & CRTSCTS) { + /* enable RTS/CTS */ + if (port->mapbase == 0xa4430000) { /* SCIF0 */ + /* Clear PTCR bit 9-2; enable all scif pins but sck */ + data = __raw_readw(PORT_PTCR); + __raw_writew((data & 0xfc03), PORT_PTCR); + } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ + /* Clear PVCR bit 9-2 */ + data = __raw_readw(PORT_PVCR); + __raw_writew((data & 0xfc03), PORT_PVCR); + } + } else { + if (port->mapbase == 0xa4430000) { /* SCIF0 */ + /* Clear PTCR bit 5-2; enable only tx and rx */ + data = __raw_readw(PORT_PTCR); + __raw_writew((data & 0xffc3), PORT_PTCR); + } else if (port->mapbase == 0xa4438000) { /* SCIF1 */ + /* Clear PVCR bit 5-2 */ + data = __raw_readw(PORT_PVCR); + __raw_writew((data & 0xffc3), PORT_PVCR); + } + } + } + #elif defined(CONFIG_CPU_SH3) + /* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */ + static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) + { + unsigned short data; + + /* We need to set SCPCR to enable RTS/CTS */ + data = __raw_readw(SCPCR); + /* Clear out SCP7MD1,0, SCP6MD1,0, SCP4MD1,0*/ + __raw_writew(data & 0x0fcf, SCPCR); + + if (!(cflag & CRTSCTS)) { + /* We need to set SCPCR to enable RTS/CTS */ + data = __raw_readw(SCPCR); + /* Clear out SCP7MD1,0, SCP4MD1,0, + Set SCP6MD1,0 = {01} (output) */ + __raw_writew((data & 0x0fcf) | 0x1000, SCPCR); + + data = __raw_readb(SCPDR); + /* Set /RTS2 (bit6) = 0 */ + __raw_writeb(data & 0xbf, SCPDR); + } + } + #elif defined(CONFIG_CPU_SUBTYPE_SH7722) + static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) + { + unsigned short data; + + if (port->mapbase == 0xffe00000) { + data = __raw_readw(PSCR); + data &= ~0x03cf; + if (!(cflag & CRTSCTS)) + data |= 0x0340; + + __raw_writew(data, PSCR); + } + } + #elif defined(CONFIG_CPU_SUBTYPE_SH7757) || \ + defined(CONFIG_CPU_SUBTYPE_SH7763) || \ + defined(CONFIG_CPU_SUBTYPE_SH7780) || \ + defined(CONFIG_CPU_SUBTYPE_SH7785) || \ + defined(CONFIG_CPU_SUBTYPE_SH7786) || \ + defined(CONFIG_CPU_SUBTYPE_SHX3) + static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) + { + if (!(cflag & CRTSCTS)) + __raw_writew(0x0080, SCSPTR0); /* Set RTS = 1 */ + } + #elif defined(CONFIG_CPU_SH4) && !defined(CONFIG_CPU_SH4A) + static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) + { + if (!(cflag & CRTSCTS)) + __raw_writew(0x0080, SCSPTR2); /* Set RTS = 1 */ + } + #else + static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) + { + /* Nothing to do */ + } + #endif + + #if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ + defined(CONFIG_CPU_SUBTYPE_SH7780) || \ + defined(CONFIG_CPU_SUBTYPE_SH7785) || \ + defined(CONFIG_CPU_SUBTYPE_SH7786) + static int scif_txfill(struct uart_port *port) + { + return sci_in(port, SCTFDR) & 0xff; + } + + static int scif_txroom(struct uart_port *port) + { + return SCIF_TXROOM_MAX - scif_txfill(port); + } + + static int scif_rxfill(struct uart_port *port) + { + return sci_in(port, SCRFDR) & 0xff; + } + #elif defined(CONFIG_CPU_SUBTYPE_SH7763) + static int scif_txfill(struct uart_port *port) + { + if (port->mapbase == 0xffe00000 || + port->mapbase == 0xffe08000) + /* SCIF0/1*/ + return sci_in(port, SCTFDR) & 0xff; + else + /* SCIF2 */ + return sci_in(port, SCFDR) >> 8; + } + + static int scif_txroom(struct uart_port *port) + { + if (port->mapbase == 0xffe00000 || + port->mapbase == 0xffe08000) + /* SCIF0/1*/ + return SCIF_TXROOM_MAX - scif_txfill(port); + else + /* SCIF2 */ + return SCIF2_TXROOM_MAX - scif_txfill(port); + } + + static int scif_rxfill(struct uart_port *port) + { + if ((port->mapbase == 0xffe00000) || + (port->mapbase == 0xffe08000)) { + /* SCIF0/1*/ + return sci_in(port, SCRFDR) & 0xff; + } else { + /* SCIF2 */ + return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; + } + } + #elif defined(CONFIG_ARCH_SH7372) + static int scif_txfill(struct uart_port *port) + { + if (port->type == PORT_SCIFA) + return sci_in(port, SCFDR) >> 8; + else + return sci_in(port, SCTFDR); + } + + static int scif_txroom(struct uart_port *port) + { + return port->fifosize - scif_txfill(port); + } + + static int scif_rxfill(struct uart_port *port) + { + if (port->type == PORT_SCIFA) + return sci_in(port, SCFDR) & SCIF_RFDC_MASK; + else + return sci_in(port, SCRFDR); + } + #else + static int scif_txfill(struct uart_port *port) + { + return sci_in(port, SCFDR) >> 8; + } + + static int scif_txroom(struct uart_port *port) + { + return SCIF_TXROOM_MAX - scif_txfill(port); + } + + static int scif_rxfill(struct uart_port *port) + { + return sci_in(port, SCFDR) & SCIF_RFDC_MASK; + } + #endif + + static int sci_txfill(struct uart_port *port) + { + return !(sci_in(port, SCxSR) & SCI_TDRE); + } + + static int sci_txroom(struct uart_port *port) + { + return !sci_txfill(port); + } + + static int sci_rxfill(struct uart_port *port) + { + return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; + } + + /* ********************************************************************** * + * the interrupt related routines * + * ********************************************************************** */ + + static void sci_transmit_chars(struct uart_port *port) + { + struct circ_buf *xmit = &port->state->xmit; + unsigned int stopped = uart_tx_stopped(port); + unsigned short status; + unsigned short ctrl; + int count; + + status = sci_in(port, SCxSR); + if (!(status & SCxSR_TDxE(port))) { + ctrl = sci_in(port, SCSCR); + if (uart_circ_empty(xmit)) - ctrl &= ~SCI_CTRL_FLAGS_TIE; ++ ctrl &= ~SCSCR_TIE; + else - ctrl |= SCI_CTRL_FLAGS_TIE; ++ ctrl |= SCSCR_TIE; + sci_out(port, SCSCR, ctrl); + return; + } + + if (port->type == PORT_SCI) + count = sci_txroom(port); + else + count = scif_txroom(port); + + do { + unsigned char c; + + if (port->x_char) { + c = port->x_char; + port->x_char = 0; + } else if (!uart_circ_empty(xmit) && !stopped) { + c = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } else { + break; + } + + sci_out(port, SCxTDR, c); + + port->icount.tx++; + } while (--count > 0); + + sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + if (uart_circ_empty(xmit)) { + sci_stop_tx(port); + } else { + ctrl = sci_in(port, SCSCR); + + if (port->type != PORT_SCI) { + sci_in(port, SCxSR); /* Dummy read */ + sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); + } + - ctrl |= SCI_CTRL_FLAGS_TIE; ++ ctrl |= SCSCR_TIE; + sci_out(port, SCSCR, ctrl); + } + } + + /* On SH3, SCIF may read end-of-break as a space->mark char */ + #define STEPFN(c) ({int __c = (c); (((__c-1)|(__c)) == -1); }) + + static inline void sci_receive_chars(struct uart_port *port) + { + struct sci_port *sci_port = to_sci_port(port); + struct tty_struct *tty = port->state->port.tty; + int i, count, copied = 0; + unsigned short status; + unsigned char flag; + + status = sci_in(port, SCxSR); + if (!(status & SCxSR_RDxF(port))) + return; + + while (1) { + if (port->type == PORT_SCI) + count = sci_rxfill(port); + else + count = scif_rxfill(port); + + /* Don't copy more bytes than there is room for in the buffer */ + count = tty_buffer_request_room(tty, count); + + /* If for any reason we can't copy more data, we're done! */ + if (count == 0) + break; + + if (port->type == PORT_SCI) { + char c = sci_in(port, SCxRDR); + if (uart_handle_sysrq_char(port, c) || + sci_port->break_flag) + count = 0; + else + tty_insert_flip_char(tty, c, TTY_NORMAL); + } else { + for (i = 0; i < count; i++) { + char c = sci_in(port, SCxRDR); + status = sci_in(port, SCxSR); + #if defined(CONFIG_CPU_SH3) + /* Skip "chars" during break */ + if (sci_port->break_flag) { + if ((c == 0) && + (status & SCxSR_FER(port))) { + count--; i--; + continue; + } + + /* Nonzero => end-of-break */ + dev_dbg(port->dev, "debounce<%02x>\n", c); + sci_port->break_flag = 0; + + if (STEPFN(c)) { + count--; i--; + continue; + } + } + #endif /* CONFIG_CPU_SH3 */ + if (uart_handle_sysrq_char(port, c)) { + count--; i--; + continue; + } + + /* Store data and status */ + if (status & SCxSR_FER(port)) { + flag = TTY_FRAME; + dev_notice(port->dev, "frame error\n"); + } else if (status & SCxSR_PER(port)) { + flag = TTY_PARITY; + dev_notice(port->dev, "parity error\n"); + } else + flag = TTY_NORMAL; + + tty_insert_flip_char(tty, c, flag); + } + } + + sci_in(port, SCxSR); /* dummy read */ + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + + copied += count; + port->icount.rx += count; + } + + if (copied) { + /* Tell the rest of the system the news. New characters! */ + tty_flip_buffer_push(tty); + } else { + sci_in(port, SCxSR); /* dummy read */ + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + } + } + + #define SCI_BREAK_JIFFIES (HZ/20) + /* The sci generates interrupts during the break, + * 1 per millisecond or so during the break period, for 9600 baud. + * So dont bother disabling interrupts. + * But dont want more than 1 break event. + * Use a kernel timer to periodically poll the rx line until + * the break is finished. + */ + static void sci_schedule_break_timer(struct sci_port *port) + { + port->break_timer.expires = jiffies + SCI_BREAK_JIFFIES; + add_timer(&port->break_timer); + } + /* Ensure that two consecutive samples find the break over. */ + static void sci_break_timer(unsigned long data) + { + struct sci_port *port = (struct sci_port *)data; + + if (sci_rxd_in(&port->port) == 0) { + port->break_flag = 1; + sci_schedule_break_timer(port); + } else if (port->break_flag == 1) { + /* break is over. */ + port->break_flag = 2; + sci_schedule_break_timer(port); + } else + port->break_flag = 0; + } + + static inline int sci_handle_errors(struct uart_port *port) + { + int copied = 0; + unsigned short status = sci_in(port, SCxSR); + struct tty_struct *tty = port->state->port.tty; + + if (status & SCxSR_ORER(port)) { + /* overrun error */ + if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) + copied++; + + dev_notice(port->dev, "overrun error"); + } + + if (status & SCxSR_FER(port)) { + if (sci_rxd_in(port) == 0) { + /* Notify of BREAK */ + struct sci_port *sci_port = to_sci_port(port); + + if (!sci_port->break_flag) { + sci_port->break_flag = 1; + sci_schedule_break_timer(sci_port); + + /* Do sysrq handling. */ + if (uart_handle_break(port)) + return 0; + + dev_dbg(port->dev, "BREAK detected\n"); + + if (tty_insert_flip_char(tty, 0, TTY_BREAK)) + copied++; + } + + } else { + /* frame error */ + if (tty_insert_flip_char(tty, 0, TTY_FRAME)) + copied++; + + dev_notice(port->dev, "frame error\n"); + } + } + + if (status & SCxSR_PER(port)) { + /* parity error */ + if (tty_insert_flip_char(tty, 0, TTY_PARITY)) + copied++; + + dev_notice(port->dev, "parity error"); + } + + if (copied) + tty_flip_buffer_push(tty); + + return copied; + } + + static inline int sci_handle_fifo_overrun(struct uart_port *port) + { + struct tty_struct *tty = port->state->port.tty; + int copied = 0; + + if (port->type != PORT_SCIF) + return 0; + + if ((sci_in(port, SCLSR) & SCIF_ORER) != 0) { + sci_out(port, SCLSR, 0); + + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + tty_flip_buffer_push(tty); + + dev_notice(port->dev, "overrun error\n"); + copied++; + } + + return copied; + } + + static inline int sci_handle_breaks(struct uart_port *port) + { + int copied = 0; + unsigned short status = sci_in(port, SCxSR); + struct tty_struct *tty = port->state->port.tty; + struct sci_port *s = to_sci_port(port); + + if (uart_handle_break(port)) + return 0; + + if (!s->break_flag && status & SCxSR_BRK(port)) { + #if defined(CONFIG_CPU_SH3) + /* Debounce break */ + s->break_flag = 1; + #endif + /* Notify of BREAK */ + if (tty_insert_flip_char(tty, 0, TTY_BREAK)) + copied++; + + dev_dbg(port->dev, "BREAK detected\n"); + } + + if (copied) + tty_flip_buffer_push(tty); + + copied += sci_handle_fifo_overrun(port); + + return copied; + } + + static irqreturn_t sci_rx_interrupt(int irq, void *ptr) + { + #ifdef CONFIG_SERIAL_SH_SCI_DMA + struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); + + if (s->chan_rx) { + u16 scr = sci_in(port, SCSCR); + u16 ssr = sci_in(port, SCxSR); + + /* Disable future Rx interrupts */ + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + disable_irq_nosync(irq); + scr |= 0x4000; + } else { - scr &= ~SCI_CTRL_FLAGS_RIE; ++ scr &= ~SCSCR_RIE; + } + sci_out(port, SCSCR, scr); + /* Clear current interrupt */ + sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); + dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", + jiffies, s->rx_timeout); + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); + + return IRQ_HANDLED; + } + #endif + + /* I think sci_receive_chars has to be called irrespective + * of whether the I_IXOFF is set, otherwise, how is the interrupt + * to be disabled? + */ + sci_receive_chars(ptr); + + return IRQ_HANDLED; + } + + static irqreturn_t sci_tx_interrupt(int irq, void *ptr) + { + struct uart_port *port = ptr; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + sci_transmit_chars(port); + spin_unlock_irqrestore(&port->lock, flags); + + return IRQ_HANDLED; + } + + static irqreturn_t sci_er_interrupt(int irq, void *ptr) + { + struct uart_port *port = ptr; + + /* Handle errors */ + if (port->type == PORT_SCI) { + if (sci_handle_errors(port)) { + /* discard character in rx buffer */ + sci_in(port, SCxSR); + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + } + } else { + sci_handle_fifo_overrun(port); + sci_rx_interrupt(irq, ptr); + } + + sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); + + /* Kick the transmission */ + sci_tx_interrupt(irq, ptr); + + return IRQ_HANDLED; + } + + static irqreturn_t sci_br_interrupt(int irq, void *ptr) + { + struct uart_port *port = ptr; + + /* Handle BREAKs */ + sci_handle_breaks(port); + sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port)); + + return IRQ_HANDLED; + } + ++static inline unsigned long port_rx_irq_mask(struct uart_port *port) ++{ ++ /* ++ * Not all ports (such as SCIFA) will support REIE. Rather than ++ * special-casing the port type, we check the port initialization ++ * IRQ enable mask to see whether the IRQ is desired at all. If ++ * it's unset, it's logically inferred that there's no point in ++ * testing for it. ++ */ ++ return SCSCR_RIE | (to_sci_port(port)->scscr & SCSCR_REIE); ++} ++ + static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) + { + unsigned short ssr_status, scr_status, err_enabled; + struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); + irqreturn_t ret = IRQ_NONE; + + ssr_status = sci_in(port, SCxSR); + scr_status = sci_in(port, SCSCR); - err_enabled = scr_status & (SCI_CTRL_FLAGS_REIE | SCI_CTRL_FLAGS_RIE); ++ err_enabled = scr_status & port_rx_irq_mask(port); + + /* Tx Interrupt */ - if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCI_CTRL_FLAGS_TIE) && ++ if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCSCR_TIE) && + !s->chan_tx) + ret = sci_tx_interrupt(irq, ptr); ++ + /* + * Rx Interrupt: if we're using DMA, the DMA controller clears RDF / + * DR flags + */ + if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) && - (scr_status & SCI_CTRL_FLAGS_RIE)) ++ (scr_status & SCSCR_RIE)) + ret = sci_rx_interrupt(irq, ptr); ++ + /* Error Interrupt */ + if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled) + ret = sci_er_interrupt(irq, ptr); ++ + /* Break Interrupt */ + if ((ssr_status & SCxSR_BRK(port)) && err_enabled) + ret = sci_br_interrupt(irq, ptr); + + return ret; + } + + /* + * Here we define a transistion notifier so that we can update all of our + * ports' baud rate when the peripheral clock changes. + */ + static int sci_notifier(struct notifier_block *self, + unsigned long phase, void *p) + { + struct sh_sci_priv *priv = container_of(self, + struct sh_sci_priv, clk_nb); + struct sci_port *sci_port; + unsigned long flags; + + if ((phase == CPUFREQ_POSTCHANGE) || + (phase == CPUFREQ_RESUMECHANGE)) { + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(sci_port, &priv->ports, node) + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); + spin_unlock_irqrestore(&priv->lock, flags); + } + + return NOTIFY_OK; + } + + static void sci_clk_enable(struct uart_port *port) + { + struct sci_port *sci_port = to_sci_port(port); + + clk_enable(sci_port->iclk); + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); + clk_enable(sci_port->fclk); + } + + static void sci_clk_disable(struct uart_port *port) + { + struct sci_port *sci_port = to_sci_port(port); + + clk_disable(sci_port->fclk); + clk_disable(sci_port->iclk); + } + + static int sci_request_irq(struct sci_port *port) + { + int i; + irqreturn_t (*handlers[4])(int irq, void *ptr) = { + sci_er_interrupt, sci_rx_interrupt, sci_tx_interrupt, + sci_br_interrupt, + }; + const char *desc[] = { "SCI Receive Error", "SCI Receive Data Full", + "SCI Transmit Data Empty", "SCI Break" }; + + if (port->irqs[0] == port->irqs[1]) { + if (unlikely(!port->irqs[0])) + return -ENODEV; + + if (request_irq(port->irqs[0], sci_mpxed_interrupt, + IRQF_DISABLED, "sci", port)) { + dev_err(port->port.dev, "Can't allocate IRQ\n"); + return -ENODEV; + } + } else { + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + if (unlikely(!port->irqs[i])) + continue; + + if (request_irq(port->irqs[i], handlers[i], + IRQF_DISABLED, desc[i], port)) { + dev_err(port->port.dev, "Can't allocate IRQ\n"); + return -ENODEV; + } + } + } + + return 0; + } + + static void sci_free_irq(struct sci_port *port) + { + int i; + + if (port->irqs[0] == port->irqs[1]) + free_irq(port->irqs[0], port); + else { + for (i = 0; i < ARRAY_SIZE(port->irqs); i++) { + if (!port->irqs[i]) + continue; + + free_irq(port->irqs[i], port); + } + } + } + + static unsigned int sci_tx_empty(struct uart_port *port) + { + unsigned short status = sci_in(port, SCxSR); + unsigned short in_tx_fifo = scif_txfill(port); + + return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; + } + + static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) + { + /* This routine is used for seting signals of: DTR, DCD, CTS/RTS */ + /* We use SCIF's hardware for CTS/RTS, so don't need any for that. */ + /* If you have signals for DTR and DCD, please implement here. */ + } + + static unsigned int sci_get_mctrl(struct uart_port *port) + { + /* This routine is used for getting signals of: DTR, DCD, DSR, RI, + and CTS/RTS */ + + return TIOCM_DTR | TIOCM_RTS | TIOCM_DSR; + } + + #ifdef CONFIG_SERIAL_SH_SCI_DMA + static void sci_dma_tx_complete(void *arg) + { + struct sci_port *s = arg; + struct uart_port *port = &s->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned long flags; + + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + + spin_lock_irqsave(&port->lock, flags); + + xmit->tail += sg_dma_len(&s->sg_tx); + xmit->tail &= UART_XMIT_SIZE - 1; + + port->icount.tx += sg_dma_len(&s->sg_tx); + + async_tx_ack(s->desc_tx); + s->cookie_tx = -EINVAL; + s->desc_tx = NULL; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (!uart_circ_empty(xmit)) { + schedule_work(&s->work_tx); + } else if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + u16 ctrl = sci_in(port, SCSCR); - sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE); ++ sci_out(port, SCSCR, ctrl & ~SCSCR_TIE); + } + + spin_unlock_irqrestore(&port->lock, flags); + } + + /* Locking: called with port lock held */ + static int sci_dma_rx_push(struct sci_port *s, struct tty_struct *tty, + size_t count) + { + struct uart_port *port = &s->port; + int i, active, room; + + room = tty_buffer_request_room(tty, count); + + if (s->active_rx == s->cookie_rx[0]) { + active = 0; + } else if (s->active_rx == s->cookie_rx[1]) { + active = 1; + } else { + dev_err(port->dev, "cookie %d not found!\n", s->active_rx); + return 0; + } + + if (room < count) + dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", + count - room); + if (!room) + return room; + + for (i = 0; i < room; i++) + tty_insert_flip_char(tty, ((u8 *)sg_virt(&s->sg_rx[active]))[i], + TTY_NORMAL); + + port->icount.rx += room; + + return room; + } + + static void sci_dma_rx_complete(void *arg) + { + struct sci_port *s = arg; + struct uart_port *port = &s->port; + struct tty_struct *tty = port->state->port.tty; + unsigned long flags; + int count; + + dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx); + + spin_lock_irqsave(&port->lock, flags); + + count = sci_dma_rx_push(s, tty, s->buf_len_rx); + + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); + + spin_unlock_irqrestore(&port->lock, flags); + + if (count) + tty_flip_buffer_push(tty); + + schedule_work(&s->work_rx); + } + + static void sci_start_rx(struct uart_port *port); + static void sci_start_tx(struct uart_port *port); + + static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) + { + struct dma_chan *chan = s->chan_rx; + struct uart_port *port = &s->port; + + s->chan_rx = NULL; + s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; + dma_release_channel(chan); + if (sg_dma_address(&s->sg_rx[0])) + dma_free_coherent(port->dev, s->buf_len_rx * 2, + sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0])); + if (enable_pio) + sci_start_rx(port); + } + + static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) + { + struct dma_chan *chan = s->chan_tx; + struct uart_port *port = &s->port; + + s->chan_tx = NULL; + s->cookie_tx = -EINVAL; + dma_release_channel(chan); + if (enable_pio) + sci_start_tx(port); + } + + static void sci_submit_rx(struct sci_port *s) + { + struct dma_chan *chan = s->chan_rx; + int i; + + for (i = 0; i < 2; i++) { + struct scatterlist *sg = &s->sg_rx[i]; + struct dma_async_tx_descriptor *desc; + + desc = chan->device->device_prep_slave_sg(chan, + sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT); + + if (desc) { + s->desc_rx[i] = desc; + desc->callback = sci_dma_rx_complete; + desc->callback_param = s; + s->cookie_rx[i] = desc->tx_submit(desc); + } + + if (!desc || s->cookie_rx[i] < 0) { + if (i) { + async_tx_ack(s->desc_rx[0]); + s->cookie_rx[0] = -EINVAL; + } + if (desc) { + async_tx_ack(desc); + s->cookie_rx[i] = -EINVAL; + } + dev_warn(s->port.dev, + "failed to re-start DMA, using PIO\n"); + sci_rx_dma_release(s, true); + return; + } + dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, + s->cookie_rx[i], i); + } + + s->active_rx = s->cookie_rx[0]; + + dma_async_issue_pending(chan); + } + + static void work_fn_rx(struct work_struct *work) + { + struct sci_port *s = container_of(work, struct sci_port, work_rx); + struct uart_port *port = &s->port; + struct dma_async_tx_descriptor *desc; + int new; + + if (s->active_rx == s->cookie_rx[0]) { + new = 0; + } else if (s->active_rx == s->cookie_rx[1]) { + new = 1; + } else { + dev_err(port->dev, "cookie %d not found!\n", s->active_rx); + return; + } + desc = s->desc_rx[new]; + + if (dma_async_is_tx_complete(s->chan_rx, s->active_rx, NULL, NULL) != + DMA_SUCCESS) { + /* Handle incomplete DMA receive */ + struct tty_struct *tty = port->state->port.tty; + struct dma_chan *chan = s->chan_rx; + struct sh_desc *sh_desc = container_of(desc, struct sh_desc, + async_tx); + unsigned long flags; + int count; + + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dev_dbg(port->dev, "Read %u bytes with cookie %d\n", + sh_desc->partial, sh_desc->cookie); + + spin_lock_irqsave(&port->lock, flags); + count = sci_dma_rx_push(s, tty, sh_desc->partial); + spin_unlock_irqrestore(&port->lock, flags); + + if (count) + tty_flip_buffer_push(tty); + + sci_submit_rx(s); + + return; + } + + s->cookie_rx[new] = desc->tx_submit(desc); + if (s->cookie_rx[new] < 0) { + dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); + sci_rx_dma_release(s, true); + return; + } + + s->active_rx = s->cookie_rx[!new]; + + dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__, + s->cookie_rx[new], new, s->active_rx); + } + + static void work_fn_tx(struct work_struct *work) + { + struct sci_port *s = container_of(work, struct sci_port, work_tx); + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan = s->chan_tx; + struct uart_port *port = &s->port; + struct circ_buf *xmit = &port->state->xmit; + struct scatterlist *sg = &s->sg_tx; + + /* + * DMA is idle now. + * Port xmit buffer is already mapped, and it is one page... Just adjust + * offsets and lengths. Since it is a circular buffer, we have to + * transmit till the end, and then the rest. Take the port lock to get a + * consistent xmit buffer state. + */ + spin_lock_irq(&port->lock); + sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); + sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + + sg->offset; + sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), + CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); + spin_unlock_irq(&port->lock); + + BUG_ON(!sg_dma_len(sg)); + + desc = chan->device->device_prep_slave_sg(chan, + sg, s->sg_len_tx, DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + /* switch to PIO */ + sci_tx_dma_release(s, true); + return; + } + + dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE); + + spin_lock_irq(&port->lock); + s->desc_tx = desc; + desc->callback = sci_dma_tx_complete; + desc->callback_param = s; + spin_unlock_irq(&port->lock); + s->cookie_tx = desc->tx_submit(desc); + if (s->cookie_tx < 0) { + dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); + /* switch to PIO */ + sci_tx_dma_release(s, true); + return; + } + + dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", __func__, + xmit->buf, xmit->tail, xmit->head, s->cookie_tx); + + dma_async_issue_pending(chan); + } + #endif + + static void sci_start_tx(struct uart_port *port) + { + struct sci_port *s = to_sci_port(port); + unsigned short ctrl; + + #ifdef CONFIG_SERIAL_SH_SCI_DMA + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + u16 new, scr = sci_in(port, SCSCR); + if (s->chan_tx) + new = scr | 0x8000; + else + new = scr & ~0x8000; + if (new != scr) + sci_out(port, SCSCR, new); + } ++ + if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && + s->cookie_tx < 0) + schedule_work(&s->work_tx); + #endif ++ + if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = sci_in(port, SCSCR); - sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE); ++ sci_out(port, SCSCR, ctrl | SCSCR_TIE); + } + } + + static void sci_stop_tx(struct uart_port *port) + { + unsigned short ctrl; + + /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = sci_in(port, SCSCR); ++ + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~0x8000; - ctrl &= ~SCI_CTRL_FLAGS_TIE; ++ ++ ctrl &= ~SCSCR_TIE; ++ + sci_out(port, SCSCR, ctrl); + } + + static void sci_start_rx(struct uart_port *port) + { - unsigned short ctrl = SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE; ++ unsigned short ctrl; ++ ++ ctrl = sci_in(port, SCSCR) | port_rx_irq_mask(port); + - /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ - ctrl |= sci_in(port, SCSCR); + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~0x4000; ++ + sci_out(port, SCSCR, ctrl); + } + + static void sci_stop_rx(struct uart_port *port) + { + unsigned short ctrl; + - /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ + ctrl = sci_in(port, SCSCR); ++ + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~0x4000; - ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE); ++ ++ ctrl &= ~port_rx_irq_mask(port); ++ + sci_out(port, SCSCR, ctrl); + } + + static void sci_enable_ms(struct uart_port *port) + { + /* Nothing here yet .. */ + } + + static void sci_break_ctl(struct uart_port *port, int break_state) + { + /* Nothing here yet .. */ + } + + #ifdef CONFIG_SERIAL_SH_SCI_DMA + static bool filter(struct dma_chan *chan, void *slave) + { + struct sh_dmae_slave *param = slave; + + dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__, + param->slave_id); + + if (param->dma_dev == chan->device->dev) { + chan->private = param; + return true; + } else { + return false; + } + } + + static void rx_timer_fn(unsigned long arg) + { + struct sci_port *s = (struct sci_port *)arg; + struct uart_port *port = &s->port; + u16 scr = sci_in(port, SCSCR); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + scr &= ~0x4000; + enable_irq(s->irqs[1]); + } - sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE); ++ sci_out(port, SCSCR, scr | SCSCR_RIE); + dev_dbg(port->dev, "DMA Rx timed out\n"); + schedule_work(&s->work_rx); + } + + static void sci_request_dma(struct uart_port *port) + { + struct sci_port *s = to_sci_port(port); + struct sh_dmae_slave *param; + struct dma_chan *chan; + dma_cap_mask_t mask; + int nent; + + dev_dbg(port->dev, "%s: port %d DMA %p\n", __func__, + port->line, s->dma_dev); + + if (!s->dma_dev) + return; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + param = &s->param_tx; + + /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */ + param->slave_id = s->slave_tx; + param->dma_dev = s->dma_dev; + + s->cookie_tx = -EINVAL; + chan = dma_request_channel(mask, filter, param); + dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan); + if (chan) { + s->chan_tx = chan; + sg_init_table(&s->sg_tx, 1); + /* UART circular tx buffer is an aligned page. */ + BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); + sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf), + UART_XMIT_SIZE, (int)port->state->xmit.buf & ~PAGE_MASK); + nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE); + if (!nent) + sci_tx_dma_release(s, false); + else + dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, + sg_dma_len(&s->sg_tx), + port->state->xmit.buf, sg_dma_address(&s->sg_tx)); + + s->sg_len_tx = nent; + + INIT_WORK(&s->work_tx, work_fn_tx); + } + + param = &s->param_rx; + + /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */ + param->slave_id = s->slave_rx; + param->dma_dev = s->dma_dev; + + chan = dma_request_channel(mask, filter, param); + dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); + if (chan) { + dma_addr_t dma[2]; + void *buf[2]; + int i; + + s->chan_rx = chan; + + s->buf_len_rx = 2 * max(16, (int)port->fifosize); + buf[0] = dma_alloc_coherent(port->dev, s->buf_len_rx * 2, + &dma[0], GFP_KERNEL); + + if (!buf[0]) { + dev_warn(port->dev, + "failed to allocate dma buffer, using PIO\n"); + sci_rx_dma_release(s, true); + return; + } + + buf[1] = buf[0] + s->buf_len_rx; + dma[1] = dma[0] + s->buf_len_rx; + + for (i = 0; i < 2; i++) { + struct scatterlist *sg = &s->sg_rx[i]; + + sg_init_table(sg, 1); + sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, + (int)buf[i] & ~PAGE_MASK); + sg_dma_address(sg) = dma[i]; + } + + INIT_WORK(&s->work_rx, work_fn_rx); + setup_timer(&s->rx_timer, rx_timer_fn, (unsigned long)s); + + sci_submit_rx(s); + } + } + + static void sci_free_dma(struct uart_port *port) + { + struct sci_port *s = to_sci_port(port); + + if (!s->dma_dev) + return; + + if (s->chan_tx) + sci_tx_dma_release(s, false); + if (s->chan_rx) + sci_rx_dma_release(s, false); + } + #endif + + static int sci_startup(struct uart_port *port) + { + struct sci_port *s = to_sci_port(port); + + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + + if (s->enable) + s->enable(port); + + sci_request_irq(s); + #ifdef CONFIG_SERIAL_SH_SCI_DMA + sci_request_dma(port); + #endif + sci_start_tx(port); + sci_start_rx(port); + + return 0; + } + + static void sci_shutdown(struct uart_port *port) + { + struct sci_port *s = to_sci_port(port); + + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + + sci_stop_rx(port); + sci_stop_tx(port); + #ifdef CONFIG_SERIAL_SH_SCI_DMA + sci_free_dma(port); + #endif + sci_free_irq(s); + + if (s->disable) + s->disable(port); + } + ++static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps, ++ unsigned long freq) ++{ ++ switch (algo_id) { ++ case SCBRR_ALGO_1: ++ return ((freq + 16 * bps) / (16 * bps) - 1); ++ case SCBRR_ALGO_2: ++ return ((freq + 16 * bps) / (32 * bps) - 1); ++ case SCBRR_ALGO_3: ++ return (((freq * 2) + 16 * bps) / (16 * bps) - 1); ++ case SCBRR_ALGO_4: ++ return (((freq * 2) + 16 * bps) / (32 * bps) - 1); ++ case SCBRR_ALGO_5: ++ return (((freq * 1000 / 32) / bps) - 1); ++ } ++ ++ /* Warn, but use a safe default */ ++ WARN_ON(1); ++ return ((freq + 16 * bps) / (32 * bps) - 1); ++} ++ + static void sci_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) + { -#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct sci_port *s = to_sci_port(port); -#endif + unsigned int status, baud, smr_val, max_baud; + int t = -1; + u16 scfcr = 0; + + /* + * earlyprintk comes here early on with port->uartclk set to zero. + * the clock framework is not up and running at this point so here + * we assume that 115200 is the maximum baud rate. please note that + * the baud rate is not programmed during earlyprintk - it is assumed + * that the previous boot loader has enabled required clocks and + * setup the baud rate generator hardware for us already. + */ + max_baud = port->uartclk ? port->uartclk / 16 : 115200; + + baud = uart_get_baud_rate(port, termios, old, 0, max_baud); + if (likely(baud && port->uartclk)) - t = SCBRR_VALUE(baud, port->uartclk); ++ t = sci_scbrr_calc(s->scbrr_algo_id, baud, port->uartclk); + + do { + status = sci_in(port, SCxSR); + } while (!(status & SCxSR_TEND(port))); + + sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ + + if (port->type != PORT_SCI) + sci_out(port, SCFCR, scfcr | SCFCR_RFRST | SCFCR_TFRST); + + smr_val = sci_in(port, SCSMR) & 3; + if ((termios->c_cflag & CSIZE) == CS7) + smr_val |= 0x40; + if (termios->c_cflag & PARENB) + smr_val |= 0x20; + if (termios->c_cflag & PARODD) + smr_val |= 0x30; + if (termios->c_cflag & CSTOPB) + smr_val |= 0x08; + + uart_update_timeout(port, termios->c_cflag, baud); + + sci_out(port, SCSMR, smr_val); + + dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t, - SCSCR_INIT(port)); ++ s->scscr); + + if (t > 0) { + if (t >= 256) { + sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1); + t >>= 2; + } else + sci_out(port, SCSMR, sci_in(port, SCSMR) & ~3); + + sci_out(port, SCBRR, t); + udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */ + } + + sci_init_pins(port, termios->c_cflag); + sci_out(port, SCFCR, scfcr | ((termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0)); + - sci_out(port, SCSCR, SCSCR_INIT(port)); ++ sci_out(port, SCSCR, s->scscr); + + #ifdef CONFIG_SERIAL_SH_SCI_DMA + /* + * Calculate delay for 1.5 DMA buffers: see + * drivers/serial/serial_core.c::uart_update_timeout(). With 10 bits + * (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function + * calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)." + * Then below we calculate 3 jiffies (12ms) for 1.5 DMA buffers (3 FIFO + * sizes), but it has been found out experimentally, that this is not + * enough: the driver too often needlessly runs on a DMA timeout. 20ms + * as a minimum seem to work perfectly. + */ + if (s->chan_rx) { + s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / + port->fifosize / 2; + dev_dbg(port->dev, + "DMA Rx t-out %ums, tty t-out %u jiffies\n", + s->rx_timeout * 1000 / HZ, port->timeout); + if (s->rx_timeout < msecs_to_jiffies(20)) + s->rx_timeout = msecs_to_jiffies(20); + } + #endif + + if ((termios->c_cflag & CREAD) != 0) + sci_start_rx(port); + } + + static const char *sci_type(struct uart_port *port) + { + switch (port->type) { + case PORT_IRDA: + return "irda"; + case PORT_SCI: + return "sci"; + case PORT_SCIF: + return "scif"; + case PORT_SCIFA: + return "scifa"; + case PORT_SCIFB: + return "scifb"; + } + + return NULL; + } + + static void sci_release_port(struct uart_port *port) + { + /* Nothing here yet .. */ + } + + static int sci_request_port(struct uart_port *port) + { + /* Nothing here yet .. */ + return 0; + } + + static void sci_config_port(struct uart_port *port, int flags) + { + struct sci_port *s = to_sci_port(port); + + port->type = s->type; + + if (port->membase) + return; + + if (port->flags & UPF_IOREMAP) { + port->membase = ioremap_nocache(port->mapbase, 0x40); + + if (IS_ERR(port->membase)) + dev_err(port->dev, "can't remap port#%d\n", port->line); + } else { + /* + * For the simple (and majority of) cases where we don't + * need to do any remapping, just cast the cookie + * directly. + */ + port->membase = (void __iomem *)port->mapbase; + } + } + + static int sci_verify_port(struct uart_port *port, struct serial_struct *ser) + { + struct sci_port *s = to_sci_port(port); + + if (ser->irq != s->irqs[SCIx_TXI_IRQ] || ser->irq > nr_irqs) + return -EINVAL; + if (ser->baud_base < 2400) + /* No paper tape reader for Mitch.. */ + return -EINVAL; + + return 0; + } + + static struct uart_ops sci_uart_ops = { + .tx_empty = sci_tx_empty, + .set_mctrl = sci_set_mctrl, + .get_mctrl = sci_get_mctrl, + .start_tx = sci_start_tx, + .stop_tx = sci_stop_tx, + .stop_rx = sci_stop_rx, + .enable_ms = sci_enable_ms, + .break_ctl = sci_break_ctl, + .startup = sci_startup, + .shutdown = sci_shutdown, + .set_termios = sci_set_termios, + .type = sci_type, + .release_port = sci_release_port, + .request_port = sci_request_port, + .config_port = sci_config_port, + .verify_port = sci_verify_port, + #ifdef CONFIG_CONSOLE_POLL + .poll_get_char = sci_poll_get_char, + .poll_put_char = sci_poll_put_char, + #endif + }; + + static int __devinit sci_init_single(struct platform_device *dev, + struct sci_port *sci_port, + unsigned int index, + struct plat_sci_port *p) + { + struct uart_port *port = &sci_port->port; + + port->ops = &sci_uart_ops; + port->iotype = UPIO_MEM; + port->line = index; + + switch (p->type) { + case PORT_SCIFB: + port->fifosize = 256; + break; + case PORT_SCIFA: + port->fifosize = 64; + break; + case PORT_SCIF: + port->fifosize = 16; + break; + default: + port->fifosize = 1; + break; + } + + if (dev) { + sci_port->iclk = clk_get(&dev->dev, "sci_ick"); + if (IS_ERR(sci_port->iclk)) { + sci_port->iclk = clk_get(&dev->dev, "peripheral_clk"); + if (IS_ERR(sci_port->iclk)) { + dev_err(&dev->dev, "can't get iclk\n"); + return PTR_ERR(sci_port->iclk); + } + } + + /* + * The function clock is optional, ignore it if we can't + * find it. + */ + sci_port->fclk = clk_get(&dev->dev, "sci_fck"); + if (IS_ERR(sci_port->fclk)) + sci_port->fclk = NULL; + + sci_port->enable = sci_clk_enable; + sci_port->disable = sci_clk_disable; + port->dev = &dev->dev; + } + + sci_port->break_timer.data = (unsigned long)sci_port; + sci_port->break_timer.function = sci_break_timer; + init_timer(&sci_port->break_timer); + + port->mapbase = p->mapbase; + port->membase = p->membase; + - port->irq = p->irqs[SCIx_TXI_IRQ]; - port->flags = p->flags; - sci_port->type = port->type = p->type; ++ port->irq = p->irqs[SCIx_TXI_IRQ]; ++ port->flags = p->flags; ++ sci_port->type = port->type = p->type; ++ sci_port->scscr = p->scscr; ++ sci_port->scbrr_algo_id = p->scbrr_algo_id; + + #ifdef CONFIG_SERIAL_SH_SCI_DMA + sci_port->dma_dev = p->dma_dev; + sci_port->slave_tx = p->dma_slave_tx; + sci_port->slave_rx = p->dma_slave_rx; + + dev_dbg(port->dev, "%s: DMA device %p, tx %d, rx %d\n", __func__, + p->dma_dev, p->dma_slave_tx, p->dma_slave_rx); + #endif + + memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs)); + return 0; + } + + #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE + static struct tty_driver *serial_console_device(struct console *co, int *index) + { + struct uart_driver *p = &sci_uart_driver; + *index = co->index; + return p->tty_driver; + } + + static void serial_console_putchar(struct uart_port *port, int ch) + { + sci_poll_put_char(port, ch); + } + + /* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + */ + static void serial_console_write(struct console *co, const char *s, + unsigned count) + { + struct uart_port *port = co->data; + struct sci_port *sci_port = to_sci_port(port); + unsigned short bits; + + if (sci_port->enable) + sci_port->enable(port); + + uart_console_write(port, s, count, serial_console_putchar); + + /* wait until fifo is empty and last bit has been transmitted */ + bits = SCxSR_TDxE(port) | SCxSR_TEND(port); + while ((sci_in(port, SCxSR) & bits) != bits) + cpu_relax(); + + if (sci_port->disable) + sci_port->disable(port); + } + + static int __devinit serial_console_setup(struct console *co, char *options) + { + struct sci_port *sci_port; + struct uart_port *port; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= SCI_NPORTS) + co->index = 0; + + if (co->data) { + port = co->data; + sci_port = to_sci_port(port); + } else { + sci_port = &sci_ports[co->index]; + port = &sci_port->port; + co->data = port; + } + + /* + * Also need to check port->type, we don't actually have any + * UPIO_PORT ports, but uart_report_port() handily misreports + * it anyways if we don't have a port available by the time this is + * called. + */ + if (!port->type) + return -ENODEV; + + sci_config_port(port, 0); + + if (sci_port->enable) + sci_port->enable(port); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + ret = uart_set_options(port, co, baud, parity, bits, flow); + #if defined(__H8300H__) || defined(__H8300S__) + /* disable rx interrupt */ + if (ret == 0) + sci_stop_rx(port); + #endif + /* TODO: disable clock */ + return ret; + } + + static struct console serial_console = { + .name = "ttySC", + .device = serial_console_device, + .write = serial_console_write, + .setup = serial_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + }; + + static int __init sci_console_init(void) + { + register_console(&serial_console); + return 0; + } + console_initcall(sci_console_init); + + static struct sci_port early_serial_port; + static struct console early_serial_console = { + .name = "early_ttySC", + .write = serial_console_write, + .flags = CON_PRINTBUFFER, + }; + static char early_serial_buf[32]; + + #endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ + + #if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) + #define SCI_CONSOLE (&serial_console) + #else + #define SCI_CONSOLE 0 + #endif + + static char banner[] __initdata = + KERN_INFO "SuperH SCI(F) driver initialized\n"; + + static struct uart_driver sci_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "sci", + .dev_name = "ttySC", + .major = SCI_MAJOR, + .minor = SCI_MINOR_START, + .nr = SCI_NPORTS, + .cons = SCI_CONSOLE, + }; + + + static int sci_remove(struct platform_device *dev) + { + struct sh_sci_priv *priv = platform_get_drvdata(dev); + struct sci_port *p; + unsigned long flags; + + cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); + + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(p, &priv->ports, node) { + uart_remove_one_port(&sci_uart_driver, &p->port); + clk_put(p->iclk); + clk_put(p->fclk); + } + spin_unlock_irqrestore(&priv->lock, flags); + + kfree(priv); + return 0; + } + + static int __devinit sci_probe_single(struct platform_device *dev, + unsigned int index, + struct plat_sci_port *p, + struct sci_port *sciport) + { + struct sh_sci_priv *priv = platform_get_drvdata(dev); + unsigned long flags; + int ret; + + /* Sanity check */ + if (unlikely(index >= SCI_NPORTS)) { + dev_notice(&dev->dev, "Attempting to register port " + "%d when only %d are available.\n", + index+1, SCI_NPORTS); + dev_notice(&dev->dev, "Consider bumping " + "CONFIG_SERIAL_SH_SCI_NR_UARTS!\n"); + return 0; + } + + ret = sci_init_single(dev, sciport, index, p); + if (ret) + return ret; + + ret = uart_add_one_port(&sci_uart_driver, &sciport->port); + if (ret) + return ret; + + INIT_LIST_HEAD(&sciport->node); + + spin_lock_irqsave(&priv->lock, flags); + list_add(&sciport->node, &priv->ports); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; + } + + /* + * Register a set of serial devices attached to a platform device. The + * list is terminated with a zero flags entry, which means we expect + * all entries to have at least UPF_BOOT_AUTOCONF set. Platforms that need + * remapping (such as sh64) should also set UPF_IOREMAP. + */ + static int __devinit sci_probe(struct platform_device *dev) + { + struct plat_sci_port *p = dev->dev.platform_data; + struct sh_sci_priv *priv; + int i, ret = -EINVAL; + + #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE + if (is_early_platform_device(dev)) { + if (dev->id == -1) + return -ENOTSUPP; + early_serial_console.index = dev->id; + early_serial_console.data = &early_serial_port.port; + sci_init_single(NULL, &early_serial_port, dev->id, p); + serial_console_setup(&early_serial_console, early_serial_buf); + if (!strstr(early_serial_buf, "keep")) + early_serial_console.flags |= CON_BOOT; + register_console(&early_serial_console); + return 0; + } + #endif + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + INIT_LIST_HEAD(&priv->ports); + spin_lock_init(&priv->lock); + platform_set_drvdata(dev, priv); + + priv->clk_nb.notifier_call = sci_notifier; + cpufreq_register_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); + + if (dev->id != -1) { + ret = sci_probe_single(dev, dev->id, p, &sci_ports[dev->id]); + if (ret) + goto err_unreg; + } else { + for (i = 0; p && p->flags != 0; p++, i++) { + ret = sci_probe_single(dev, i, p, &sci_ports[i]); + if (ret) + goto err_unreg; + } + } + + #ifdef CONFIG_SH_STANDARD_BIOS + sh_bios_gdb_detach(); + #endif + + return 0; + + err_unreg: + sci_remove(dev); + return ret; + } + + static int sci_suspend(struct device *dev) + { + struct sh_sci_priv *priv = dev_get_drvdata(dev); + struct sci_port *p; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(p, &priv->ports, node) + uart_suspend_port(&sci_uart_driver, &p->port); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; + } + + static int sci_resume(struct device *dev) + { + struct sh_sci_priv *priv = dev_get_drvdata(dev); + struct sci_port *p; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + list_for_each_entry(p, &priv->ports, node) + uart_resume_port(&sci_uart_driver, &p->port); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; + } + + static const struct dev_pm_ops sci_dev_pm_ops = { + .suspend = sci_suspend, + .resume = sci_resume, + }; + + static struct platform_driver sci_driver = { + .probe = sci_probe, + .remove = sci_remove, + .driver = { + .name = "sh-sci", + .owner = THIS_MODULE, + .pm = &sci_dev_pm_ops, + }, + }; + + static int __init sci_init(void) + { + int ret; + + printk(banner); + + ret = uart_register_driver(&sci_uart_driver); + if (likely(ret == 0)) { + ret = platform_driver_register(&sci_driver); + if (unlikely(ret)) + uart_unregister_driver(&sci_uart_driver); + } + + return ret; + } + + static void __exit sci_exit(void) + { + platform_driver_unregister(&sci_driver); + uart_unregister_driver(&sci_uart_driver); + } + + #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE + early_platform_init_buffer("earlyprintk", &sci_driver, + early_serial_buf, ARRAY_SIZE(early_serial_buf)); + #endif + module_init(sci_init); + module_exit(sci_exit); + + MODULE_LICENSE("GPL"); + MODULE_ALIAS("platform:sh-sci"); diff --cc drivers/tty/serial/sh-sci.h index 000000000000,4bc614e4221c..b223d6cbf33a mode 000000,100644..100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@@ -1,0 -1,660 +1,507 @@@ + #include + #include + #include + + #if defined(CONFIG_H83007) || defined(CONFIG_H83068) + #include + #endif + #if defined(CONFIG_H8S2678) + #include + #endif + + #if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ + defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7708) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) + # define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */ + # define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */ -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ + #elif defined(CONFIG_CPU_SUBTYPE_SH7705) + # define SCIF0 0xA4400000 + # define SCIF2 0xA4410000 -# define SCSMR_Ir 0xA44A0000 -# define IRDA_SCIF SCIF0 + # define SCPCR 0xA4000116 + # define SCPDR 0xA4000136 - -/* Set the clock source, - * SCIF2 (0xA4410000) -> External clock, SCK pin used as clock input - * SCIF0 (0xA4400000) -> Internal clock, SCK pin as serial clock output - */ -# define SCSCR_INIT(port) (port->mapbase == SCIF2) ? 0xF3 : 0xF0 + #elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \ + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) -# define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */ + # define PORT_PTCR 0xA405011EUL + # define PORT_PVCR 0xA4050122UL + # define SCIF_ORER 0x0200 /* overrun error bit */ + #elif defined(CONFIG_SH_RTS7751R2D) + # define SCSPTR1 0xFFE0001C /* 8 bit SCIF */ + # define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ + # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ + #elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ + defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ + defined(CONFIG_CPU_SUBTYPE_SH7091) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751R) + # define SCSPTR1 0xffe0001c /* 8 bit SCI */ + # define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ + # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) (((port)->type == PORT_SCI) ? \ - 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ - 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ ) + #elif defined(CONFIG_CPU_SUBTYPE_SH7760) + # define SCSPTR0 0xfe600024 /* 16 bit SCIF */ + # define SCSPTR1 0xfe610024 /* 16 bit SCIF */ + # define SCSPTR2 0xfe620024 /* 16 bit SCIF */ + # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ + #elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) + # define SCSPTR0 0xA4400000 /* 16 bit SCIF */ + # define SCIF_ORER 0x0001 /* overrun error bit */ + # define PACR 0xa4050100 + # define PBCR 0xa4050102 -# define SCSCR_INIT(port) 0x3B + #elif defined(CONFIG_CPU_SUBTYPE_SH7343) + # define SCSPTR0 0xffe00010 /* 16 bit SCIF */ + # define SCSPTR1 0xffe10010 /* 16 bit SCIF */ + # define SCSPTR2 0xffe20010 /* 16 bit SCIF */ + # define SCSPTR3 0xffe30010 /* 16 bit SCIF */ -# define SCSCR_INIT(port) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0,CKE=1 */ + #elif defined(CONFIG_CPU_SUBTYPE_SH7722) + # define PADR 0xA4050120 + # define PSDR 0xA405013e + # define PWDR 0xA4050166 + # define PSCR 0xA405011E + # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ + #elif defined(CONFIG_CPU_SUBTYPE_SH7366) + # define SCPDR0 0xA405013E /* 16 bit SCIF0 PSDR */ + # define SCSPTR0 SCPDR0 + # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ + #elif defined(CONFIG_CPU_SUBTYPE_SH7723) + # define SCSPTR0 0xa4050160 + # define SCSPTR1 0xa405013e + # define SCSPTR2 0xa4050160 + # define SCSPTR3 0xa405013e + # define SCSPTR4 0xa4050128 + # define SCSPTR5 0xa4050128 + # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ + #elif defined(CONFIG_CPU_SUBTYPE_SH7724) + # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) ((port)->type == PORT_SCIFA ? \ - 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ : \ - 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ ) + #elif defined(CONFIG_CPU_SUBTYPE_SH4_202) + # define SCSPTR2 0xffe80020 /* 16 bit SCIF */ + # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ + #elif defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103) -# define SCIF_BASE_ADDR 0x01030000 -# define SCIF_ADDR_SH5 PHYS_PERIPHERAL_BLOCK+SCIF_BASE_ADDR + # define SCIF_PTR2_OFFS 0x0000020 -# define SCIF_LSR2_OFFS 0x0000024 + # define SCSPTR2 ((port->mapbase)+SCIF_PTR2_OFFS) /* 16 bit SCIF */ -# define SCLSR2 ((port->mapbase)+SCIF_LSR2_OFFS) /* 16 bit SCIF */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0, TE=1,RE=1,REIE=1 */ + #elif defined(CONFIG_H83007) || defined(CONFIG_H83068) -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ + # define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) + #elif defined(CONFIG_H8S2678) -# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ + # define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) + #elif defined(CONFIG_CPU_SUBTYPE_SH7757) + # define SCSPTR0 0xfe4b0020 + # define SCSPTR1 0xfe4b0020 + # define SCSPTR2 0xfe4b0020 + # define SCIF_ORER 0x0001 -# define SCSCR_INIT(port) 0x38 + # define SCIF_ONLY + #elif defined(CONFIG_CPU_SUBTYPE_SH7763) + # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ + # define SCSPTR1 0xffe08024 /* 16 bit SCIF */ + # define SCSPTR2 0xffe10020 /* 16 bit SCIF/IRDA */ + # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ + #elif defined(CONFIG_CPU_SUBTYPE_SH7770) + # define SCSPTR0 0xff923020 /* 16 bit SCIF */ + # define SCSPTR1 0xff924020 /* 16 bit SCIF */ + # define SCSPTR2 0xff925020 /* 16 bit SCIF */ + # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x3c /* TIE=0,RIE=0,TE=1,RE=1,REIE=1,cke=2 */ + #elif defined(CONFIG_CPU_SUBTYPE_SH7780) + # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ + # define SCSPTR1 0xffe10024 /* 16 bit SCIF */ + # define SCIF_ORER 0x0001 /* Overrun error bit */ - -#if defined(CONFIG_SH_SH2007) -/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=0 */ -# define SCSCR_INIT(port) 0x38 -#else -/* TIE=0,RIE=0,TE=1,RE=1,REIE=1,CKE1=1 */ -# define SCSCR_INIT(port) 0x3a -#endif - + #elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \ + defined(CONFIG_CPU_SUBTYPE_SH7786) + # define SCSPTR0 0xffea0024 /* 16 bit SCIF */ + # define SCSPTR1 0xffeb0024 /* 16 bit SCIF */ + # define SCSPTR2 0xffec0024 /* 16 bit SCIF */ + # define SCSPTR3 0xffed0024 /* 16 bit SCIF */ + # define SCSPTR4 0xffee0024 /* 16 bit SCIF */ + # define SCSPTR5 0xffef0024 /* 16 bit SCIF */ + # define SCIF_ORER 0x0001 /* Overrun error bit */ -# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ + #elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \ + defined(CONFIG_CPU_SUBTYPE_SH7203) || \ + defined(CONFIG_CPU_SUBTYPE_SH7206) || \ + defined(CONFIG_CPU_SUBTYPE_SH7263) + # define SCSPTR0 0xfffe8020 /* 16 bit SCIF */ + # define SCSPTR1 0xfffe8820 /* 16 bit SCIF */ + # define SCSPTR2 0xfffe9020 /* 16 bit SCIF */ + # define SCSPTR3 0xfffe9820 /* 16 bit SCIF */ + # if defined(CONFIG_CPU_SUBTYPE_SH7201) + # define SCSPTR4 0xfffeA020 /* 16 bit SCIF */ + # define SCSPTR5 0xfffeA820 /* 16 bit SCIF */ + # define SCSPTR6 0xfffeB020 /* 16 bit SCIF */ + # define SCSPTR7 0xfffeB820 /* 16 bit SCIF */ + # endif -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ + #elif defined(CONFIG_CPU_SUBTYPE_SH7619) + # define SCSPTR0 0xf8400020 /* 16 bit SCIF */ + # define SCSPTR1 0xf8410020 /* 16 bit SCIF */ + # define SCSPTR2 0xf8420020 /* 16 bit SCIF */ + # define SCIF_ORER 0x0001 /* overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ + #elif defined(CONFIG_CPU_SUBTYPE_SHX3) + # define SCSPTR0 0xffc30020 /* 16 bit SCIF */ + # define SCSPTR1 0xffc40020 /* 16 bit SCIF */ + # define SCSPTR2 0xffc50020 /* 16 bit SCIF */ + # define SCSPTR3 0xffc60020 /* 16 bit SCIF */ + # define SCIF_ORER 0x0001 /* Overrun error bit */ -# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */ + #else + # error CPU subtype not defined + #endif + -/* SCSCR */ -#define SCI_CTRL_FLAGS_TIE 0x80 /* all */ -#define SCI_CTRL_FLAGS_RIE 0x40 /* all */ -#define SCI_CTRL_FLAGS_TE 0x20 /* all */ -#define SCI_CTRL_FLAGS_RE 0x10 /* all */ -#if defined(CONFIG_CPU_SUBTYPE_SH7750) || \ - defined(CONFIG_CPU_SUBTYPE_SH7091) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7722) || \ - defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751) || \ - defined(CONFIG_CPU_SUBTYPE_SH7751R) || \ - defined(CONFIG_CPU_SUBTYPE_SH7763) || \ - defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) || \ - defined(CONFIG_CPU_SUBTYPE_SHX3) -#define SCI_CTRL_FLAGS_REIE 0x08 /* 7750 SCIF */ -#elif defined(CONFIG_CPU_SUBTYPE_SH7724) -#define SCI_CTRL_FLAGS_REIE ((port)->type == PORT_SCIFA ? 0 : 8) -#else -#define SCI_CTRL_FLAGS_REIE 0 -#endif -/* SCI_CTRL_FLAGS_MPIE 0x08 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_CTRL_FLAGS_TEIE 0x04 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ -/* SCI_CTRL_FLAGS_CKE1 0x02 * all */ -/* SCI_CTRL_FLAGS_CKE0 0x01 * 7707 SCI/SCIF, 7708 SCI, 7709 SCI/SCIF, 7750 SCI */ - + /* SCxSR SCI */ + #define SCI_TDRE 0x80 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ + #define SCI_RDRF 0x40 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ + #define SCI_ORER 0x20 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ + #define SCI_FER 0x10 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ + #define SCI_PER 0x08 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ + #define SCI_TEND 0x04 /* 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ + /* SCI_MPB 0x02 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ + /* SCI_MPBT 0x01 * 7707 SCI, 7708 SCI, 7709 SCI, 7750 SCI */ + + #define SCI_ERRORS ( SCI_PER | SCI_FER | SCI_ORER) + + /* SCxSR SCIF */ + #define SCIF_ER 0x0080 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ + #define SCIF_TEND 0x0040 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ + #define SCIF_TDFE 0x0020 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ + #define SCIF_BRK 0x0010 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ + #define SCIF_FER 0x0008 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ + #define SCIF_PER 0x0004 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ + #define SCIF_RDF 0x0002 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ + #define SCIF_DR 0x0001 /* 7705 SCIF, 7707 SCIF, 7709 SCIF, 7750 SCIF */ + + #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7720) || \ + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) + # define SCIF_ORER 0x0200 + # define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER) + # define SCIF_RFDC_MASK 0x007f + # define SCIF_TXROOM_MAX 64 + #elif defined(CONFIG_CPU_SUBTYPE_SH7763) + # define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK ) + # define SCIF_RFDC_MASK 0x007f + # define SCIF_TXROOM_MAX 64 + /* SH7763 SCIF2 support */ + # define SCIF2_RFDC_MASK 0x001f + # define SCIF2_TXROOM_MAX 16 + #else + # define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK) + # define SCIF_RFDC_MASK 0x001f + # define SCIF_TXROOM_MAX 16 + #endif + + #ifndef SCIF_ORER + #define SCIF_ORER 0x0000 + #endif + + #define SCxSR_TEND(port) (((port)->type == PORT_SCI) ? SCI_TEND : SCIF_TEND) + #define SCxSR_ERRORS(port) (((port)->type == PORT_SCI) ? SCI_ERRORS : SCIF_ERRORS) + #define SCxSR_RDxF(port) (((port)->type == PORT_SCI) ? SCI_RDRF : SCIF_RDF) + #define SCxSR_TDxE(port) (((port)->type == PORT_SCI) ? SCI_TDRE : SCIF_TDFE) + #define SCxSR_FER(port) (((port)->type == PORT_SCI) ? SCI_FER : SCIF_FER) + #define SCxSR_PER(port) (((port)->type == PORT_SCI) ? SCI_PER : SCIF_PER) + #define SCxSR_BRK(port) (((port)->type == PORT_SCI) ? 0x00 : SCIF_BRK) + #define SCxSR_ORER(port) (((port)->type == PORT_SCI) ? SCI_ORER : SCIF_ORER) + + #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7720) || \ + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) + # define SCxSR_RDxF_CLEAR(port) (sci_in(port, SCxSR) & 0xfffc) + # define SCxSR_ERROR_CLEAR(port) (sci_in(port, SCxSR) & 0xfd73) + # define SCxSR_TDxE_CLEAR(port) (sci_in(port, SCxSR) & 0xffdf) + # define SCxSR_BREAK_CLEAR(port) (sci_in(port, SCxSR) & 0xffe3) + #else + # define SCxSR_RDxF_CLEAR(port) (((port)->type == PORT_SCI) ? 0xbc : 0x00fc) + # define SCxSR_ERROR_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x0073) + # define SCxSR_TDxE_CLEAR(port) (((port)->type == PORT_SCI) ? 0x78 : 0x00df) + # define SCxSR_BREAK_CLEAR(port) (((port)->type == PORT_SCI) ? 0xc4 : 0x00e3) + #endif + + /* SCFCR */ + #define SCFCR_RFRST 0x0002 + #define SCFCR_TFRST 0x0004 -#define SCFCR_TCRST 0x4000 + #define SCFCR_MCE 0x0008 + + #define SCI_MAJOR 204 + #define SCI_MINOR_START 8 + -/* Generic serial flags */ -#define SCI_RX_THROTTLE 0x0000001 - -#define SCI_MAGIC 0xbabeface - -/* - * Events are used to schedule things to happen at timer-interrupt - * time, instead of at rs interrupt time. - */ -#define SCI_EVENT_WRITE_WAKEUP 0 - + #define SCI_IN(size, offset) \ + if ((size) == 8) { \ + return ioread8(port->membase + (offset)); \ + } else { \ + return ioread16(port->membase + (offset)); \ + } + #define SCI_OUT(size, offset, value) \ + if ((size) == 8) { \ + iowrite8(value, port->membase + (offset)); \ + } else if ((size) == 16) { \ + iowrite16(value, port->membase + (offset)); \ + } + + #define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ + static inline unsigned int sci_##name##_in(struct uart_port *port) \ + { \ + if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ + SCI_IN(scif_size, scif_offset) \ + } else { /* PORT_SCI or PORT_SCIFA */ \ + SCI_IN(sci_size, sci_offset); \ + } \ + } \ + static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ + { \ + if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ + SCI_OUT(scif_size, scif_offset, value) \ + } else { /* PORT_SCI or PORT_SCIFA */ \ + SCI_OUT(sci_size, sci_offset, value); \ + } \ + } + + #ifdef CONFIG_H8300 + /* h8300 don't have SCIF */ + #define CPU_SCIF_FNS(name) \ + static inline unsigned int sci_##name##_in(struct uart_port *port) \ + { \ + return 0; \ + } \ + static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ + { \ + } + #else + #define CPU_SCIF_FNS(name, scif_offset, scif_size) \ + static inline unsigned int sci_##name##_in(struct uart_port *port) \ + { \ + SCI_IN(scif_size, scif_offset); \ + } \ + static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ + { \ + SCI_OUT(scif_size, scif_offset, value); \ + } + #endif + + #define CPU_SCI_FNS(name, sci_offset, sci_size) \ + static inline unsigned int sci_##name##_in(struct uart_port* port) \ + { \ + SCI_IN(sci_size, sci_offset); \ + } \ + static inline void sci_##name##_out(struct uart_port* port, unsigned int value) \ + { \ + SCI_OUT(sci_size, sci_offset, value); \ + } + + #if defined(CONFIG_CPU_SH3) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) + #if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) + #define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) + #define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) + #elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7720) || \ + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) + #define SCIF_FNS(name, scif_offset, scif_size) \ + CPU_SCIF_FNS(name, scif_offset, scif_size) + #elif defined(CONFIG_ARCH_SH7372) + #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \ + CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) + #define SCIF_FNS(name, scif_offset, scif_size) \ + CPU_SCIF_FNS(name, scif_offset, scif_size) + #else + #define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh3_scif_offset, sh3_scif_size) + #define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh3_scif_offset, sh3_scif_size) + #endif + #elif defined(__H8300H__) || defined(__H8300S__) + #define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCI_FNS(name, h8_sci_offset, h8_sci_size) + #define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name) + #elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ + defined(CONFIG_CPU_SUBTYPE_SH7724) + #define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scif_offset, sh4_scif_size) + #define SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) + #else + #define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ + sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ + h8_sci_offset, h8_sci_size) \ + CPU_SCIx_FNS(name, sh4_sci_offset, sh4_sci_size, sh4_scif_offset, sh4_scif_size) + #define SCIF_FNS(name, sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size) \ + CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) + #endif + + #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ + defined(CONFIG_CPU_SUBTYPE_SH7720) || \ + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) + + SCIF_FNS(SCSMR, 0x00, 16) + SCIF_FNS(SCBRR, 0x04, 8) + SCIF_FNS(SCSCR, 0x08, 16) -SCIF_FNS(SCTDSR, 0x0c, 8) -SCIF_FNS(SCFER, 0x10, 16) + SCIF_FNS(SCxSR, 0x14, 16) + SCIF_FNS(SCFCR, 0x18, 16) + SCIF_FNS(SCFDR, 0x1c, 16) + SCIF_FNS(SCxTDR, 0x20, 8) + SCIF_FNS(SCxRDR, 0x24, 8) + SCIF_FNS(SCLSR, 0x00, 0) + #elif defined(CONFIG_ARCH_SH7372) + SCIF_FNS(SCSMR, 0x00, 16) + SCIF_FNS(SCBRR, 0x04, 8) + SCIF_FNS(SCSCR, 0x08, 16) + SCIF_FNS(SCTDSR, 0x0c, 16) + SCIF_FNS(SCFER, 0x10, 16) + SCIF_FNS(SCxSR, 0x14, 16) + SCIF_FNS(SCFCR, 0x18, 16) + SCIF_FNS(SCFDR, 0x1c, 16) + SCIF_FNS(SCTFDR, 0x38, 16) + SCIF_FNS(SCRFDR, 0x3c, 16) + SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8) + SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8) + SCIF_FNS(SCLSR, 0x00, 0) + #elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ + defined(CONFIG_CPU_SUBTYPE_SH7724) + SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16) + SCIx_FNS(SCBRR, 0x04, 8, 0x04, 8) + SCIx_FNS(SCSCR, 0x08, 16, 0x08, 16) + SCIx_FNS(SCxTDR, 0x20, 8, 0x0c, 8) + SCIx_FNS(SCxSR, 0x14, 16, 0x10, 16) + SCIx_FNS(SCxRDR, 0x24, 8, 0x14, 8) + SCIx_FNS(SCSPTR, 0, 0, 0, 0) -SCIF_FNS(SCTDSR, 0x0c, 8) -SCIF_FNS(SCFER, 0x10, 16) + SCIF_FNS(SCFCR, 0x18, 16) + SCIF_FNS(SCFDR, 0x1c, 16) + SCIF_FNS(SCLSR, 0x24, 16) + #else + /* reg SCI/SH3 SCI/SH4 SCIF/SH3 SCIF/SH4 SCI/H8*/ + /* name off sz off sz off sz off sz off sz*/ + SCIx_FNS(SCSMR, 0x00, 8, 0x00, 8, 0x00, 8, 0x00, 16, 0x00, 8) + SCIx_FNS(SCBRR, 0x02, 8, 0x04, 8, 0x02, 8, 0x04, 8, 0x01, 8) + SCIx_FNS(SCSCR, 0x04, 8, 0x08, 8, 0x04, 8, 0x08, 16, 0x02, 8) + SCIx_FNS(SCxTDR, 0x06, 8, 0x0c, 8, 0x06, 8, 0x0C, 8, 0x03, 8) + SCIx_FNS(SCxSR, 0x08, 8, 0x10, 8, 0x08, 16, 0x10, 16, 0x04, 8) + SCIx_FNS(SCxRDR, 0x0a, 8, 0x14, 8, 0x0A, 8, 0x14, 8, 0x05, 8) + SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16) + #if defined(CONFIG_CPU_SUBTYPE_SH7760) || \ + defined(CONFIG_CPU_SUBTYPE_SH7780) || \ + defined(CONFIG_CPU_SUBTYPE_SH7785) || \ + defined(CONFIG_CPU_SUBTYPE_SH7786) + SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) + SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) + SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) + SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) + SCIF_FNS(SCLSR, 0, 0, 0x28, 16) + #elif defined(CONFIG_CPU_SUBTYPE_SH7763) + SCIF_FNS(SCFDR, 0, 0, 0x1C, 16) + SCIF_FNS(SCSPTR2, 0, 0, 0x20, 16) -SCIF_FNS(SCLSR2, 0, 0, 0x24, 16) + SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16) + SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16) + SCIF_FNS(SCSPTR, 0, 0, 0x24, 16) + SCIF_FNS(SCLSR, 0, 0, 0x28, 16) + #else + SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16) + #if defined(CONFIG_CPU_SUBTYPE_SH7722) + SCIF_FNS(SCSPTR, 0, 0, 0, 0) + #else + SCIF_FNS(SCSPTR, 0, 0, 0x20, 16) + #endif + SCIF_FNS(SCLSR, 0, 0, 0x24, 16) + #endif + #endif + #define sci_in(port, reg) sci_##reg##_in(port) + #define sci_out(port, reg, value) sci_##reg##_out(port, value) + + /* H8/300 series SCI pins assignment */ + #if defined(__H8300H__) || defined(__H8300S__) + static const struct __attribute__((packed)) { + int port; /* GPIO port no */ + unsigned short rx,tx; /* GPIO bit no */ + } h8300_sci_pins[] = { + #if defined(CONFIG_H83007) || defined(CONFIG_H83068) + { /* SCI0 */ + .port = H8300_GPIO_P9, + .rx = H8300_GPIO_B2, + .tx = H8300_GPIO_B0, + }, + { /* SCI1 */ + .port = H8300_GPIO_P9, + .rx = H8300_GPIO_B3, + .tx = H8300_GPIO_B1, + }, + { /* SCI2 */ + .port = H8300_GPIO_PB, + .rx = H8300_GPIO_B7, + .tx = H8300_GPIO_B6, + } + #elif defined(CONFIG_H8S2678) + { /* SCI0 */ + .port = H8300_GPIO_P3, + .rx = H8300_GPIO_B2, + .tx = H8300_GPIO_B0, + }, + { /* SCI1 */ + .port = H8300_GPIO_P3, + .rx = H8300_GPIO_B3, + .tx = H8300_GPIO_B1, + }, + { /* SCI2 */ + .port = H8300_GPIO_P5, + .rx = H8300_GPIO_B1, + .tx = H8300_GPIO_B0, + } + #endif + }; + #endif + + #if defined(CONFIG_CPU_SUBTYPE_SH7706) || \ + defined(CONFIG_CPU_SUBTYPE_SH7707) || \ + defined(CONFIG_CPU_SUBTYPE_SH7708) || \ + defined(CONFIG_CPU_SUBTYPE_SH7709) + static inline int sci_rxd_in(struct uart_port *port) + { + if (port->mapbase == 0xfffffe80) + return __raw_readb(SCPDR)&0x01 ? 1 : 0; /* SCI */ + return 1; + } + #elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751) || \ + defined(CONFIG_CPU_SUBTYPE_SH7751R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7750R) || \ + defined(CONFIG_CPU_SUBTYPE_SH7750S) || \ + defined(CONFIG_CPU_SUBTYPE_SH7091) + static inline int sci_rxd_in(struct uart_port *port) + { + if (port->mapbase == 0xffe00000) + return __raw_readb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */ + return 1; + } + #elif defined(__H8300H__) || defined(__H8300S__) + static inline int sci_rxd_in(struct uart_port *port) + { + int ch = (port->mapbase - SMR0) >> 3; + return (H8300_SCI_DR(ch) & h8300_sci_pins[ch].rx) ? 1 : 0; + } + #else /* default case for non-SCI processors */ + static inline int sci_rxd_in(struct uart_port *port) + { + return 1; + } + #endif - -/* - * Values for the BitRate Register (SCBRR) - * - * The values are actually divisors for a frequency which can - * be internal to the SH3 (14.7456MHz) or derived from an external - * clock source. This driver assumes the internal clock is used; - * to support using an external clock source, config options or - * possibly command-line options would need to be added. - * - * Also, to support speeds below 2400 (why?) the lower 2 bits of - * the SCSMR register would also need to be set to non-zero values. - * - * -- Greg Banks 27Feb2000 - * - * Answer: The SCBRR register is only eight bits, and the value in - * it gets larger with lower baud rates. At around 2400 (depending on - * the peripherial module clock) you run out of bits. However the - * lower two bits of SCSMR allow the module clock to be divided down, - * scaling the value which is needed in SCBRR. - * - * -- Stuart Menefy - 23 May 2000 - * - * I meant, why would anyone bother with bitrates below 2400. - * - * -- Greg Banks - 7Jul2000 - * - * You "speedist"! How will I use my 110bps ASR-33 teletype with paper - * tape reader as a console! - * - * -- Mitch Davis - 15 Jul 2000 - */ - -#if (defined(CONFIG_CPU_SUBTYPE_SH7780) || \ - defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786)) && \ - !defined(CONFIG_SH_SH2007) -#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1) -#elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_SH7367) || \ - defined(CONFIG_ARCH_SH7377) || \ - defined(CONFIG_ARCH_SH7372) -#define SCBRR_VALUE(bps, clk) (((clk*2)+16*bps)/(32*bps)-1) -#elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ - defined(CONFIG_CPU_SUBTYPE_SH7724) -static inline int scbrr_calc(struct uart_port *port, int bps, int clk) -{ - if (port->type == PORT_SCIF) - return (clk+16*bps)/(32*bps)-1; - else - return ((clk*2)+16*bps)/(16*bps)-1; -} -#define SCBRR_VALUE(bps, clk) scbrr_calc(port, bps, clk) -#elif defined(__H8300H__) || defined(__H8300S__) -#define SCBRR_VALUE(bps, clk) (((clk*1000/32)/bps)-1) -#else /* Generic SH */ -#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(32*bps)-1) -#endif