drivers: add serial spinal driver

Signed-off-by: Dolu1990 <charles.papon.90@gmail.com>
Signed-off-by: Mohamad Noor Alim Hussin <mnalim@efinixinc.com>
This commit is contained in:
Mohamad Noor Alim Hussin
2022-01-10 18:09:49 +08:00
committed by Byron Lathi
parent 31e941effe
commit 270244937f
4 changed files with 756 additions and 0 deletions

View File

@@ -1583,6 +1583,29 @@ config SERIAL_MILBEAUT_USIO_CONSOLE
receives all kernel messages and warnings and which allows logins in
single user mode).
config SERIAL_SPINAL_LIB_UART
tristate "Spinal lib UART support"
select SERIAL_CORE
help
This driver supports the spinal lib UART port.
config SERIAL_SPINAL_LIB_UART_BAUDRATE
int "Default baudrate for spinal lib UART ports"
depends on SERIAL_SPINAL_LIB_UART
default 115200
help
This setting lets you define what the default baudrate is for the
spinal lib UART ports. The usual default varies from board to board,
and this setting is a way of catering for that.
config SERIAL_SPINAL_LIB_UART_CONSOLE
bool "Spinal lib UART console support"
depends on SERIAL_SPINAL_LIB_UART=y
select SERIAL_CORE_CONSOLE
select SERIAL_EARLYCON
help
Enable a spinal lib UART port to be the system console.
endmenu
config SERIAL_MCTRL_GPIO

View File

@@ -90,6 +90,7 @@ obj-$(CONFIG_SERIAL_OWL) += owl-uart.o
obj-$(CONFIG_SERIAL_RDA) += rda-uart.o
obj-$(CONFIG_SERIAL_MILBEAUT_USIO) += milbeaut_usio.o
obj-$(CONFIG_SERIAL_SIFIVE) += sifive.o
obj-$(CONFIG_SERIAL_SPINAL_LIB_UART) += serial_spinal_lib.o
# GPIOLIB helpers for modem control lines
obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o

View File

@@ -0,0 +1,729 @@
/*
* Copyright (C) 2023 Efinix Inc. All rights reserved.
*
* SPDX-License-Identifier: GPL-3.0
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/console.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/clk.h>
#define DRV_NAME "spinal_lib_uart"
#define SERIAL_SPINAL_LIB_UART_MAXPORTS 4
/* Register Offset */
#define SPINAL_LIB_UART_DATA 0x00
#define SPINAL_LIB_UART_STATUS 0x04
#define SPINAL_LIB_UART_CLOCK_DIVIDER 0x08
#define SPINAL_LIB_UART_FRAME_CONFIG 0x0C
#define SPINAL_LIB_UART_FRAME_FLAGS 0x10
#define SPINAL_LIB_UART_STATUS_TX_BUSY 0x8000
#define SPINAL_LIB_UART_FRAME_CONFIG_STOP1 (0 << 16)
#define SPINAL_LIB_UART_FRAME_CONFIG_STOP2 (1 << 16)
#define SPINAL_LIB_UART_FRAME_CONFIG_ODD (1 << 9)
#define SPINAL_LIB_UART_FRAME_CONFIG_EVEN (1 << 8)
#define SPINAL_LIB_UART_MISC_READ_OVERFLOW (1 << 1)
#define SPINAL_LIB_UART_MISC_BREAK_ACTIVE (1 << 8)
#define SPINAL_LIB_UART_MISC_BREAK_DETECTED (1 << 9)
#define SPINAL_LIB_UART_MISC_BREAK_ENABLE (1 << 10)
#define SPINAL_LIB_UART_MISC_BREAK_DISABLE (1 << 11)
#define SPINAL_LIB_UART_CONTROL_TRDY_MSK (1 << 0)
#define SPINAL_LIB_UART_CONTROL_RRDY_MSK (1 << 1)
#define SPINAL_LIB_UART_STATUS_TRDY_MSK (1 << 8)
#define SPINAL_LIB_UART_STATUS_RRDY_MSK (1 << 9)
/* Depth of TX/RX FIFO (in bytes) */
#define SPINAL_LIB_UART_FIFO_DEPTH 128
#undef pr_fmt
#define pr_fmt(fmt) "%s : " fmt, __func__
/*
* Local per-uart structure.
*/
struct spinal_lib_uart {
struct device *dev;
struct uart_port port;
struct timer_list tmr;
u32 break_on_rx;
u32 imr;
};
static struct spinal_lib_uart *spinal_lib_uart_ports[SERIAL_SPINAL_LIB_UART_MAXPORTS];
static u32 spinal_lib_uart_tx_availability(struct uart_port *port)
{
return (readl(port->membase + SPINAL_LIB_UART_STATUS) >> 16) & 0xFF;
}
static u32 spinal_lib_uart_rx_occupancy(struct uart_port *port)
{
return (readl(port->membase + SPINAL_LIB_UART_STATUS) >> 24) & 0xFF;
}
static void spinal_lib_uart_tx(struct uart_port *port, char ch)
{
writel(ch, port->membase + SPINAL_LIB_UART_DATA);
}
static inline void spinal_lib_uart_writel(struct uart_port *port, u32 data, u32 reg)
{
writel(data, port->membase + reg);
}
static inline u32 spinal_lib_uart_readl(struct uart_port *port, u32 reg)
{
return readl(port->membase + reg);
}
static void spinal_lib_uart_update_ctrl_reg(struct spinal_lib_uart *pp)
{
unsigned short imr = pp->imr;
/*
* If the device doesn't have an irq, ensure that the irq bits are
* masked out to keep the irq line inactive.
*/
spinal_lib_uart_writel(&pp->port, imr, SPINAL_LIB_UART_STATUS);
}
static inline void spinal_lib_uart_disable_tx_interrupt(struct spinal_lib_uart *pp)
{
pp->imr &= ~SPINAL_LIB_UART_CONTROL_TRDY_MSK;
spinal_lib_uart_update_ctrl_reg(pp);
}
static inline void spinal_lib_uart_disable_rx_interrupt(struct spinal_lib_uart *pp)
{
pp->imr &= ~SPINAL_LIB_UART_CONTROL_RRDY_MSK;
spinal_lib_uart_update_ctrl_reg(pp);
}
static inline void spinal_lib_uart_enable_tx_interrupt(struct spinal_lib_uart *pp)
{
pp->imr |= SPINAL_LIB_UART_CONTROL_TRDY_MSK;
spinal_lib_uart_update_ctrl_reg(pp);
}
static inline void spinal_lib_uart_enable_rx_interrupt(struct spinal_lib_uart *pp)
{
pp->imr |= SPINAL_LIB_UART_CONTROL_RRDY_MSK;
spinal_lib_uart_update_ctrl_reg(pp);
}
static unsigned int spinal_lib_uart_tx_empty(struct uart_port *port)
{
return (spinal_lib_uart_tx_availability(port) & SPINAL_LIB_UART_STATUS_TX_BUSY) ? 0 : TIOCSER_TEMT;
}
static unsigned int spinal_lib_uart_get_mctrl(struct uart_port *port)
{
return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
}
static void spinal_lib_uart_set_mctrl(struct uart_port *port, unsigned int sigs)
{
}
static void spinal_lib_uart_stop_tx(struct uart_port *port)
{
struct spinal_lib_uart *pp = container_of(port, struct spinal_lib_uart, port);
spinal_lib_uart_disable_tx_interrupt(pp);
}
static void spinal_lib_uart_stop_rx(struct uart_port *port)
{
struct spinal_lib_uart *pp = container_of(port, struct spinal_lib_uart, port);
spinal_lib_uart_disable_rx_interrupt(pp);
}
static void spinal_lib_uart_break_ctl(struct uart_port *port, int break_state)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
writel(break_state != 0 ? SPINAL_LIB_UART_MISC_BREAK_ENABLE : SPINAL_LIB_UART_MISC_BREAK_DISABLE, port->membase + SPINAL_LIB_UART_FRAME_FLAGS);
spin_unlock_irqrestore(&port->lock, flags);
}
static void spinal_lib_uart_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
{
unsigned long flags;
unsigned int baud, baudclk;
unsigned int config = 0;
baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
switch(termios->c_cflag & CSIZE) {
case CS5: config |= 4; break;
case CS6: config |= 5; break;
case CS7: config |= 6; break;
case CS8: config |= 7; break;
}
baudclk = port->uartclk / (baud * (config + 1)) - 1;
if (termios->c_cflag & CSTOPB)
config |= SPINAL_LIB_UART_FRAME_CONFIG_STOP2;
else
config |= SPINAL_LIB_UART_FRAME_CONFIG_STOP1;
if (termios->c_cflag & PARODD)
config |= SPINAL_LIB_UART_FRAME_CONFIG_ODD;
if (termios->c_cflag & PARENB)
config |= SPINAL_LIB_UART_FRAME_CONFIG_EVEN;
spinal_lib_uart_writel(port, baudclk, SPINAL_LIB_UART_CLOCK_DIVIDER);
spinal_lib_uart_writel(port, config, SPINAL_LIB_UART_FRAME_CONFIG);
spin_lock_irqsave(&port->lock, flags);
uart_update_timeout(port, termios->c_cflag, baud);
spin_unlock_irqrestore(&port->lock, flags);
}
static void spinal_lib_uart_rx_chars(struct spinal_lib_uart *pp)
{
struct uart_port *port = &pp->port;
unsigned char ch, flag;
u32 count;
u32 flags;
flags = spinal_lib_uart_readl(port, SPINAL_LIB_UART_FRAME_FLAGS);
if(!pp->break_on_rx){
if(flags & SPINAL_LIB_UART_MISC_BREAK_DETECTED){
spinal_lib_uart_writel(port,
SPINAL_LIB_UART_MISC_BREAK_DETECTED,
SPINAL_LIB_UART_FRAME_FLAGS);
pp->break_on_rx = 1;
port->icount.brk++;
uart_handle_break(port);
}
} else {
if((flags & SPINAL_LIB_UART_MISC_BREAK_ACTIVE) == 0){
pp->break_on_rx = 0;
}
}
if(!pp->break_on_rx) while((count = spinal_lib_uart_rx_occupancy(port))){
while(count--){
ch = spinal_lib_uart_readl(port, SPINAL_LIB_UART_DATA);
flag = TTY_NORMAL;
port->icount.rx++;
if (uart_handle_sysrq_char(port, ch))
continue;
uart_insert_char(port, 0, 0, ch, flag);
}
}
spin_unlock(&port->lock);
tty_flip_buffer_push(&port->state->port);
spin_lock(&port->lock);
}
static void spinal_lib_uart_tx_chars(struct spinal_lib_uart *pp)
{
struct uart_port *port = &pp->port;
struct circ_buf *xmit = &port->state->xmit;
unsigned int numbytes;
numbytes = SPINAL_LIB_UART_FIFO_DEPTH;
if (port->x_char && spinal_lib_uart_tx_availability(port)) {
/* Send special char - probably flow control */
spinal_lib_uart_tx(port, port->x_char);
port->x_char = 0;
port->icount.tx++;
return;
}
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
spinal_lib_uart_stop_tx(port);
return;
}
while (numbytes && !uart_circ_empty(xmit) && spinal_lib_uart_tx_availability(port)) {
spinal_lib_uart_tx(port, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
numbytes--;
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (uart_circ_empty(xmit))
spinal_lib_uart_stop_tx(port);
else
spinal_lib_uart_enable_tx_interrupt(pp);
}
static void spinal_lib_uart_start_tx(struct uart_port *port)
{
struct spinal_lib_uart *pp = container_of(port, struct spinal_lib_uart, port);
spinal_lib_uart_enable_tx_interrupt(pp);
}
static irqreturn_t spinal_lib_uart_interrupt(int irq, void *data)
{
struct uart_port *port = data;
struct spinal_lib_uart *pp = container_of(port, struct spinal_lib_uart, port);
unsigned int isr;
isr = spinal_lib_uart_readl(port, SPINAL_LIB_UART_STATUS);
/* Disable Tx/Rx interrupt */
spinal_lib_uart_disable_tx_interrupt(pp);
spinal_lib_uart_disable_rx_interrupt(pp);
spin_lock(&port->lock);
if (isr & SPINAL_LIB_UART_STATUS_RRDY_MSK)
spinal_lib_uart_rx_chars(pp);
if (isr & SPINAL_LIB_UART_STATUS_TRDY_MSK)
spinal_lib_uart_tx_chars(pp);
spin_unlock(&port->lock);
/* Enable Rx interrupt */
spinal_lib_uart_enable_rx_interrupt(pp);
return IRQ_RETVAL(isr);
}
static void spinal_lib_uart_timer(struct timer_list *t)
{
struct spinal_lib_uart *pp = from_timer(pp, t, tmr);
struct uart_port *port = &pp->port;
spinal_lib_uart_interrupt(0, port);
mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port));
}
static void spinal_lib_uart_config_port(struct uart_port *port, int flags)
{
port->type = PORT_SPINAL_LIB;
if (flags & UART_CONFIG_TYPE)
port->type = PORT_SPINAL_LIB;
}
static int spinal_lib_uart_startup(struct uart_port *port)
{
struct spinal_lib_uart *pp = container_of(port, struct spinal_lib_uart, port);
unsigned long flags;
if (!port->irq) {
timer_setup(&pp->tmr, spinal_lib_uart_timer, 0);
mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port));
}
spin_lock_irqsave(&port->lock, flags);
/* Enable RX interrupts now */
spinal_lib_uart_enable_rx_interrupt(pp);
spin_unlock_irqrestore(&port->lock, flags);
return 0;
}
static void spinal_lib_uart_shutdown(struct uart_port *port)
{
struct spinal_lib_uart *pp = container_of(port, struct spinal_lib_uart, port);
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
/* Disable all interrupts now */
spinal_lib_uart_disable_tx_interrupt(pp);
spinal_lib_uart_disable_rx_interrupt(pp);
spin_unlock_irqrestore(&port->lock, flags);
if (!port->irq)
del_timer_sync(&pp->tmr);
}
static const char *spinal_lib_uart_type(struct uart_port *port)
{
return (port->type == PORT_SPINAL_LIB) ? "Spinal lib UART" : NULL;
}
static int spinal_lib_uart_request_port(struct uart_port *port)
{
/* UARTs always present */
return 0;
}
static void spinal_lib_uart_release_port(struct uart_port *port)
{
/* Nothing to release... */
}
static int spinal_lib_uart_verify_port(struct uart_port *port,
struct serial_struct *ser)
{
if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_SPINAL_LIB))
return -EINVAL;
return 0;
}
#ifdef CONFIG_CONSOLE_POLL
static int spinal_lib_uart_poll_get_char(struct uart_port *port)
{
while (!spinal_lib_uart_rx_occupancy(port))
return NO_POLL_CHAR;
return spinal_lib_uart_readl(port, SPINAL_LIB_UART_DATA);
}
static void spinal_lib_uart_poll_put_char(struct uart_port *port, unsigned char c)
{
while (!spinal_lib_uart_tx_availability(port))
cpu_relax();
spinal_lib_uart_writel(port, c, SPINAL_LIB_UART_DATA);
}
#endif
/*
* Define the basic serial functions we support.
*/
static const struct uart_ops spinal_lib_uart_ops = {
.tx_empty = spinal_lib_uart_tx_empty,
.get_mctrl = spinal_lib_uart_get_mctrl,
.set_mctrl = spinal_lib_uart_set_mctrl,
.start_tx = spinal_lib_uart_start_tx,
.stop_tx = spinal_lib_uart_stop_tx,
.stop_rx = spinal_lib_uart_stop_rx,
.break_ctl = spinal_lib_uart_break_ctl,
.startup = spinal_lib_uart_startup,
.shutdown = spinal_lib_uart_shutdown,
.set_termios = spinal_lib_uart_set_termios,
.type = spinal_lib_uart_type,
.request_port = spinal_lib_uart_request_port,
.release_port = spinal_lib_uart_release_port,
.config_port = spinal_lib_uart_config_port,
.verify_port = spinal_lib_uart_verify_port,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = spinal_lib_uart_poll_get_char,
.poll_put_char = spinal_lib_uart_poll_put_char,
#endif
};
#ifdef CONFIG_SERIAL_SPINAL_LIB_UART_CONSOLE
static void spinal_lib_uart_console_putc(struct uart_port *port, int c)
{
while (!spinal_lib_uart_tx_availability(port))
cpu_relax();
spinal_lib_uart_writel(port, c, SPINAL_LIB_UART_DATA);
}
static void spinal_lib_uart_console_write(struct console *co, const char *s,
unsigned int count)
{
struct spinal_lib_uart *pp = spinal_lib_uart_ports[co->index];
unsigned long flags;
int locked = 1;
if (!pp)
return;
local_irq_save(flags);
if (pp->port.sysrq)
locked = 0;
else if (oops_in_progress)
locked = spin_trylock(&pp->port.lock);
else
spin_lock(&pp->port.lock);
uart_console_write(&pp->port, s, count, spinal_lib_uart_console_putc);
if (locked)
spin_unlock(&pp->port.lock);
local_irq_restore(flags);
}
static int __init spinal_lib_uart_console_setup(struct console *co, char *options)
{
struct spinal_lib_uart *pp;
int baud = CONFIG_SERIAL_SPINAL_LIB_UART_BAUDRATE;
int bits = 8;
int parity = 'n';
int flow = 'n';
if (co->index < 0 || co->index >= SERIAL_SPINAL_LIB_UART_MAXPORTS)
return -ENODEV;
pp = spinal_lib_uart_ports[co->index];
if (!pp)
return -ENODEV;
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
return uart_set_options(&pp->port, co, baud, parity, bits, flow);
}
static struct uart_driver spinal_lib_uart_driver;
static struct console spinal_lib_uart_console = {
.name = "ttySL",
.write = spinal_lib_uart_console_write,
.device = uart_console_device,
.setup = spinal_lib_uart_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &spinal_lib_uart_driver,
};
static int __init spinal_lib_uart_console_init(void)
{
register_console(&spinal_lib_uart_console);
return 0;
}
console_initcall(spinal_lib_uart_console_init);
#define SPINAL_LIB_UART_CONSOLE (&spinal_lib_uart_console)
static void spinal_lib_uart_earlycon_write(struct console *co, const char *s,
unsigned int count)
{
struct earlycon_device *dev = co->data;
uart_console_write(&dev->port, s, count, spinal_lib_uart_console_putc);
}
static int __init spinal_lib_uart_earlycon_setup(struct earlycon_device *dev,
const char *options)
{
struct uart_port *port = &dev->port;
unsigned int baudclk;
int bits = 8;
int parity = 'n';
int flow = 'n';
unsigned int config = 0;
if (!port->membase)
return -ENODEV;
if (options)
uart_parse_options(options, &dev->baud, &parity, &bits, &flow);
switch (parity) {
case 'e': config |= SPINAL_LIB_UART_FRAME_CONFIG_EVEN; break;
case 'o': config |= SPINAL_LIB_UART_FRAME_CONFIG_ODD; break;
default: break;
}
config |= (bits - 1);
if (dev->baud) {
baudclk = port->uartclk / (dev->baud * bits) - 1;
spinal_lib_uart_writel(port, baudclk, SPINAL_LIB_UART_CLOCK_DIVIDER);
spinal_lib_uart_writel(port, config, SPINAL_LIB_UART_FRAME_CONFIG);
}
dev->con->write = spinal_lib_uart_earlycon_write;
return 0;
}
OF_EARLYCON_DECLARE(sluart, "spinal-lib,uart-2.0", spinal_lib_uart_earlycon_setup);
#else
#define SPINAL_LIB_UART_CONSOLE NULL
#endif /* CONFIG_SERIAL_SPINAL_LIB_UART_CONSOLE */
/*
* Define the spinal_lib_uart UART driver structure.
*/
static struct uart_driver spinal_lib_uart_driver = {
.owner = THIS_MODULE,
.driver_name = DRV_NAME,
.dev_name = "ttySL",
.nr = SERIAL_SPINAL_LIB_UART_MAXPORTS,
.cons = SPINAL_LIB_UART_CONSOLE,
};
static int spinal_lib_uart_probe(struct platform_device *pdev)
{
struct resource *res_mem;
struct uart_port *port;
struct device *dev = &pdev->dev;
int ret;
struct spinal_lib_uart *pp;
struct clk *clk;
int irq;
u32 hz;
if (pdev->dev.of_node) {
pdev->id = of_alias_get_id(pdev->dev.of_node, "serial");
if (pdev->id < 0)
pdev->id = of_alias_get_id(pdev->dev.of_node, "uart");
}
if (pdev->id < 0 || pdev->id >= SERIAL_SPINAL_LIB_UART_MAXPORTS) {
return -ENODEV;
}
pp = devm_kzalloc(&pdev->dev, sizeof(*pp), GFP_KERNEL);
if (!pp) {
dev_err(dev, "out of memory\n");
return -ENOMEM;
}
spinal_lib_uart_ports[pdev->id] = pp;
pp->dev = &pdev->dev;
pp->break_on_rx = 0;
port = &pp->port;
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res_mem)
port->mapbase = res_mem->start;
else
return -ENODEV;
port->membase = devm_ioremap_resource(&pdev->dev, res_mem);
if (IS_ERR(port->membase))
return PTR_ERR(port->membase);
irq = platform_get_irq(pdev, 0);
port->irq = irq;
ret = devm_request_irq(pp->dev, port->irq, spinal_lib_uart_interrupt,
port->irqflags, DRV_NAME, port);
if (ret) {
port->irq = 0;
pr_err(DRV_NAME ": unable to attach Spinal Lib UART %d "
"interrupt vector=%d\n", port->line, port->irq);
}
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_info(&pdev->dev, "No peripheral clock\n");
goto exit;
}
hz = clk_get_rate(clk);
if(!hz){
dev_info(&pdev->dev, "Bad frequancy\n");
goto exit;
}
port->uartclk = hz;
port->fifosize = SPINAL_LIB_UART_FIFO_DEPTH;
port->regshift = 0;
port->line = pdev->id;
port->type = PORT_SPINAL_LIB;
port->iotype = SERIAL_IO_MEM;
port->ops = &spinal_lib_uart_ops;
port->flags = UPF_BOOT_AUTOCONF;
port->dev = &pdev->dev;
port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_SPINAL_LIB_UART_CONSOLE);
platform_set_drvdata(pdev, port);
ret = uart_add_one_port(&spinal_lib_uart_driver, port);
if (ret) {
spinal_lib_uart_ports[pdev->id]->port.membase = NULL;
return ret;
}
return 0;
exit:
return -EINVAL;
}
static int spinal_lib_uart_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
if (port) {
uart_remove_one_port(&spinal_lib_uart_driver, port);
spinal_lib_uart_ports[pdev->id]->port.membase = NULL;
}
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id spinal_lib_uart_match[] = {
{ .compatible = "spinal-lib,uart-2.0", },
{},
};
MODULE_DEVICE_TABLE(of, spinal_lib_uart_match);
#endif /* CONFIG_OF */
static struct platform_driver spinal_lib_uart_platform_driver = {
.probe = spinal_lib_uart_probe,
.remove = spinal_lib_uart_remove,
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(spinal_lib_uart_match),
},
};
static int __init spinal_lib_uart_init(void)
{
int ret;
ret = uart_register_driver(&spinal_lib_uart_driver);
if (ret < 0) {
pr_err("Could not register %s driver\n",
spinal_lib_uart_driver.driver_name);
return ret;
}
ret = platform_driver_register(&spinal_lib_uart_platform_driver);
if (ret < 0) {
pr_err("Uart platform driver register failed, e = %d\n", ret);
uart_unregister_driver(&spinal_lib_uart_driver);
return ret;
}
return 0;
}
static void __exit spinal_lib_uart_exit(void)
{
platform_driver_unregister(&spinal_lib_uart_platform_driver);
uart_unregister_driver(&spinal_lib_uart_driver);
}
module_init(spinal_lib_uart_init);
module_exit(spinal_lib_uart_exit);
MODULE_DESCRIPTION("Spinal lib UART driver");
MODULE_AUTHOR("Charles Papon <charles.papon.90@gmail.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);

View File

@@ -279,4 +279,7 @@
/* Freescale LINFlexD UART */
#define PORT_LINFLEXUART 122
/* Spinal lib UART */
#define PORT_SPINAL_LIB 123
#endif /* _UAPILINUX_SERIAL_CORE_H */