[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [RFC 25/29] xen/arm: Add exynos 4210 UART support
Signed-off-by: Julien Grall <julien.grall@xxxxxxxxxx> --- config/arm32.mk | 1 + xen/drivers/char/Makefile | 1 + xen/drivers/char/exynos5-uart.c | 346 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 348 insertions(+) create mode 100644 xen/drivers/char/exynos5-uart.c diff --git a/config/arm32.mk b/config/arm32.mk index 83a7767..593a1d1 100644 --- a/config/arm32.mk +++ b/config/arm32.mk @@ -19,6 +19,7 @@ CFLAGS += -marm # - pl011: printk with PL011 UART CONFIG_EARLY_PRINTK := none HAS_PL011 := y +HAS_EXYNOS5 := y # Use only if calling $(LD) directly. #LDFLAGS_DIRECT_OpenBSD = _obsd diff --git a/xen/drivers/char/Makefile b/xen/drivers/char/Makefile index e68a54a..12a4b49 100644 --- a/xen/drivers/char/Makefile +++ b/xen/drivers/char/Makefile @@ -1,6 +1,7 @@ obj-y += console.o obj-$(HAS_NS16550) += ns16550.o obj-$(HAS_PL011) += pl011.o +obj-$(HAS_EXYNOS5) += exynos5-uart.o obj-$(HAS_EHCI) += ehci-dbgp.o obj-$(CONFIG_ARM) += arm-uart.o obj-y += serial.o diff --git a/xen/drivers/char/exynos5-uart.c b/xen/drivers/char/exynos5-uart.c new file mode 100644 index 0000000..1bae153 --- /dev/null +++ b/xen/drivers/char/exynos5-uart.c @@ -0,0 +1,346 @@ +/* + * xen/drivers/char/exynos5-uart.c + * + * Driver for Exynos 4210 UART. + * + * Anthony PERARD <anthony.perard@xxxxxxxxxx> + * Copyright (c) 2012 Citrix Systems. + * + * 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. + */ + +#include <xen/config.h> +#include <xen/console.h> +#include <xen/errno.h> +#include <xen/serial.h> +#include <xen/init.h> +#include <xen/irq.h> +#include <asm/early_printk.h> +#include <asm/device.h> + +static struct exynos5_uart { + unsigned int baud, clock_hz, data_bits, parity, stop_bits; + struct dt_irq irq; + volatile uint32_t *regs; + struct irqaction irqaction; +} exynos5_com[2] = {{0}}; + +/* register addresses */ +#define ULCON (0x00/4) +#define UCON (0x04/4) +#define UFCON (0x08/4) +#define UMCON (0x0c/4) +#define UTRSTAT (0x10/4) +#define UERSTAT (0x14/4) +#define UFSTAT (0x18/4) +#define UMSTAT (0x1c/4) +#define UTXH (0x20/4) +#define URXH (0x24/4) +#define UBRDIV (0x28/4) +#define UFRACVAL (0x2c/4) +#define UINTP (0x30/4) +#define UINTS (0x34/4) +#define UINTM (0x38/4) + +/* ULCON */ +#define RXIRQ (0x1<<0) +#define RXDMA (0x2<<0) +#define TXIRQ (0x1<<2) +#define TXDMA (0x2<<2) + +/* UFCON */ +#define FIFO_TX_RESET (1<<2) +#define FIFO_RX_RESET (1<<1) +#define FIFO_EN (1<<0) + +/* UMCON */ +#define INT_EN (1<<3) + +/* UTRSTAT */ +#define TXE (1<<2) +#define TXFE (1<<1) +#define RXDR (1<<0) + +/* Interrupt bits (UINTP, UINTS, UINTM) */ +#define MODEM (1<<3) +#define TXD (1<<2) +#define ERROR (1<<1) +#define RXD (1<<0) +#define ALLI (MODEM|TXD|ERROR|RXD) + +/* These parity settings can be ORed directly into the ULCON. */ +#define PARITY_NONE (0) +#define PARITY_ODD (0x4) +#define PARITY_EVEN (0x5) +#define FORCED_CHECKED_AS_ONE (0x6) +#define FORCED_CHECKED_AS_ZERO (0x7) + +static void exynos5_uart_interrupt(int irq, void *data, struct cpu_user_regs *regs) +{ + struct serial_port *port = data; + struct exynos5_uart *uart = port->uart; + unsigned int status = uart->regs[UINTP]; + + if ( status ) + { + do + { + // clear all pending interrept + // but should take care of ERROR and MODEM + + if ( status & ERROR ) + { + int error_bit = uart->regs[UERSTAT] & 0xf; + if ( error_bit & (1 << 0) ) + printk(XENLOG_ERR "uart: overrun error\n"); + if ( error_bit & (1 << 1) ) + printk(XENLOG_ERR "uart: parity error\n"); + if ( error_bit & (1 << 2) ) + printk(XENLOG_ERR "uart: frame error\n"); + if ( error_bit & (1 << 3) ) + printk(XENLOG_ERR "uart: break detected\n"); + uart->regs[UINTP] = ERROR; + } + + + if ( status & (RXD|ERROR) ) + { + /* uart->regs[UINTM] |= RXD|ERROR; */ + serial_rx_interrupt(port, regs); + /* uart->regs[UINTM] &= ~(RXD|ERROR); */ + uart->regs[UINTP] = RXD|ERROR; + } + + if ( status & (TXD|MODEM) ) + { + /* uart->regs[UINTM] |= TXD|MODEM; */ + serial_tx_interrupt(port, regs); + /* uart->regs[UINTM] &= ~(TXD|MODEM); */ + uart->regs[UINTP] = TXD|MODEM; + } + + status = uart->regs[UINTP]; + } while ( status != 0 ); + } +} + +static void __init exynos5_uart_init_preirq(struct serial_port *port) +{ + struct exynos5_uart *uart = port->uart; + unsigned int divisor; + + /* reset, TX/RX disables */ + uart->regs[UCON] = 0x0; + + /* No Interrupt, auto flow control */ + uart->regs[UMCON] = 0x0; + + /* Line control and baud-rate generator. */ + if ( uart->baud != BAUD_AUTO ) + { + /* Baud rate specified: program it into the divisor latch. */ + // div_val = ubrdiv + ufracval/16 + // or + // div_val = (clock_uart/(baud*16))-1 + divisor = ((uart->clock_hz) / (uart->baud)) - 1; + // FIXME will use a hacked divisor, assuming the src clock and bauds + uart->regs[UFRACVAL] = 53; + uart->regs[UBRDIV] = 4; + /* uart->regs[UFRACVAL] = divisor & 0xf; */ + /* uart->regs[UBRDIV] = divisor >> 4; */ + } + else + { + // TODO, should be updated + /* Baud rate already set: read it out from the divisor latch. */ + //divisor = (uart->regs[IBRD] << 6) | uart->regs[FBRD]; + //uart->baud = (uart->clock_hz << 2) / divisor; + } + uart->regs[ULCON] = ( (uart->data_bits - 5) << 0 + | ((uart->stop_bits - 1) << 2) + | uart->parity << 3 ); + + /* Mask and clear the interrupts */ + uart->regs[UINTM] = ALLI; + uart->regs[UINTP] = ALLI; + + /* enable FIFO */ + uart->regs[UFCON] = FIFO_TX_RESET | FIFO_RX_RESET; + while ( uart->regs[UFCON] & (FIFO_TX_RESET | FIFO_RX_RESET) ) + ; + // reset FIFO_TX_RESET | FIFO_RX_RESET | + uart->regs[UFCON] = (0x6 << 8) | FIFO_EN; + + /* Enable the UART for RX and TX */ + // level tx/rx interrupt,only rx + // enable rx timeout interrupt + uart->regs[UCON] = (0 << 9) | (0 << 8) | RXIRQ | TXIRQ | ( 1 << 7); +} + +static void __init exynos5_uart_init_postirq(struct serial_port *port) +{ + struct exynos5_uart *uart = port->uart; + int rc; + + if ( uart->irq.irq > 0 ) + { + uart->irqaction.handler = exynos5_uart_interrupt; + uart->irqaction.name = "exynos5_uart"; + uart->irqaction.dev_id = port; + if ( (rc = setup_irq(uart->irq.irq, &uart->irqaction)) != 0 ) + printk("ERROR: Failed to allocate exynos5_uart IRQ %d\n", + uart->irq.irq); + + /* Unmask interrupts */ + uart->regs[UINTM] = 0; //MODEM|TXD|ERROR; // only have rx interrupt + + /* Clear pending error interrupts */ + uart->regs[UINTP] = ALLI; + + /* Enable interrupts */ + uart->regs[UMCON] |= INT_EN; + } +} + +static void exynos5_uart_suspend(struct serial_port *port) +{ + BUG(); // XXX +} + +static void exynos5_uart_resume(struct serial_port *port) +{ + BUG(); // XXX +} + +static unsigned int exynos5_uart_tx_ready(struct serial_port *port) +{ + struct exynos5_uart *uart = port->uart; + + // Tx FIFO full + if ( uart->regs[UFSTAT] & (1 << 24) ) + return 0; + else + { + int x = 16 - ((uart->regs[UFSTAT] >> 16) & 0xff); + // Tx FIFO count + if ( x > 0 ) + return x; + else if ( x == 0 ) + return 0; + else { + panic("unwanted value: %d\n", x); + return 0; + } + } +} + +static void exynos5_uart_putc(struct serial_port *port, char c) +{ + struct exynos5_uart *uart = port->uart; + uart->regs[UTXH] = (uint32_t) (unsigned char) c; +} + +static int exynos5_uart_getc(struct serial_port *port, char *pc) +{ + struct exynos5_uart *uart = port->uart; + + // check if rx fifo is full or if there is something in it + if ( (uart->regs[UFSTAT] & (1 << 8)) || (uart->regs[UFSTAT] & 0xff) ) + { + *pc = uart->regs[URXH] & 0xff; + return 1; + } + else + return 0; +} + +static int __init exynos5_uart_irq(struct serial_port *port) +{ + struct exynos5_uart *uart = port->uart; + if ( uart->irq.irq > 0 ) + return uart->irq.irq; + else + return -1; +} + +static const struct dt_irq __init *exynos5_uart_dt_irq(struct serial_port *port) +{ + struct exynos5_uart *uart = port->uart; + + return &uart->irq; +} + +static struct uart_driver __read_mostly exynos5_uart_driver = { + .init_preirq = exynos5_uart_init_preirq, + .init_postirq = exynos5_uart_init_postirq, + .endboot = NULL, + .suspend = exynos5_uart_suspend, + .resume = exynos5_uart_resume, + .tx_ready = exynos5_uart_tx_ready, + .putc = exynos5_uart_putc, + .getc = exynos5_uart_getc, + .irq = exynos5_uart_irq, + .dt_irq_get = exynos5_uart_dt_irq, +}; + +static int __init exynos5_uart_init(struct dt_device_node *dev, + const void *data) +{ + const struct serial_arm_defaults *defaults = data; + struct exynos5_uart *uart; + int res; + + if ( (defaults->index < 0) || (defaults->index > 1) ) + return -EINVAL; + + uart = &exynos5_com[defaults->index]; + + /* uart->clock_hz = 0x16e3600; */ + uart->baud = BAUD_AUTO;//115200; + uart->data_bits = 8; + uart->parity = PARITY_NONE; + uart->stop_bits = 1; + uart->regs = (uint32_t *) defaults->register_base_address; + + res = dt_device_get_irq(dev, 0, &uart->irq); + if ( res ) + { + early_printk("exynos5: Unable to retrieve the IRQ\n"); + return res; + } + + /* Register with generic serial driver. */ + serial_register_uart(uart - exynos5_com, &exynos5_uart_driver, uart); + + dt_device_set_used_by(dev, DT_USED_BY_XEN); + + return 0; +} + +static const char const *exynos5_dt_compat[] __initdata = +{ + "samsung,exynos4210-uart", + NULL +}; + +DT_DEVICE_START(exynos5, "Exynos5 UART", DEVICE_SERIAL) + .compatible = exynos5_dt_compat, + .init = exynos5_uart_init, +DT_DEVICE_END + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ -- Julien Grall _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |