forked from nxp-auto-linux/xen
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added complete UART Driver for S32 Linflex. Changed KConfig file to enable HAS_LINFLEX CONFIG in order for the driver to be selected at build time. Also added rule in Makefile to generate driver module object. Added board-specific UART constants definitions header file. Issue: ALB-3594 Signed-off-by: Peter van der Perk <peter.vander.perk@nxp.com> Signed-off-by: Andrei Cherechesu <andrei.cherechesu@nxp.com>
- Loading branch information
1 parent
716d556
commit ff96fbe
Showing
3 changed files
with
298 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,289 @@ | ||
/* | ||
* xen/drivers/char/s32linflex-uart.c | ||
* | ||
* Driver for NXP Linflex UART. | ||
* | ||
* Peter van der Perk <peter.vander.perk@nxp.com> | ||
* Copyright 2018 NXP | ||
* | ||
*/ | ||
|
||
#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 <xen/mm.h> | ||
#include <asm/device.h> | ||
#include <asm/s32linflex-uart.h> | ||
#include <asm/io.h> | ||
|
||
#define s32linflex_uart_readl(uart, off) \ | ||
readl((uart)->regs + (off)) | ||
#define s32linflex_uart_writel(uart, off, val) \ | ||
writel((val), (uart)->regs + (off)) | ||
#define s32linflex_uart_readb(uart, off) \ | ||
readb((uart)->regs + (off)) | ||
#define s32linflex_uart_writeb(uart, off, val) \ | ||
writeb((val), (uart)->regs + (off)) | ||
|
||
#define LIN_CLK_FREQ (66666667) /* LIN_CLK freq taken from Linux */ | ||
#define LIN_BAUDRATE (115200) /* LINFlex UART Baud Rate */ | ||
#define LIN_DATABITS (8) /* LINFlex UART Data Bits */ | ||
#define LIN_STOPBITS (1) /* LINFlex UART Stop Bits */ | ||
#define LIN_PARITYNONE (0) /* LINFlex UART Parity None */ | ||
|
||
static struct s32linflex_uart { | ||
unsigned int baud, clock_hz, data_bits, parity, stop_bits, fifo_size; | ||
unsigned int irq; | ||
char __iomem *regs; | ||
struct irqaction irqaction; | ||
struct vuart_info vuart; | ||
} s32_com = {0}; | ||
|
||
|
||
static void __init s32linflex_uart_init_preirq(struct serial_port *port) | ||
{ | ||
struct s32linflex_uart *uart = port->uart; | ||
u32 ctrl; | ||
|
||
u32 clk; | ||
u32 ibr, fbr; | ||
|
||
/* set the Linflex in master mode amd activate by-pass filter */ | ||
ctrl = LINCR1_BF | LINCR1_MME; | ||
s32linflex_uart_writel(uart, LINCR1, ctrl); | ||
|
||
/* init mode */ | ||
ctrl |= LINCR1_INIT; | ||
s32linflex_uart_writel(uart, LINCR1, ctrl); | ||
|
||
/* waiting for init mode entry */ | ||
while ((s32linflex_uart_readl(uart, LINSR) & LINSR_LINS_MASK) != | ||
LINSR_LINS_INITMODE) | ||
; | ||
|
||
/* set UART bit to allow writing other bits */ | ||
s32linflex_uart_writel(uart, UARTCR, UARTCR_UART); | ||
|
||
/* provide data bits, parity, stop bit, etc */ | ||
clk = uart->clock_hz; | ||
|
||
ibr = (u32) (clk / (16 * (uart)->baud)); | ||
fbr = (u32) (clk % (16 * (uart)->baud)) / (uart->baud); | ||
|
||
s32linflex_uart_writel(uart, LINIBRR, ibr); | ||
s32linflex_uart_writel(uart, LINFBRR, fbr); | ||
|
||
/* 8 bit data, no parity, Tx and Rx enabled, UART mode */ | ||
s32linflex_uart_writel(uart, UARTCR, UARTCR_PC1 | UARTCR_RXEN | | ||
UARTCR_TXEN | UARTCR_PC0 | UARTCR_WL0 | UARTCR_UART); | ||
|
||
ctrl = s32linflex_uart_readl(uart, LINCR1); | ||
ctrl &= ~LINCR1_INIT; | ||
|
||
/* end init mode */ | ||
s32linflex_uart_writel(uart, LINCR1, ctrl); | ||
} | ||
|
||
static void s32linflex_uart_interrupt(int irq, void *data, | ||
struct cpu_user_regs *regs) | ||
{ | ||
struct serial_port *port = data; | ||
struct s32linflex_uart *uart = port->uart; | ||
unsigned int sts; | ||
|
||
sts = s32linflex_uart_readl(uart, UARTSR); | ||
|
||
if (sts & UARTSR_DRF) | ||
serial_rx_interrupt(port, regs); | ||
|
||
if (sts & UARTSR_DTF) | ||
serial_tx_interrupt(port, regs); | ||
} | ||
|
||
static void __init s32linflex_uart_init_postirq(struct serial_port *port) | ||
{ | ||
struct s32linflex_uart *uart = port->uart; | ||
unsigned int temp; | ||
|
||
uart->irqaction.handler = s32linflex_uart_interrupt; | ||
uart->irqaction.name = "s32linflex_uart"; | ||
uart->irqaction.dev_id = port; | ||
|
||
temp = s32linflex_uart_readl(uart, LINIER); | ||
temp |= (LINIER_DRIE | LINIER_DTIE); | ||
s32linflex_uart_writel(uart, LINIER, temp); | ||
} | ||
|
||
static void s32linflex_uart_suspend(struct serial_port *port) | ||
{ | ||
BUG(); | ||
} | ||
|
||
static void s32linflex_uart_resume(struct serial_port *port) | ||
{ | ||
BUG(); | ||
} | ||
|
||
|
||
static int s32linflex_uart_tx_ready(struct serial_port *port) | ||
{ | ||
struct s32linflex_uart *uart = port->uart; | ||
|
||
return (s32linflex_uart_readb(uart, UARTSR) & UARTSR_DTF) ? 1 : 0; | ||
} | ||
|
||
static void s32linflex_uart_putc(struct serial_port *port, char c) | ||
{ | ||
struct s32linflex_uart *uart = port->uart; | ||
|
||
s32linflex_uart_writeb(uart, BDRL, c); | ||
|
||
s32linflex_uart_writeb(uart, UARTSR, | ||
(s32linflex_uart_readb(uart, UARTSR) | UARTSR_DTF)); | ||
} | ||
|
||
static int s32linflex_uart_getc(struct serial_port *port, char *pc) | ||
{ | ||
struct s32linflex_uart *uart = port->uart; | ||
int ch; | ||
|
||
if (!(s32linflex_uart_readb(uart, UARTSR) & UARTSR_DRF)) | ||
return 0; | ||
|
||
if (!(s32linflex_uart_readl(uart, UARTSR) & UARTSR_RMB)) | ||
return 0; | ||
|
||
ch = s32linflex_uart_readl(uart, BDRM); | ||
*pc = ch & 0xff; | ||
|
||
s32linflex_uart_writeb(uart, UARTSR, | ||
(s32linflex_uart_readb(uart, UARTSR) | | ||
(UARTSR_DRF | UARTSR_RMB))); | ||
|
||
|
||
return 1; | ||
} | ||
|
||
static int __init s32linflex_uart_irq(struct serial_port *port) | ||
{ | ||
struct s32linflex_uart *uart = port->uart; | ||
|
||
return ((uart->irq > 0) ? uart->irq : -1); | ||
} | ||
|
||
|
||
static const struct vuart_info *s32linflex_uart_vuart_info( | ||
struct serial_port *port) | ||
{ | ||
struct s32linflex_uart *uart = port->uart; | ||
|
||
return &uart->vuart; | ||
} | ||
|
||
static void s32linflex_uart_start_tx(struct serial_port *port) | ||
{ | ||
struct s32linflex_uart *uart = port->uart; | ||
unsigned long temp; | ||
|
||
temp = s32linflex_uart_readl(uart, LINIER); | ||
s32linflex_uart_writel(uart, LINIER, temp | LINIER_DTIE); | ||
} | ||
|
||
static void s32linflex_uart_stop_tx(struct serial_port *port) | ||
{ | ||
struct s32linflex_uart *uart = port->uart; | ||
unsigned long temp; | ||
|
||
temp = s32linflex_uart_readl(uart, LINIER); | ||
temp &= ~(LINIER_DTIE); | ||
s32linflex_uart_writel(uart, LINIER, temp); | ||
} | ||
|
||
|
||
static struct uart_driver __read_mostly s32linflex_uart_driver = { | ||
.init_preirq = s32linflex_uart_init_preirq, | ||
.init_postirq = s32linflex_uart_init_postirq, | ||
.endboot = NULL, | ||
.suspend = s32linflex_uart_suspend, | ||
.resume = s32linflex_uart_resume, | ||
.tx_ready = s32linflex_uart_tx_ready, | ||
.putc = s32linflex_uart_putc, | ||
.getc = s32linflex_uart_getc, | ||
.irq = s32linflex_uart_irq, | ||
.start_tx = s32linflex_uart_start_tx, | ||
.stop_tx = s32linflex_uart_stop_tx, | ||
.vuart_info = s32linflex_uart_vuart_info, | ||
}; | ||
|
||
static int __init s32linflex_uart_init(struct dt_device_node *dev, | ||
const void *data) | ||
{ | ||
const char *config = data; | ||
struct s32linflex_uart *uart; | ||
int res; | ||
u64 addr, size; | ||
|
||
if (config && strcmp(config, "")) | ||
printk(KERN_WARNING "WARNING: UART configuration is not supported\n"); | ||
|
||
uart = &s32_com; | ||
|
||
uart->clock_hz = LIN_CLK_FREQ; | ||
uart->baud = LIN_BAUDRATE; | ||
uart->data_bits = LIN_DATABITS; | ||
uart->parity = LIN_PARITYNONE; | ||
uart->stop_bits = LIN_STOPBITS; | ||
|
||
res = dt_device_get_address(dev, 0, &addr, &size); | ||
if (res) { | ||
printk(KERN_ERR "s32linflex-uart: Unable to retrieve the base address of the UART\n"); | ||
return res; | ||
} | ||
|
||
res = platform_get_irq(dev, 0); | ||
if (res < 0) { | ||
printk(KERN_ERR "s32linflex-uart: Unable to retrieve the IRQ\n"); | ||
return -EINVAL; | ||
} | ||
uart->irq = res; | ||
|
||
uart->regs = ioremap_nocache(addr, size); | ||
if (!uart->regs) { | ||
printk(KERN_ERR "s32linflex-uart: Unable to map the UART memory\n"); | ||
return -ENOMEM; | ||
} | ||
|
||
uart->vuart.base_addr = addr; | ||
uart->vuart.size = size; | ||
uart->vuart.data_off = BDRL; | ||
uart->vuart.status_off = UARTSR; | ||
uart->vuart.status = UARTSR_DTF; | ||
|
||
/* Register with generic serial driver */ | ||
serial_register_uart(SERHND_DTUART, &s32linflex_uart_driver, uart); | ||
|
||
dt_device_set_used_by(dev, DOMID_XEN); | ||
|
||
return 0; | ||
} | ||
|
||
static const struct dt_device_match s32linflex_uart_dt_compat[] __initconst = { | ||
DT_MATCH_COMPATIBLE("fsl,s32-linflexuart"), | ||
{}, | ||
}; | ||
|
||
DT_DEVICE_START(s32linflex_uart, "NXP Linflex UART", DEVICE_SERIAL) | ||
.dt_match = s32linflex_uart_dt_compat, | ||
.init = s32linflex_uart_init, | ||
DT_DEVICE_END | ||
/* | ||
* Local variables: | ||
* mode: C | ||
* c-file-style: "BSD" | ||
* c-basic-offset: 4 | ||
* indent-tabs-mode: nil | ||
* End: | ||
*/ |