| /* |
| * Driver for CSR SiRFprimaII onboard UARTs. |
| * |
| * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. |
| * |
| * Licensed under GPLv2 or later. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/ioport.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/clk.h> |
| #include <linux/of.h> |
| #include <linux/slab.h> |
| #include <linux/io.h> |
| #include <asm/irq.h> |
| #include <asm/mach/irq.h> |
| #include <linux/pinctrl/consumer.h> |
| |
| #include "sirfsoc_uart.h" |
| |
| static unsigned int |
| sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count); |
| static unsigned int |
| sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count); |
| static struct uart_driver sirfsoc_uart_drv; |
| |
| static const struct sirfsoc_baudrate_to_regv baudrate_to_regv[] = { |
| {4000000, 2359296}, |
| {3500000, 1310721}, |
| {3000000, 1572865}, |
| {2500000, 1245186}, |
| {2000000, 1572866}, |
| {1500000, 1245188}, |
| {1152000, 1638404}, |
| {1000000, 1572869}, |
| {921600, 1114120}, |
| {576000, 1245196}, |
| {500000, 1245198}, |
| {460800, 1572876}, |
| {230400, 1310750}, |
| {115200, 1310781}, |
| {57600, 1310843}, |
| {38400, 1114328}, |
| {19200, 1114545}, |
| {9600, 1114979}, |
| }; |
| |
| static struct sirfsoc_uart_port sirfsoc_uart_ports[SIRFSOC_UART_NR] = { |
| [0] = { |
| .port = { |
| .iotype = UPIO_MEM, |
| .flags = UPF_BOOT_AUTOCONF, |
| .line = 0, |
| }, |
| }, |
| [1] = { |
| .port = { |
| .iotype = UPIO_MEM, |
| .flags = UPF_BOOT_AUTOCONF, |
| .line = 1, |
| }, |
| }, |
| [2] = { |
| .port = { |
| .iotype = UPIO_MEM, |
| .flags = UPF_BOOT_AUTOCONF, |
| .line = 2, |
| }, |
| }, |
| }; |
| |
| static inline struct sirfsoc_uart_port *to_sirfport(struct uart_port *port) |
| { |
| return container_of(port, struct sirfsoc_uart_port, port); |
| } |
| |
| static inline unsigned int sirfsoc_uart_tx_empty(struct uart_port *port) |
| { |
| unsigned long reg; |
| reg = rd_regl(port, SIRFUART_TX_FIFO_STATUS); |
| if (reg & SIRFUART_FIFOEMPTY_MASK(port)) |
| return TIOCSER_TEMT; |
| else |
| return 0; |
| } |
| |
| static unsigned int sirfsoc_uart_get_mctrl(struct uart_port *port) |
| { |
| struct sirfsoc_uart_port *sirfport = to_sirfport(port); |
| if (!(sirfport->ms_enabled)) { |
| goto cts_asserted; |
| } else if (sirfport->hw_flow_ctrl) { |
| if (!(rd_regl(port, SIRFUART_AFC_CTRL) & |
| SIRFUART_CTS_IN_STATUS)) |
| goto cts_asserted; |
| else |
| goto cts_deasserted; |
| } |
| cts_deasserted: |
| return TIOCM_CAR | TIOCM_DSR; |
| cts_asserted: |
| return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; |
| } |
| |
| static void sirfsoc_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) |
| { |
| struct sirfsoc_uart_port *sirfport = to_sirfport(port); |
| unsigned int assert = mctrl & TIOCM_RTS; |
| unsigned int val = assert ? SIRFUART_AFC_CTRL_RX_THD : 0x0; |
| unsigned int current_val; |
| if (sirfport->hw_flow_ctrl) { |
| current_val = rd_regl(port, SIRFUART_AFC_CTRL) & ~0xFF; |
| val |= current_val; |
| wr_regl(port, SIRFUART_AFC_CTRL, val); |
| } |
| } |
| |
| static void sirfsoc_uart_stop_tx(struct uart_port *port) |
| { |
| unsigned int regv; |
| regv = rd_regl(port, SIRFUART_INT_EN); |
| wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_TX_INT_EN); |
| } |
| |
| void sirfsoc_uart_start_tx(struct uart_port *port) |
| { |
| struct sirfsoc_uart_port *sirfport = to_sirfport(port); |
| unsigned long regv; |
| sirfsoc_uart_pio_tx_chars(sirfport, 1); |
| wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_START); |
| regv = rd_regl(port, SIRFUART_INT_EN); |
| wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_TX_INT_EN); |
| } |
| |
| static void sirfsoc_uart_stop_rx(struct uart_port *port) |
| { |
| unsigned long regv; |
| wr_regl(port, SIRFUART_RX_FIFO_OP, 0); |
| regv = rd_regl(port, SIRFUART_INT_EN); |
| wr_regl(port, SIRFUART_INT_EN, regv & ~SIRFUART_RX_IO_INT_EN); |
| } |
| |
| static void sirfsoc_uart_disable_ms(struct uart_port *port) |
| { |
| struct sirfsoc_uart_port *sirfport = to_sirfport(port); |
| unsigned long reg; |
| sirfport->ms_enabled = 0; |
| if (!sirfport->hw_flow_ctrl) |
| return; |
| reg = rd_regl(port, SIRFUART_AFC_CTRL); |
| wr_regl(port, SIRFUART_AFC_CTRL, reg & ~0x3FF); |
| reg = rd_regl(port, SIRFUART_INT_EN); |
| wr_regl(port, SIRFUART_INT_EN, reg & ~SIRFUART_CTS_INT_EN); |
| } |
| |
| static void sirfsoc_uart_enable_ms(struct uart_port *port) |
| { |
| struct sirfsoc_uart_port *sirfport = to_sirfport(port); |
| unsigned long reg; |
| unsigned long flg; |
| if (!sirfport->hw_flow_ctrl) |
| return; |
| flg = SIRFUART_AFC_RX_EN | SIRFUART_AFC_TX_EN; |
| reg = rd_regl(port, SIRFUART_AFC_CTRL); |
| wr_regl(port, SIRFUART_AFC_CTRL, reg | flg); |
| reg = rd_regl(port, SIRFUART_INT_EN); |
| wr_regl(port, SIRFUART_INT_EN, reg | SIRFUART_CTS_INT_EN); |
| uart_handle_cts_change(port, |
| !(rd_regl(port, SIRFUART_AFC_CTRL) & SIRFUART_CTS_IN_STATUS)); |
| sirfport->ms_enabled = 1; |
| } |
| |
| static void sirfsoc_uart_break_ctl(struct uart_port *port, int break_state) |
| { |
| unsigned long ulcon = rd_regl(port, SIRFUART_LINE_CTRL); |
| if (break_state) |
| ulcon |= SIRFUART_SET_BREAK; |
| else |
| ulcon &= ~SIRFUART_SET_BREAK; |
| wr_regl(port, SIRFUART_LINE_CTRL, ulcon); |
| } |
| |
| static unsigned int |
| sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count) |
| { |
| unsigned int ch, rx_count = 0; |
| struct tty_struct *tty; |
| |
| tty = tty_port_tty_get(&port->state->port); |
| if (!tty) |
| return -ENODEV; |
| |
| while (!(rd_regl(port, SIRFUART_RX_FIFO_STATUS) & |
| SIRFUART_FIFOEMPTY_MASK(port))) { |
| ch = rd_regl(port, SIRFUART_RX_FIFO_DATA) | SIRFUART_DUMMY_READ; |
| if (unlikely(uart_handle_sysrq_char(port, ch))) |
| continue; |
| uart_insert_char(port, 0, 0, ch, TTY_NORMAL); |
| rx_count++; |
| if (rx_count >= max_rx_count) |
| break; |
| } |
| |
| port->icount.rx += rx_count; |
| tty_flip_buffer_push(tty); |
| tty_kref_put(tty); |
| |
| return rx_count; |
| } |
| |
| static unsigned int |
| sirfsoc_uart_pio_tx_chars(struct sirfsoc_uart_port *sirfport, int count) |
| { |
| struct uart_port *port = &sirfport->port; |
| struct circ_buf *xmit = &port->state->xmit; |
| unsigned int num_tx = 0; |
| while (!uart_circ_empty(xmit) && |
| !(rd_regl(port, SIRFUART_TX_FIFO_STATUS) & |
| SIRFUART_FIFOFULL_MASK(port)) && |
| count--) { |
| wr_regl(port, SIRFUART_TX_FIFO_DATA, xmit->buf[xmit->tail]); |
| xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); |
| port->icount.tx++; |
| num_tx++; |
| } |
| if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) |
| uart_write_wakeup(port); |
| return num_tx; |
| } |
| |
| static irqreturn_t sirfsoc_uart_isr(int irq, void *dev_id) |
| { |
| unsigned long intr_status; |
| unsigned long cts_status; |
| unsigned long flag = TTY_NORMAL; |
| struct sirfsoc_uart_port *sirfport = (struct sirfsoc_uart_port *)dev_id; |
| struct uart_port *port = &sirfport->port; |
| struct uart_state *state = port->state; |
| struct circ_buf *xmit = &port->state->xmit; |
| intr_status = rd_regl(port, SIRFUART_INT_STATUS); |
| wr_regl(port, SIRFUART_INT_STATUS, intr_status); |
| intr_status &= rd_regl(port, SIRFUART_INT_EN); |
| if (unlikely(intr_status & (SIRFUART_ERR_INT_STAT))) { |
| if (intr_status & SIRFUART_RXD_BREAK) { |
| if (uart_handle_break(port)) |
| goto recv_char; |
| uart_insert_char(port, intr_status, |
| SIRFUART_RX_OFLOW, 0, TTY_BREAK); |
| return IRQ_HANDLED; |
| } |
| if (intr_status & SIRFUART_RX_OFLOW) |
| port->icount.overrun++; |
| if (intr_status & SIRFUART_FRM_ERR) { |
| port->icount.frame++; |
| flag = TTY_FRAME; |
| } |
| if (intr_status & SIRFUART_PARITY_ERR) |
| flag = TTY_PARITY; |
| wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); |
| wr_regl(port, SIRFUART_RX_FIFO_OP, 0); |
| wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START); |
| intr_status &= port->read_status_mask; |
| uart_insert_char(port, intr_status, |
| SIRFUART_RX_OFLOW_INT, 0, flag); |
| } |
| recv_char: |
| if (intr_status & SIRFUART_CTS_INT_EN) { |
| cts_status = !(rd_regl(port, SIRFUART_AFC_CTRL) & |
| SIRFUART_CTS_IN_STATUS); |
| if (cts_status != 0) { |
| uart_handle_cts_change(port, 1); |
| } else { |
| uart_handle_cts_change(port, 0); |
| wake_up_interruptible(&state->port.delta_msr_wait); |
| } |
| } |
| if (intr_status & SIRFUART_RX_IO_INT_EN) |
| sirfsoc_uart_pio_rx_chars(port, SIRFSOC_UART_IO_RX_MAX_CNT); |
| if (intr_status & SIRFUART_TX_INT_EN) { |
| if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { |
| return IRQ_HANDLED; |
| } else { |
| sirfsoc_uart_pio_tx_chars(sirfport, |
| SIRFSOC_UART_IO_TX_REASONABLE_CNT); |
| if ((uart_circ_empty(xmit)) && |
| (rd_regl(port, SIRFUART_TX_FIFO_STATUS) & |
| SIRFUART_FIFOEMPTY_MASK(port))) |
| sirfsoc_uart_stop_tx(port); |
| } |
| } |
| return IRQ_HANDLED; |
| } |
| |
| static void sirfsoc_uart_start_rx(struct uart_port *port) |
| { |
| unsigned long regv; |
| regv = rd_regl(port, SIRFUART_INT_EN); |
| wr_regl(port, SIRFUART_INT_EN, regv | SIRFUART_RX_IO_INT_EN); |
| wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); |
| wr_regl(port, SIRFUART_RX_FIFO_OP, 0); |
| wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_START); |
| } |
| |
| static unsigned int |
| sirfsoc_calc_sample_div(unsigned long baud_rate, |
| unsigned long ioclk_rate, unsigned long *setted_baud) |
| { |
| unsigned long min_delta = ~0UL; |
| unsigned short sample_div; |
| unsigned int regv = 0; |
| unsigned long ioclk_div; |
| unsigned long baud_tmp; |
| int temp_delta; |
| |
| for (sample_div = SIRF_MIN_SAMPLE_DIV; |
| sample_div <= SIRF_MAX_SAMPLE_DIV; sample_div++) { |
| ioclk_div = (ioclk_rate / (baud_rate * (sample_div + 1))) - 1; |
| if (ioclk_div > SIRF_IOCLK_DIV_MAX) |
| continue; |
| baud_tmp = ioclk_rate / ((ioclk_div + 1) * (sample_div + 1)); |
| temp_delta = baud_tmp - baud_rate; |
| temp_delta = (temp_delta > 0) ? temp_delta : -temp_delta; |
| if (temp_delta < min_delta) { |
| regv = regv & (~SIRF_IOCLK_DIV_MASK); |
| regv = regv | ioclk_div; |
| regv = regv & (~SIRF_SAMPLE_DIV_MASK); |
| regv = regv | (sample_div << SIRF_SAMPLE_DIV_SHIFT); |
| min_delta = temp_delta; |
| *setted_baud = baud_tmp; |
| } |
| } |
| return regv; |
| } |
| |
| static void sirfsoc_uart_set_termios(struct uart_port *port, |
| struct ktermios *termios, |
| struct ktermios *old) |
| { |
| struct sirfsoc_uart_port *sirfport = to_sirfport(port); |
| unsigned long ioclk_rate; |
| unsigned long config_reg = 0; |
| unsigned long baud_rate; |
| unsigned long setted_baud; |
| unsigned long flags; |
| unsigned long ic; |
| unsigned int clk_div_reg = 0; |
| unsigned long temp_reg_val; |
| unsigned long rx_time_out; |
| int threshold_div; |
| int temp; |
| |
| ioclk_rate = 150000000; |
| switch (termios->c_cflag & CSIZE) { |
| default: |
| case CS8: |
| config_reg |= SIRFUART_DATA_BIT_LEN_8; |
| break; |
| case CS7: |
| config_reg |= SIRFUART_DATA_BIT_LEN_7; |
| break; |
| case CS6: |
| config_reg |= SIRFUART_DATA_BIT_LEN_6; |
| break; |
| case CS5: |
| config_reg |= SIRFUART_DATA_BIT_LEN_5; |
| break; |
| } |
| if (termios->c_cflag & CSTOPB) |
| config_reg |= SIRFUART_STOP_BIT_LEN_2; |
| baud_rate = uart_get_baud_rate(port, termios, old, 0, 4000000); |
| spin_lock_irqsave(&port->lock, flags); |
| port->read_status_mask = SIRFUART_RX_OFLOW_INT; |
| port->ignore_status_mask = 0; |
| /* read flags */ |
| if (termios->c_iflag & INPCK) |
| port->read_status_mask |= |
| SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT; |
| if (termios->c_iflag & (BRKINT | PARMRK)) |
| port->read_status_mask |= SIRFUART_RXD_BREAK_INT; |
| /* ignore flags */ |
| if (termios->c_iflag & IGNPAR) |
| port->ignore_status_mask |= |
| SIRFUART_FRM_ERR_INT | SIRFUART_PARITY_ERR_INT; |
| if ((termios->c_cflag & CREAD) == 0) |
| port->ignore_status_mask |= SIRFUART_DUMMY_READ; |
| /* enable parity if PARENB is set*/ |
| if (termios->c_cflag & PARENB) { |
| if (termios->c_cflag & CMSPAR) { |
| if (termios->c_cflag & PARODD) |
| config_reg |= SIRFUART_STICK_BIT_MARK; |
| else |
| config_reg |= SIRFUART_STICK_BIT_SPACE; |
| } else if (termios->c_cflag & PARODD) { |
| config_reg |= SIRFUART_STICK_BIT_ODD; |
| } else { |
| config_reg |= SIRFUART_STICK_BIT_EVEN; |
| } |
| } |
| /* Hardware Flow Control Settings */ |
| if (UART_ENABLE_MS(port, termios->c_cflag)) { |
| if (!sirfport->ms_enabled) |
| sirfsoc_uart_enable_ms(port); |
| } else { |
| if (sirfport->ms_enabled) |
| sirfsoc_uart_disable_ms(port); |
| } |
| |
| /* common rate: fast calculation */ |
| for (ic = 0; ic < SIRF_BAUD_RATE_SUPPORT_NR; ic++) |
| if (baud_rate == baudrate_to_regv[ic].baud_rate) |
| clk_div_reg = baudrate_to_regv[ic].reg_val; |
| setted_baud = baud_rate; |
| /* arbitary rate setting */ |
| if (unlikely(clk_div_reg == 0)) |
| clk_div_reg = sirfsoc_calc_sample_div(baud_rate, ioclk_rate, |
| &setted_baud); |
| wr_regl(port, SIRFUART_DIVISOR, clk_div_reg); |
| |
| if (tty_termios_baud_rate(termios)) |
| tty_termios_encode_baud_rate(termios, setted_baud, setted_baud); |
| |
| /* set receive timeout */ |
| rx_time_out = SIRFSOC_UART_RX_TIMEOUT(baud_rate, 20000); |
| rx_time_out = (rx_time_out > 0xFFFF) ? 0xFFFF : rx_time_out; |
| config_reg |= SIRFUART_RECV_TIMEOUT(rx_time_out); |
| temp_reg_val = rd_regl(port, SIRFUART_TX_FIFO_OP); |
| wr_regl(port, SIRFUART_RX_FIFO_OP, 0); |
| wr_regl(port, SIRFUART_TX_FIFO_OP, |
| temp_reg_val & ~SIRFUART_TX_FIFO_START); |
| wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, SIRFUART_TX_MODE_IO); |
| wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, SIRFUART_RX_MODE_IO); |
| wr_regl(port, SIRFUART_LINE_CTRL, config_reg); |
| |
| /* Reset Rx/Tx FIFO Threshold level for proper baudrate */ |
| if (baud_rate < 1000000) |
| threshold_div = 1; |
| else |
| threshold_div = 2; |
| temp = port->line == 1 ? 16 : 64; |
| wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp / threshold_div); |
| wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp / threshold_div); |
| temp_reg_val |= SIRFUART_TX_FIFO_START; |
| wr_regl(port, SIRFUART_TX_FIFO_OP, temp_reg_val); |
| uart_update_timeout(port, termios->c_cflag, baud_rate); |
| sirfsoc_uart_start_rx(port); |
| wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_TX_EN | SIRFUART_RX_EN); |
| spin_unlock_irqrestore(&port->lock, flags); |
| } |
| |
| static void startup_uart_controller(struct uart_port *port) |
| { |
| unsigned long temp_regv; |
| int temp; |
| temp_regv = rd_regl(port, SIRFUART_TX_DMA_IO_CTRL); |
| wr_regl(port, SIRFUART_TX_DMA_IO_CTRL, temp_regv | SIRFUART_TX_MODE_IO); |
| temp_regv = rd_regl(port, SIRFUART_RX_DMA_IO_CTRL); |
| wr_regl(port, SIRFUART_RX_DMA_IO_CTRL, temp_regv | SIRFUART_RX_MODE_IO); |
| wr_regl(port, SIRFUART_TX_DMA_IO_LEN, 0); |
| wr_regl(port, SIRFUART_RX_DMA_IO_LEN, 0); |
| wr_regl(port, SIRFUART_TX_RX_EN, SIRFUART_RX_EN | SIRFUART_TX_EN); |
| wr_regl(port, SIRFUART_TX_FIFO_OP, SIRFUART_TX_FIFO_RESET); |
| wr_regl(port, SIRFUART_TX_FIFO_OP, 0); |
| wr_regl(port, SIRFUART_RX_FIFO_OP, SIRFUART_RX_FIFO_RESET); |
| wr_regl(port, SIRFUART_RX_FIFO_OP, 0); |
| temp = port->line == 1 ? 16 : 64; |
| wr_regl(port, SIRFUART_TX_FIFO_CTRL, temp); |
| wr_regl(port, SIRFUART_RX_FIFO_CTRL, temp); |
| } |
| |
| static int sirfsoc_uart_startup(struct uart_port *port) |
| { |
| struct sirfsoc_uart_port *sirfport = to_sirfport(port); |
| unsigned int index = port->line; |
| int ret; |
| set_irq_flags(port->irq, IRQF_VALID | IRQF_NOAUTOEN); |
| ret = request_irq(port->irq, |
| sirfsoc_uart_isr, |
| 0, |
| SIRFUART_PORT_NAME, |
| sirfport); |
| if (ret != 0) { |
| dev_err(port->dev, "UART%d request IRQ line (%d) failed.\n", |
| index, port->irq); |
| goto irq_err; |
| } |
| startup_uart_controller(port); |
| enable_irq(port->irq); |
| irq_err: |
| return ret; |
| } |
| |
| static void sirfsoc_uart_shutdown(struct uart_port *port) |
| { |
| struct sirfsoc_uart_port *sirfport = to_sirfport(port); |
| wr_regl(port, SIRFUART_INT_EN, 0); |
| free_irq(port->irq, sirfport); |
| if (sirfport->ms_enabled) { |
| sirfsoc_uart_disable_ms(port); |
| sirfport->ms_enabled = 0; |
| } |
| } |
| |
| static const char *sirfsoc_uart_type(struct uart_port *port) |
| { |
| return port->type == SIRFSOC_PORT_TYPE ? SIRFUART_PORT_NAME : NULL; |
| } |
| |
| static int sirfsoc_uart_request_port(struct uart_port *port) |
| { |
| void *ret; |
| ret = request_mem_region(port->mapbase, |
| SIRFUART_MAP_SIZE, SIRFUART_PORT_NAME); |
| return ret ? 0 : -EBUSY; |
| } |
| |
| static void sirfsoc_uart_release_port(struct uart_port *port) |
| { |
| release_mem_region(port->mapbase, SIRFUART_MAP_SIZE); |
| } |
| |
| static void sirfsoc_uart_config_port(struct uart_port *port, int flags) |
| { |
| if (flags & UART_CONFIG_TYPE) { |
| port->type = SIRFSOC_PORT_TYPE; |
| sirfsoc_uart_request_port(port); |
| } |
| } |
| |
| static struct uart_ops sirfsoc_uart_ops = { |
| .tx_empty = sirfsoc_uart_tx_empty, |
| .get_mctrl = sirfsoc_uart_get_mctrl, |
| .set_mctrl = sirfsoc_uart_set_mctrl, |
| .stop_tx = sirfsoc_uart_stop_tx, |
| .start_tx = sirfsoc_uart_start_tx, |
| .stop_rx = sirfsoc_uart_stop_rx, |
| .enable_ms = sirfsoc_uart_enable_ms, |
| .break_ctl = sirfsoc_uart_break_ctl, |
| .startup = sirfsoc_uart_startup, |
| .shutdown = sirfsoc_uart_shutdown, |
| .set_termios = sirfsoc_uart_set_termios, |
| .type = sirfsoc_uart_type, |
| .release_port = sirfsoc_uart_release_port, |
| .request_port = sirfsoc_uart_request_port, |
| .config_port = sirfsoc_uart_config_port, |
| }; |
| |
| #ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE |
| static int __init sirfsoc_uart_console_setup(struct console *co, char *options) |
| { |
| unsigned int baud = 115200; |
| unsigned int bits = 8; |
| unsigned int parity = 'n'; |
| unsigned int flow = 'n'; |
| struct uart_port *port = &sirfsoc_uart_ports[co->index].port; |
| |
| if (co->index < 0 || co->index >= SIRFSOC_UART_NR) |
| return -EINVAL; |
| |
| if (!port->mapbase) |
| return -ENODEV; |
| |
| if (options) |
| uart_parse_options(options, &baud, &parity, &bits, &flow); |
| port->cons = co; |
| return uart_set_options(port, co, baud, parity, bits, flow); |
| } |
| |
| static void sirfsoc_uart_console_putchar(struct uart_port *port, int ch) |
| { |
| while (rd_regl(port, |
| SIRFUART_TX_FIFO_STATUS) & SIRFUART_FIFOFULL_MASK(port)) |
| cpu_relax(); |
| wr_regb(port, SIRFUART_TX_FIFO_DATA, ch); |
| } |
| |
| static void sirfsoc_uart_console_write(struct console *co, const char *s, |
| unsigned int count) |
| { |
| struct uart_port *port = &sirfsoc_uart_ports[co->index].port; |
| uart_console_write(port, s, count, sirfsoc_uart_console_putchar); |
| } |
| |
| static struct console sirfsoc_uart_console = { |
| .name = SIRFSOC_UART_NAME, |
| .device = uart_console_device, |
| .flags = CON_PRINTBUFFER, |
| .index = -1, |
| .write = sirfsoc_uart_console_write, |
| .setup = sirfsoc_uart_console_setup, |
| .data = &sirfsoc_uart_drv, |
| }; |
| |
| static int __init sirfsoc_uart_console_init(void) |
| { |
| register_console(&sirfsoc_uart_console); |
| return 0; |
| } |
| console_initcall(sirfsoc_uart_console_init); |
| #endif |
| |
| static struct uart_driver sirfsoc_uart_drv = { |
| .owner = THIS_MODULE, |
| .driver_name = SIRFUART_PORT_NAME, |
| .nr = SIRFSOC_UART_NR, |
| .dev_name = SIRFSOC_UART_NAME, |
| .major = SIRFSOC_UART_MAJOR, |
| .minor = SIRFSOC_UART_MINOR, |
| #ifdef CONFIG_SERIAL_SIRFSOC_CONSOLE |
| .cons = &sirfsoc_uart_console, |
| #else |
| .cons = NULL, |
| #endif |
| }; |
| |
| int sirfsoc_uart_probe(struct platform_device *pdev) |
| { |
| struct sirfsoc_uart_port *sirfport; |
| struct uart_port *port; |
| struct resource *res; |
| int ret; |
| |
| if (of_property_read_u32(pdev->dev.of_node, "cell-index", &pdev->id)) { |
| dev_err(&pdev->dev, |
| "Unable to find cell-index in uart node.\n"); |
| ret = -EFAULT; |
| goto err; |
| } |
| |
| sirfport = &sirfsoc_uart_ports[pdev->id]; |
| port = &sirfport->port; |
| port->dev = &pdev->dev; |
| port->private_data = sirfport; |
| |
| if (of_find_property(pdev->dev.of_node, "hw_flow_ctrl", NULL)) |
| sirfport->hw_flow_ctrl = 1; |
| |
| if (of_property_read_u32(pdev->dev.of_node, |
| "fifosize", |
| &port->fifosize)) { |
| dev_err(&pdev->dev, |
| "Unable to find fifosize in uart node.\n"); |
| ret = -EFAULT; |
| goto err; |
| } |
| |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (res == NULL) { |
| dev_err(&pdev->dev, "Insufficient resources.\n"); |
| ret = -EFAULT; |
| goto err; |
| } |
| port->mapbase = res->start; |
| port->membase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); |
| if (!port->membase) { |
| dev_err(&pdev->dev, "Cannot remap resource.\n"); |
| ret = -ENOMEM; |
| goto err; |
| } |
| res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
| if (res == NULL) { |
| dev_err(&pdev->dev, "Insufficient resources.\n"); |
| ret = -EFAULT; |
| goto irq_err; |
| } |
| port->irq = res->start; |
| |
| if (sirfport->hw_flow_ctrl) { |
| sirfport->p = pinctrl_get_select_default(&pdev->dev); |
| ret = IS_ERR(sirfport->p); |
| if (ret) |
| goto pin_err; |
| } |
| |
| port->ops = &sirfsoc_uart_ops; |
| spin_lock_init(&port->lock); |
| |
| platform_set_drvdata(pdev, sirfport); |
| ret = uart_add_one_port(&sirfsoc_uart_drv, port); |
| if (ret != 0) { |
| dev_err(&pdev->dev, "Cannot add UART port(%d).\n", pdev->id); |
| goto port_err; |
| } |
| |
| return 0; |
| |
| port_err: |
| platform_set_drvdata(pdev, NULL); |
| if (sirfport->hw_flow_ctrl) |
| pinctrl_put(sirfport->p); |
| pin_err: |
| irq_err: |
| devm_iounmap(&pdev->dev, port->membase); |
| err: |
| return ret; |
| } |
| |
| static int sirfsoc_uart_remove(struct platform_device *pdev) |
| { |
| struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); |
| struct uart_port *port = &sirfport->port; |
| platform_set_drvdata(pdev, NULL); |
| if (sirfport->hw_flow_ctrl) |
| pinctrl_put(sirfport->p); |
| devm_iounmap(&pdev->dev, port->membase); |
| uart_remove_one_port(&sirfsoc_uart_drv, port); |
| return 0; |
| } |
| |
| static int |
| sirfsoc_uart_suspend(struct platform_device *pdev, pm_message_t state) |
| { |
| struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); |
| struct uart_port *port = &sirfport->port; |
| uart_suspend_port(&sirfsoc_uart_drv, port); |
| return 0; |
| } |
| |
| static int sirfsoc_uart_resume(struct platform_device *pdev) |
| { |
| struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev); |
| struct uart_port *port = &sirfport->port; |
| uart_resume_port(&sirfsoc_uart_drv, port); |
| return 0; |
| } |
| |
| static struct of_device_id sirfsoc_uart_ids[] __devinitdata = { |
| { .compatible = "sirf,prima2-uart", }, |
| {} |
| }; |
| MODULE_DEVICE_TABLE(of, sirfsoc_serial_of_match); |
| |
| static struct platform_driver sirfsoc_uart_driver = { |
| .probe = sirfsoc_uart_probe, |
| .remove = __devexit_p(sirfsoc_uart_remove), |
| .suspend = sirfsoc_uart_suspend, |
| .resume = sirfsoc_uart_resume, |
| .driver = { |
| .name = SIRFUART_PORT_NAME, |
| .owner = THIS_MODULE, |
| .of_match_table = sirfsoc_uart_ids, |
| }, |
| }; |
| |
| static int __init sirfsoc_uart_init(void) |
| { |
| int ret = 0; |
| |
| ret = uart_register_driver(&sirfsoc_uart_drv); |
| if (ret) |
| goto out; |
| |
| ret = platform_driver_register(&sirfsoc_uart_driver); |
| if (ret) |
| uart_unregister_driver(&sirfsoc_uart_drv); |
| out: |
| return ret; |
| } |
| module_init(sirfsoc_uart_init); |
| |
| static void __exit sirfsoc_uart_exit(void) |
| { |
| platform_driver_unregister(&sirfsoc_uart_driver); |
| uart_unregister_driver(&sirfsoc_uart_drv); |
| } |
| module_exit(sirfsoc_uart_exit); |
| |
| MODULE_LICENSE("GPL v2"); |
| MODULE_AUTHOR("Bin Shi <Bin.Shi@csr.com>, Rong Wang<Rong.Wang@csr.com>"); |
| MODULE_DESCRIPTION("CSR SiRFprimaII Uart Driver"); |