M: Breno Leitao <leitao@linux.vnet.ibm.com>
L: linux-serial@vger.kernel.org
S: Maintained
- F: drivers/serial/jsm/
+ F: drivers/tty/serial/jsm/
+K10TEMP HARDWARE MONITORING DRIVER
+M: Clemens Ladisch <clemens@ladisch.de>
+L: lm-sensors@lm-sensors.org
+S: Maintained
+F: Documentation/hwmon/k10temp
+F: drivers/hwmon/k10temp.c
+
K8TEMP HARDWARE MONITORING DRIVER
M: Rudolf Marek <r.marek@assembler.cz>
L: lm-sensors@lm-sensors.org
--- /dev/null
+ /*
+ * 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 <linux/module.h>
+ #include <linux/tty.h>
+ #include <linux/ioport.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
+ #include <linux/serial.h>
+ #include <linux/clk.h>
+ #include <linux/console.h>
+ #include <linux/sysrq.h>
+ #include <linux/tty_flip.h>
+ #include <linux/platform_device.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/atmel_pdc.h>
+ #include <linux/atmel_serial.h>
+ #include <linux/uaccess.h>
+
+ #include <asm/io.h>
+ #include <asm/ioctls.h>
+
+ #include <asm/mach/serial_at91.h>
+ #include <mach/board.h>
+
+ #ifdef CONFIG_ARM
+ #include <mach/cpu.h>
+ #include <mach/gpio.h>
+ #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 <linux/serial_core.h>
+
+ 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");
--- /dev/null
- .dev_name = "s3c2410_serial",
+ /* 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 <linux/module.h>
+ #include <linux/ioport.h>
+ #include <linux/io.h>
+ #include <linux/platform_device.h>
+ #include <linux/init.h>
+ #include <linux/sysrq.h>
+ #include <linux/console.h>
+ #include <linux/tty.h>
+ #include <linux/tty_flip.h>
+ #include <linux/serial_core.h>
+ #include <linux/serial.h>
+ #include <linux/delay.h>
+ #include <linux/clk.h>
+ #include <linux/cpufreq.h>
+
+ #include <asm/irq.h>
+
+ #include <mach/hardware.h>
+ #include <mach/map.h>
+
+ #include <plat/regs-serial.h>
+
+ #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,
- .driver_name = S3C24XX_SERIAL_NAME,
++ .driver_name = "s3c2410_serial",
+ .nr = CONFIG_SERIAL_SAMSUNG_UARTS,
+ .cons = S3C24XX_SERIAL_CONSOLE,
++ .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 <ben@simtec.co.uk>");
+ MODULE_LICENSE("GPL v2");
--- /dev/null
- * Copyright (C) 2002 - 2008 Paul Mundt
+ /*
+ * drivers/serial/sh-sci.c
+ *
+ * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO)
+ *
- ctrl &= ~SCI_CTRL_FLAGS_TIE;
++ * 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 <linux/module.h>
+ #include <linux/errno.h>
+ #include <linux/timer.h>
+ #include <linux/interrupt.h>
+ #include <linux/tty.h>
+ #include <linux/tty_flip.h>
+ #include <linux/serial.h>
+ #include <linux/major.h>
+ #include <linux/string.h>
+ #include <linux/sysrq.h>
+ #include <linux/ioport.h>
+ #include <linux/mm.h>
+ #include <linux/init.h>
+ #include <linux/delay.h>
+ #include <linux/console.h>
+ #include <linux/platform_device.h>
+ #include <linux/serial_sci.h>
+ #include <linux/notifier.h>
+ #include <linux/cpufreq.h>
+ #include <linux/clk.h>
+ #include <linux/ctype.h>
+ #include <linux/err.h>
+ #include <linux/list.h>
+ #include <linux/dmaengine.h>
+ #include <linux/scatterlist.h>
+ #include <linux/slab.h>
+
+ #ifdef CONFIG_SUPERH
+ #include <asm/sh_bios.h>
+ #endif
+
+ #ifdef CONFIG_H8300
+ #include <asm/gpio.h>
+ #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));
+ }
+
- scr &= ~SCI_CTRL_FLAGS_RIE;
++ 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 {
- err_enabled = scr_status & (SCI_CTRL_FLAGS_REIE | 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);
- if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCI_CTRL_FLAGS_TIE) &&
++ err_enabled = scr_status & port_rx_irq_mask(port);
+
+ /* Tx Interrupt */
- (scr_status & SCI_CTRL_FLAGS_RIE))
++ 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) &&
- sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE);
++ (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);
- 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;
- unsigned short ctrl = SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE;
++
++ ctrl &= ~SCSCR_TIE;
++
+ sci_out(port, SCSCR, ctrl);
+ }
+
+ static void sci_start_rx(struct uart_port *port)
+ {
- /* Set RIE (Receive Interrupt Enable) bit in SCSCR */
- ctrl |= sci_in(port, SCSCR);
++ unsigned short ctrl;
++
++ ctrl = sci_in(port, SCSCR) | port_rx_irq_mask(port);
+
- /* Clear RIE (Receive Interrupt Enable) bit in 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;
+
- ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE);
+ ctrl = sci_in(port, SCSCR);
++
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
+ ctrl &= ~0x4000;
- sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE);
++
++ 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]);
+ }
-#ifdef CONFIG_SERIAL_SH_SCI_DMA
++ 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)
+ {
-#endif
+ struct sci_port *s = to_sci_port(port);
- t = SCBRR_VALUE(baud, port->uartclk);
+ 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))
- SCSCR_INIT(port));
++ 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,
- sci_out(port, SCSCR, 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));
+
- port->irq = p->irqs[SCIx_TXI_IRQ];
- port->flags = p->flags;
- sci_port->type = port->type = p->type;
++ 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;
++ 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");
--- /dev/null
-# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
+ #include <linux/serial_core.h>
+ #include <linux/io.h>
+ #include <linux/gpio.h>
+
+ #if defined(CONFIG_H83007) || defined(CONFIG_H83068)
+ #include <asm/regs306x.h>
+ #endif
+ #if defined(CONFIG_H8S2678)
+ #include <asm/regs267x.h>
+ #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 SCSMR_Ir 0xA44A0000
-# define IRDA_SCIF SCIF0
+ #elif defined(CONFIG_CPU_SUBTYPE_SH7705)
+ # define SCIF0 0xA4400000
+ # define SCIF2 0xA4410000
-
-/* 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
+ # define SCPCR 0xA4000116
+ # define SCPDR 0xA4000136
-# define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */
+ #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) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=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) (((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_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) 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) 0x3B
+ #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) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0,CKE=1 */
+ #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) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1,REIE=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) ((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_SH7724)
+ # 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_SH4_202)
+ # define SCSPTR2 0xffe80020 /* 16 bit SCIF */
+ # define SCIF_ORER 0x0001 /* overrun error bit */
-# define SCIF_BASE_ADDR 0x01030000
-# define SCIF_ADDR_SH5 PHYS_PERIPHERAL_BLOCK+SCIF_BASE_ADDR
+ #elif defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103)
-# define SCIF_LSR2_OFFS 0x0000024
+ # define SCIF_PTR2_OFFS 0x0000020
-# 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 */
+ # define SCSPTR2 ((port->mapbase)+SCIF_PTR2_OFFS) /* 16 bit SCIF */
-# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=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) 0x38
+ # 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 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
+ # 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) 0x3c /* TIE=0,RIE=0,TE=1,RE=1,REIE=1,cke=2 */
+ #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 */
-
-#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_SH7780)
+ # define SCSPTR0 0xffe00024 /* 16 bit SCIF */
+ # define SCSPTR1 0xffe10024 /* 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_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) 0x38 /* 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 */
-/* 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 */
-
+ #else
+ # error CPU subtype not defined
+ #endif
+
-#define SCFCR_TCRST 0x4000
+ /* 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
-/* 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 SCFCR_MCE 0x0008
+
+ #define SCI_MAJOR 204
+ #define SCI_MINOR_START 8
+
-SCIF_FNS(SCTDSR, 0x0c, 8)
-SCIF_FNS(SCFER, 0x10, 16)
+ #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(SCLSR2, 0, 0, 0x24, 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)
-
-/*
- * 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
+ 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