Newer
Older
/* 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;
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;
mode |= ATMEL_US_PAR_SPACE;
mode |= ATMEL_US_PAR_ODD;
mode |= ATMEL_US_PAR_EVEN;
mode |= ATMEL_US_PAR_NONE;
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 & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= ATMEL_US_RXBRK;
if (atmel_use_pdc_rx(port))
atmel_uart_writel(port, ATMEL_US_IER, 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 = atmel_uart_readl(port, ATMEL_US_IMR);
atmel_uart_writel(port, ATMEL_US_IDR, -1);
/* disable receiver and transmitter */
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
Cyrille Pitchen
committed
/* mode */
if (port->rs485.flags & SER_RS485_ENABLED) {
atmel_uart_writel(port, ATMEL_US_TTGR,
port->rs485.delay_rts_after_send);
mode |= ATMEL_US_USMODE_RS485;
Cyrille Pitchen
committed
} else if (termios->c_cflag & CRTSCTS) {
/* RS232 with hardware handshake (RTS/CTS) */
mode |= ATMEL_US_USMODE_HWHS;
} else {
/* RS232 without hadware handshake */
mode |= ATMEL_US_USMODE_NORMAL;
Cyrille Pitchen
committed
/* set the mode, clock divisor, parity, stop bits and data size */
atmel_uart_writel(port, ATMEL_US_MR, mode);
Cyrille Pitchen
committed
/*
* when switching the mode, set the RTS line state according to the
* new mode, otherwise keep the former state
*/
if ((old_mode & ATMEL_US_USMODE) != (mode & ATMEL_US_USMODE)) {
unsigned int rts_state;
if ((mode & ATMEL_US_USMODE) == ATMEL_US_USMODE_HWHS) {
/* let the hardware control the RTS line */
rts_state = ATMEL_US_RTSDIS;
} else {
/* force RTS line to low level */
rts_state = ATMEL_US_RTSEN;
}
atmel_uart_writel(port, ATMEL_US_CR, rts_state);
Cyrille Pitchen
committed
}
/* set the baud rate */
atmel_uart_writel(port, ATMEL_US_BRGR, quot);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_TXEN | ATMEL_US_RXEN);
/* restore interrupts */
atmel_uart_writel(port, ATMEL_US_IER, imr);
/* CTS flow-control and modem-status interrupts */
if (UART_ENABLE_MS(port, termios->c_cflag))
atmel_enable_ms(port);
else
atmel_disable_ms(port);
spin_unlock_irqrestore(&port->lock, flags);
}
static void atmel_set_ldisc(struct uart_port *port, struct ktermios *termios)
if (termios->c_line == N_PPS) {
spin_lock_irq(&port->lock);
spin_unlock_irq(&port->lock);
} else {
port->flags &= ~UPF_HARDPPS_CD;
if (!UART_ENABLE_MS(port, termios->c_cflag)) {
spin_lock_irq(&port->lock);
atmel_disable_ms(port);
spin_unlock_irq(&port->lock);
}
/*
* 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;
}
}
}
/*
* 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)
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 (port->mapbase != (unsigned long)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 (!(atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_RXRDY))
cpu_relax();
return atmel_uart_read_char(port);
}
static void atmel_poll_put_char(struct uart_port *port, unsigned char ch)
{
while (!(atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXRDY))
cpu_relax();
atmel_uart_write_char(port, ch);
}
#endif
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,
#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 int atmel_init_port(struct atmel_uart_port *atmel_port,
struct uart_port *port = &atmel_port->uart;
struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
atmel_init_property(atmel_port, pdev);
atmel_set_ops(port);
atmel_init_rs485(port, pdev);
port->iotype = UPIO_MEM;
port->flags = UPF_BOOT_AUTOCONF;
port->ops = &atmel_pops;
port->fifosize = 1;
port->dev = &pdev->dev;
port->mapbase = pdev->resource[0].start;
port->irq = pdev->resource[1].start;
port->rs485_config = atmel_config_rs485;
tasklet_init(&atmel_port->tasklet, atmel_tasklet_func,
(unsigned long)port);
tasklet_disable(&atmel_port->tasklet);
memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
if (pdata && pdata->regs) {
Haavard Skinnemoen
committed
/* Already mapped by setup code */
port->membase = pdata->regs;
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");
if (IS_ERR(atmel_port->clk)) {
ret = PTR_ERR(atmel_port->clk);
atmel_port->clk = NULL;
return ret;
}
ret = clk_prepare_enable(atmel_port->clk);
if (ret) {
clk_put(atmel_port->clk);
atmel_port->clk = NULL;
return ret;
}
port->uartclk = clk_get_rate(atmel_port->clk);
clk_disable_unprepare(atmel_port->clk);
/* only enable clock when USART is in use */
/* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */
if (port->rs485.flags & SER_RS485_ENABLED)
atmel_port->tx_done_mask = ATMEL_US_TXEMPTY;
else if (atmel_use_pdc_tx(port)) {
atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE;
} else {
atmel_port->tx_done_mask = ATMEL_US_TXRDY;
}
Jean-Christophe PLAGNIOL-VILLARD
committed
struct platform_device *atmel_default_console_device; /* the serial console device */
#ifdef CONFIG_SERIAL_ATMEL_CONSOLE
static void atmel_console_putchar(struct uart_port *port, int ch)
while (!(atmel_uart_readl(port, ATMEL_US_CSR) & ATMEL_US_TXRDY))
atmel_uart_write_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;
* First, save IMR and then disable interrupts
imr = atmel_uart_readl(port, ATMEL_US_IMR);
atmel_uart_writel(port, ATMEL_US_IDR,
ATMEL_US_RXRDY | atmel_port->tx_done_mask);
/* Store PDC transmit status and disable it */
pdc_tx = atmel_uart_readl(port, ATMEL_PDC_PTSR) & ATMEL_PDC_TXTEN;
atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS);
uart_console_write(port, s, count, atmel_console_putchar);
* Finally, wait for transmitter to become empty
* and restore IMR
status = atmel_uart_readl(port, ATMEL_US_CSR);
} while (!(status & ATMEL_US_TXRDY));
/* Restore PDC transmit status */
if (pdc_tx)
atmel_uart_writel(port, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
/* set interrupts back the way they were */
atmel_uart_writel(port, ATMEL_US_IER, 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 = atmel_uart_readl(port, ATMEL_US_BRGR) & ATMEL_US_CD;
if (!quot)
return;
mr = atmel_uart_readl(port, ATMEL_US_MR) & ATMEL_US_CHRL;
if (mr == ATMEL_US_CHRL_8)
*bits = 8;
else
*bits = 7;
mr = atmel_uart_readl(port, ATMEL_US_MR) & ATMEL_US_PAR;
if (mr == ATMEL_US_PAR_EVEN)
else if (mr == ATMEL_US_PAR_ODD)
/*
* 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 */
ret = clk_prepare_enable(atmel_ports[co->index].clk);
if (ret)
return ret;
atmel_uart_writel(port, ATMEL_US_IDR, -1);
atmel_uart_writel(port, ATMEL_US_CR, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
atmel_uart_writel(port, ATMEL_US_CR, 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) {
struct atmel_uart_data *pdata =
dev_get_platdata(&atmel_default_console_device->dev);
Linus Torvalds
committed
int id = pdata->num;
struct atmel_uart_port *atmel_port = &atmel_ports[id];
atmel_port->backup_imr = 0;
atmel_port->uart.line = id;
add_preferred_console(ATMEL_DEVICENAME, id, NULL);
ret = atmel_init_port(atmel_port, atmel_default_console_device);
if (ret)
return ret;
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);
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;
}
#define ATMEL_CONSOLE_DEVICE NULL
static inline bool atmel_is_console_port(struct uart_port *port)
{
return false;
}
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,
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 (!(atmel_uart_readl(port, ATMEL_US_CSR) &
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()) {
unsigned long flags;
spin_lock_irqsave(&atmel_port->lock_suspended, flags);
atmel_port->suspended = true;
spin_unlock_irqrestore(&atmel_port->lock_suspended, flags);
device_set_wakeup_enable(&pdev->dev, 0);
uart_suspend_port(&atmel_uart, port);
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);
unsigned long flags;
spin_lock_irqsave(&atmel_port->lock_suspended, flags);
if (atmel_port->pending) {
atmel_handle_receive(port, atmel_port->pending);
atmel_handle_status(port, atmel_port->pending,
atmel_port->pending_status);
atmel_handle_transmit(port, atmel_port->pending);
atmel_port->pending = 0;
}
atmel_port->suspended = false;
spin_unlock_irqrestore(&atmel_port->lock_suspended, flags);
uart_resume_port(&atmel_uart, port);
device_set_wakeup_enable(&pdev->dev, atmel_port->may_wakeup);
return 0;
}
#define atmel_serial_suspend NULL
#define atmel_serial_resume NULL
static void atmel_serial_probe_fifos(struct atmel_uart_port *atmel_port,
struct platform_device *pdev)
{
atmel_port->fifo_size = 0;
atmel_port->rts_low = 0;
atmel_port->rts_high = 0;
if (of_property_read_u32(pdev->dev.of_node,
"atmel,fifo-size",
if (atmel_port->fifo_size < ATMEL_MIN_FIFO_SIZE) {
atmel_port->fifo_size = 0;
dev_err(&pdev->dev, "Invalid FIFO size\n");
return;
}
/*
* 0 <= rts_low <= rts_high <= fifo_size
* Once their CTS line asserted by the remote peer, some x86 UARTs tend
* to flush their internal TX FIFO, commonly up to 16 data, before
* actually stopping to send new data. So we try to set the RTS High
* Threshold to a reasonably high value respecting this 16 data
* empirical rule when possible.
*/
atmel_port->rts_high = max_t(int, atmel_port->fifo_size >> 1,
atmel_port->fifo_size - ATMEL_RTS_HIGH_OFFSET);
atmel_port->rts_low = max_t(int, atmel_port->fifo_size >> 2,
atmel_port->fifo_size - ATMEL_RTS_LOW_OFFSET);
dev_info(&pdev->dev, "Using FIFO (%u data)\n",
dev_dbg(&pdev->dev, "RTS High Threshold : %2u data\n",
dev_dbg(&pdev->dev, "RTS Low Threshold : %2u data\n",
static int atmel_serial_probe(struct platform_device *pdev)
struct atmel_uart_port *atmel_port;
struct device_node *np = pdev->dev.of_node;
struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev);
bool rs485_enabled;
BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1));
if (np)
ret = of_alias_get_id(np, "serial");
else
if (pdata)
ret = pdata->num;
/* port id not found in platform data nor device-tree aliases:
ret = find_first_zero_bit(atmel_ports_in_use, ATMEL_MAX_UART);
if (ret >= ATMEL_MAX_UART) {
ret = -ENODEV;
goto err;
}
if (test_and_set_bit(ret, atmel_ports_in_use)) {
/* port already in use */
ret = -EBUSY;
goto err;
}
atmel_port = &atmel_ports[ret];
atmel_port->backup_imr = 0;
atmel_port->uart.line = ret;
atmel_serial_probe_fifos(atmel_port, pdev);
spin_lock_init(&atmel_port->lock_suspended);
ret = atmel_init_port(atmel_port, pdev);
goto err_clear_bit;
atmel_port->gpios = mctrl_gpio_init(&atmel_port->uart, 0);
if (IS_ERR(atmel_port->gpios)) {
ret = PTR_ERR(atmel_port->gpios);
goto err_clear_bit;
}
if (!atmel_use_pdc_rx(&atmel_port->uart)) {
data = kmalloc(sizeof(struct atmel_uart_char)
* ATMEL_SERIAL_RINGSIZE, GFP_KERNEL);
atmel_port->rx_ring.buf = data;
rs485_enabled = atmel_port->uart.rs485.flags & SER_RS485_ENABLED;
ret = uart_add_one_port(&atmel_uart, &atmel_port->uart);
if (ret)
goto err_add_port;
Albin Tonnerre
committed
#ifdef CONFIG_SERIAL_ATMEL_CONSOLE
if (atmel_is_console_port(&atmel_port->uart)
&& ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) {
/*
* The serial core enabled the clock for us, so undo
* the clk_prepare_enable() in atmel_console_setup()
clk_disable_unprepare(atmel_port->clk);
Albin Tonnerre
committed
#endif
device_init_wakeup(&pdev->dev, 1);
platform_set_drvdata(pdev, atmel_port);
/*
* The peripheral clock has been disabled by atmel_init_port():
* enable it before accessing I/O registers
*/
clk_prepare_enable(atmel_port->clk);
if (rs485_enabled) {
atmel_uart_writel(&atmel_port->uart, ATMEL_US_MR,
ATMEL_US_USMODE_NORMAL);
atmel_uart_writel(&atmel_port->uart, ATMEL_US_CR,
ATMEL_US_RTSEN);
}
/*
* Get port name of usart or uart
*/
atmel_get_ip_name(&atmel_port->uart);
/*
* The peripheral clock can now safely be disabled till the port
* is used
*/
clk_disable_unprepare(atmel_port->clk);
return 0;
err_add_port:
kfree(atmel_port->rx_ring.buf);
atmel_port->rx_ring.buf = NULL;
if (!atmel_is_console_port(&atmel_port->uart)) {
clk_put(atmel_port->clk);
atmel_port->clk = NULL;
err_clear_bit:
clear_bit(atmel_port->uart.line, atmel_ports_in_use);
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
/*
* Even if the driver is not modular, it makes sense to be able to
* unbind a device: there can be many bound devices, and there are
* situations where dynamic binding and unbinding can be useful.
*
* For example, a connected device can require a specific firmware update
* protocol that needs bitbanging on IO lines, but use the regular serial
* port in the normal case.
*/
static int 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;
tasklet_kill(&atmel_port->tasklet);
device_init_wakeup(&pdev->dev, 0);
ret = uart_remove_one_port(&atmel_uart, port);
kfree(atmel_port->rx_ring.buf);
/* "port" is allocated statically, so we shouldn't free it */
clear_bit(port->line, atmel_ports_in_use);
clk_put(atmel_port->clk);
atmel_port->clk = NULL;
return ret;
}
static struct platform_driver atmel_serial_driver = {
.probe = atmel_serial_probe,
.remove = atmel_serial_remove,
.suspend = atmel_serial_suspend,
.resume = atmel_serial_resume,
.name = "atmel_usart",
.of_match_table = of_match_ptr(atmel_serial_dt_ids),
static int __init atmel_serial_init(void)
ret = uart_register_driver(&atmel_uart);
if (ret)
return ret;
ret = platform_driver_register(&atmel_serial_driver);
uart_unregister_driver(&atmel_uart);
device_initcall(atmel_serial_init);