linux/drivers/tty/serial/owl-uart.c
<<
>>
Prefs
   1/*
   2 * Actions Semi Owl family serial console
   3 *
   4 * Copyright 2013 Actions Semi Inc.
   5 * Author: Actions Semi, Inc.
   6 *
   7 * Copyright (c) 2016-2017 Andreas Färber
   8 *
   9 * This program is free software; you can redistribute  it and/or modify it
  10 * under  the terms of  the GNU General  Public License as published by the
  11 * Free Software Foundation;  either version 2 of the  License, or (at your
  12 * option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17 * GNU General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21 */
  22
  23#include <linux/console.h>
  24#include <linux/delay.h>
  25#include <linux/io.h>
  26#include <linux/module.h>
  27#include <linux/of.h>
  28#include <linux/platform_device.h>
  29#include <linux/serial.h>
  30#include <linux/serial_core.h>
  31
  32#define OWL_UART_CTL    0x000
  33#define OWL_UART_TXDAT  0x008
  34#define OWL_UART_STAT   0x00c
  35
  36#define OWL_UART_CTL_TRFS_TX            BIT(14)
  37#define OWL_UART_CTL_EN                 BIT(15)
  38#define OWL_UART_CTL_RXIE               BIT(18)
  39#define OWL_UART_CTL_TXIE               BIT(19)
  40
  41#define OWL_UART_STAT_RIP               BIT(0)
  42#define OWL_UART_STAT_TIP               BIT(1)
  43#define OWL_UART_STAT_TFFU              BIT(6)
  44#define OWL_UART_STAT_TRFL_MASK         (0x1f << 11)
  45#define OWL_UART_STAT_UTBB              BIT(17)
  46
  47static inline void owl_uart_write(struct uart_port *port, u32 val, unsigned int off)
  48{
  49        writel(val, port->membase + off);
  50}
  51
  52static inline u32 owl_uart_read(struct uart_port *port, unsigned int off)
  53{
  54        return readl(port->membase + off);
  55}
  56
  57#ifdef CONFIG_SERIAL_OWL_CONSOLE
  58
  59static void owl_console_putchar(struct uart_port *port, int ch)
  60{
  61        if (!port->membase)
  62                return;
  63
  64        while (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TFFU)
  65                cpu_relax();
  66
  67        owl_uart_write(port, ch, OWL_UART_TXDAT);
  68}
  69
  70static void owl_uart_port_write(struct uart_port *port, const char *s,
  71                                u_int count)
  72{
  73        u32 old_ctl, val;
  74        unsigned long flags;
  75        int locked;
  76
  77        local_irq_save(flags);
  78
  79        if (port->sysrq)
  80                locked = 0;
  81        else if (oops_in_progress)
  82                locked = spin_trylock(&port->lock);
  83        else {
  84                spin_lock(&port->lock);
  85                locked = 1;
  86        }
  87
  88        old_ctl = owl_uart_read(port, OWL_UART_CTL);
  89        val = old_ctl | OWL_UART_CTL_TRFS_TX;
  90        /* disable IRQ */
  91        val &= ~(OWL_UART_CTL_RXIE | OWL_UART_CTL_TXIE);
  92        owl_uart_write(port, val, OWL_UART_CTL);
  93
  94        uart_console_write(port, s, count, owl_console_putchar);
  95
  96        /* wait until all contents have been sent out */
  97        while (owl_uart_read(port, OWL_UART_STAT) & OWL_UART_STAT_TRFL_MASK)
  98                cpu_relax();
  99
 100        /* clear IRQ pending */
 101        val = owl_uart_read(port, OWL_UART_STAT);
 102        val |= OWL_UART_STAT_TIP | OWL_UART_STAT_RIP;
 103        owl_uart_write(port, val, OWL_UART_STAT);
 104
 105        owl_uart_write(port, old_ctl, OWL_UART_CTL);
 106
 107        if (locked)
 108                spin_unlock(&port->lock);
 109
 110        local_irq_restore(flags);
 111}
 112
 113static void owl_uart_early_console_write(struct console *co,
 114                                         const char *s,
 115                                         u_int count)
 116{
 117        struct earlycon_device *dev = co->data;
 118
 119        owl_uart_port_write(&dev->port, s, count);
 120}
 121
 122static int __init
 123owl_uart_early_console_setup(struct earlycon_device *device, const char *opt)
 124{
 125        if (!device->port.membase)
 126                return -ENODEV;
 127
 128        device->con->write = owl_uart_early_console_write;
 129
 130        return 0;
 131}
 132OF_EARLYCON_DECLARE(owl, "actions,owl-uart",
 133                    owl_uart_early_console_setup);
 134
 135#endif /* CONFIG_SERIAL_OWL_CONSOLE */
 136