kernel\drivers\tty\serial\n76e003-usart.c
/*
* Copyright (C) Maxime Coquelin 2015
* Author: Maxime Coquelin <mcoquelin.n76e003@gmail.com>
* License terms: GNU General Public License (GPL), version 2
*
* Inspired by st-asc.c from STMicroelectronics (c)
*/
#if defined(CONFIG_SERIAL_N76E003_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/serial.h>
#include <linux/console.h>
#include <linux/sysrq.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/spinlock.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/serial_core.h>
#include <linux/clk.h>
#include <linux/spi/spidev.h>
#define DRIVER_NAME "n76e003-usart"
/* Register offsets */
#define USART_SR 0x00
#define USART_DR 0x04
#define USART_BRR 0x08
#define USART_CR1 0x0c
#define USART_CR2 0x10
#define USART_CR3 0x14
#define USART_GTPR 0x18
/* USART_SR */
#define USART_SR_PE BIT(0)
#define USART_SR_FE BIT(1)
#define USART_SR_NF BIT(2)
#define USART_SR_ORE BIT(3)
#define USART_SR_IDLE BIT(4)
#define USART_SR_RXNE BIT(5)
#define USART_SR_TC BIT(6)
#define USART_SR_TXE BIT(7)
#define USART_SR_LBD BIT(8)
#define USART_SR_CTS BIT(9)
#define USART_SR_ERR_MASK (USART_SR_LBD | USART_SR_ORE | \
USART_SR_FE | USART_SR_PE)
/* Dummy bits */
#define USART_SR_DUMMY_RX BIT(16)
/* USART_DR */
#define USART_DR_MASK GENMASK(8, 0)
/* USART_BRR */
#define USART_BRR_DIV_F_MASK GENMASK(3, 0)
#define USART_BRR_DIV_M_MASK GENMASK(15, 4)
#define USART_BRR_DIV_M_SHIFT 4
/* USART_CR1 */
#define USART_CR1_SBK BIT(0)
#define USART_CR1_RWU BIT(1)
#define USART_CR1_RE BIT(2)
#define USART_CR1_TE BIT(3)
#define USART_CR1_IDLEIE BIT(4)
#define USART_CR1_RXNEIE BIT(5)
#define USART_CR1_TCIE BIT(6)
#define USART_CR1_TXEIE BIT(7)
#define USART_CR1_PEIE BIT(8)
#define USART_CR1_PS BIT(9)
#define USART_CR1_PCE BIT(10)
#define USART_CR1_WAKE BIT(11)
#define USART_CR1_M BIT(12)
#define USART_CR1_UE BIT(13)
#define USART_CR1_OVER8 BIT(15)
#define USART_CR1_IE_MASK GENMASK(8, 4)
#define PORT_N76E003 113
/* USART_CR2 */
#define USART_CR2_ADD_MASK GENMASK(3, 0)
#define USART_CR2_LBDL BIT(5)
#define USART_CR2_LBDIE BIT(6)
#define USART_CR2_LBCL BIT(8)
#define USART_CR2_CPHA BIT(9)
#define USART_CR2_CPOL BIT(10)
#define USART_CR2_CLKEN BIT(11)
#define USART_CR2_STOP_2B BIT(13)
#define USART_CR2_STOP_MASK GENMASK(13, 12)
#define USART_CR2_LINEN BIT(14)
/* USART_CR3 */
#define USART_CR3_EIE BIT(0)
#define USART_CR3_IREN BIT(1)
#define USART_CR3_IRLP BIT(2)
#define USART_CR3_HDSEL BIT(3)
#define USART_CR3_NACK BIT(4)
#define USART_CR3_SCEN BIT(5)
#define USART_CR3_DMAR BIT(6)
#define USART_CR3_DMAT BIT(7)
#define USART_CR3_RTSE BIT(8)
#define USART_CR3_CTSE BIT(9)
#define USART_CR3_CTSIE BIT(10)
#define USART_CR3_ONEBIT BIT(11)
/* USART_GTPR */
#define USART_GTPR_PSC_MASK GENMASK(7, 0)
#define USART_GTPR_GT_MASK GENMASK(15, 8)
/*
* SPI2UART
BIT0
Data/Command 0:Data,1:Command
BIT1
CSTOPB(1/2) 0:1 ,1:2
BIT2
PARENB 0:not set ,1:set
BIT3
PARODD 0:not set ,1:set
BIT4
CSIZE 0:CS8 ,1: acording to second Byte
BIT5
CRTSCTS 0:not set ,1:set
BIT6
BAUD 0:not set ,1:set,acording to second Byte
*/
#define COMMAND_BIT 0
#define CSTOPB_BIT 1
#define PARENB_BIT 2
#define PARODD_BIT 3
#define CSIZE_BIT 4
#define CRTSCTS_BIT 5
#define BAUD_BIT 6
#define DRIVER_NAME "n76e003-usart"
#define n76e003_SERIAL_NAME "ttyS"
#define n76e003_MAX_PORTS 6
static unsigned bufsiz = 4096;
#define MAX_SPI_DEV_NUM 6
#define SPI_MAX_SPEED_HZ 12000000
struct spi2uart_data {
struct device *dev;
struct spi_device *spi;
char *rx_buffer;
int rx_len;
char *tx_buffer;
int tx_len;
int speed_hz;
};
struct n76e003_port {
struct uart_port port;
bool hw_flow_control;
};
static struct n76e003_port n76e003_ports[n76e003_MAX_PORTS];
static struct uart_driver n76e003_usart_driver;
static struct spi2uart_data *spi2uart_data = NULL;
static void n76e003_stop_tx(struct uart_port *port);
static inline struct n76e003_port *to_n76e003_port(struct uart_port *port)
{
return container_of(port, struct n76e003_port, port);
}
static void n76e003_receive_chars(struct uart_port *port,u8 buf)
{
struct tty_port *tport = &port->state->port;
u8 c;
u32 sr = 0;
char flag;
if (port->irq_wake)
pm_wakeup_event(tport->tty->dev, 0);
sr = USART_SR_DUMMY_RX | USART_SR_RXNE;
c = buf;
flag = TTY_NORMAL;
port->icount.rx++;
#if 0
if (sr & USART_SR_ERR_MASK) {
if (sr & USART_SR_LBD) {
port->icount.brk++;
if (uart_handle_break(port))
continue;
} else if (sr & USART_SR_ORE) {
port->icount.overrun++;
} else if (sr & USART_SR_PE) {
port->icount.parity++;
} else if (sr & USART_SR_FE) {
port->icount.frame++;
}
sr &= port->read_status_mask;
if (sr & USART_SR_LBD)
flag = TTY_BREAK;
else if (sr & USART_SR_PE)
flag = TTY_PARITY;
else if (sr & USART_SR_FE)
flag = TTY_FRAME;
}
#endif
if (uart_handle_sysrq_char(port, c))
{
pr_err("sysrq char ,break");
return;
}
uart_insert_char(port, sr, USART_SR_ORE, c, flag);
tty_flip_buffer_push(tport);
}
static void n76e003_transmit_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
if (port->x_char) {
pr_err("xon/xoff char\n");
port->x_char = 0;
port->icount.tx++;
return;
}
if (uart_tx_stopped(port)) {
pr_err("tx stoped\n");
n76e003_stop_tx(port);
return;
}
if (uart_circ_empty(xmit)) {
pr_err("tx empty\n");
n76e003_stop_tx(port);
return;
}
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (uart_circ_empty(xmit))
n76e003_stop_tx(port);
}
static irqreturn_t n76e003_interrupt(int irq, void *ptr)
{
struct uart_port *port = ptr;
u32 sr;
spin_lock(&port->lock);
sr = readl_relaxed(port->membase + USART_SR);
if (sr & USART_SR_RXNE)
n76e003_receive_chars(port,0);
if (sr & USART_SR_TXE)
n76e003_transmit_chars(port);
spin_unlock(&port->lock);
return IRQ_HANDLED;
}
static unsigned int n76e003_tx_empty(struct uart_port *port)
{
return 0;
}
static void n76e003_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
}
static unsigned int n76e003_get_mctrl(struct uart_port *port)
{
/* This routine is used to get signals of: DCD, DSR, RI, and CTS */
return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
}
/* Transmit stop */
static void n76e003_stop_tx(struct uart_port *port)
{
}
static ssize_t
spidev_sync(struct spi2uart_data *spidev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
struct spi_device *spi;
spi = spidev->spi;
if (spi == NULL)
status = -ESHUTDOWN;
else
status = spi_sync(spi, message);
if (status == 0)
status = message->actual_length;
return status;
}
static int spidev_message(struct spi2uart_data *spidev,
struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
{
struct spi_message msg;
struct spi_transfer *k_xfers;
struct spi_transfer *k_tmp;
struct spi_ioc_transfer *u_tmp;
unsigned n, total, tx_total, rx_total;
u8 *tx_buf, *rx_buf;
int status = -EFAULT;
spi_message_init(&msg);
k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
if (k_xfers == NULL)
return -ENOMEM;
/* Construct spi_message, copying any tx data to bounce buffer.
* We walk the array of user-provided transfers, using each one
* to initialize a kernel version of the same transfer.
*/
tx_buf = spidev->tx_buffer;
rx_buf = spidev->rx_buffer;
total = 0;
tx_total = 0;
rx_total = 0;
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
n > 0;
n--, k_tmp++, u_tmp++) {
k_tmp->len = u_tmp->len;
total += k_tmp->len;
/* Since the function returns the total length of transfers
* on success, restrict the total to positive int values to
* avoid the return value looking like an error. Also check
* each transfer length to avoid arithmetic overflow.
*/
if (total > INT_MAX || k_tmp->len > INT_MAX) {
status = -EMSGSIZE;
goto done;
}
if (u_tmp->rx_buf) {
/* this transfer needs space in RX bounce buffer */
rx_total += k_tmp->len;
if (rx_total > bufsiz) {
status = -EMSGSIZE;
goto done;
}
k_tmp->rx_buf = (void *)u_xfers->rx_buf;
//rx_buf += k_tmp->len;
}
if (u_tmp->tx_buf) {
/* this transfer needs space in TX bounce buffer */
tx_total += k_tmp->len;
if (tx_total > bufsiz) {
status = -EMSGSIZE;
goto done;
}
k_tmp->tx_buf = tx_buf;
strncpy(tx_buf,(char *)u_tmp->tx_buf,u_tmp->len);
tx_buf += k_tmp->len;
}
k_tmp->cs_change = !!u_tmp->cs_change;
k_tmp->tx_nbits = u_tmp->tx_nbits;
k_tmp->rx_nbits = u_tmp->rx_nbits;
k_tmp->bits_per_word = u_tmp->bits_per_word;
k_tmp->delay_usecs = u_tmp->delay_usecs;
k_tmp->speed_hz = u_tmp->speed_hz;
if (!k_tmp->speed_hz)
k_tmp->speed_hz = spidev->speed_hz;
spi_message_add_tail(k_tmp, &msg);
}
status = spidev_sync(spidev, &msg);
if (status < 0)
{
printk("%s LINE = %d\n",__func__,__LINE__);
goto done;
}
status = total;
done:
kfree(k_xfers);
return status;
}
/* There are probably characters waiting to be transmitted. */
static void n76e003_start_tx(struct uart_port *port)
{
struct circ_buf *xmit = &port->state->xmit;
int ret = 0;
int i = 0;
u8 recevie_buf = 0x0;
u8 send_buf = 0x0;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)&send_buf, //定义发送缓冲区指针
.rx_buf = (unsigned long)&recevie_buf, //定义接收缓冲区指针
.len = 1,
.delay_usecs = 0,
.speed_hz = 1000000,
.bits_per_word = 8,
};
if (uart_circ_empty(xmit))
return;
for(i=xmit->tail;i<xmit->head;i++)
{
send_buf = xmit->buf[xmit->tail];
ret = spidev_message(spi2uart_data,&tr,1);
printk("recevie_buf = %x\n",recevie_buf);
n76e003_transmit_chars(port);
n76e003_receive_chars(port,recevie_buf);
}
}
/* Throttle the remote when input buffer is about to overflow. */
static void n76e003_throttle(struct uart_port *port)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
spin_unlock_irqrestore(&port->lock, flags);
}
/* Unthrottle the remote, the input buffer can now accept data. */
static void n76e003_unthrottle(struct uart_port *port)
{
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
spin_unlock_irqrestore(&port->lock, flags);
}
/* Receive stop */
static void n76e003_stop_rx(struct uart_port *port)
{
}
/* Handle breaks - ignored by us */
static void n76e003_break_ctl(struct uart_port *port, int break_state)
{
}
static int n76e003_startup(struct uart_port *port)
{
const char *name = to_platform_device(port->dev)->name;
u32 val;
int ret;
ret = request_irq(port->irq, n76e003_interrupt, 0, name, port);
val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
return 0;
}
static void n76e003_shutdown(struct uart_port *port)
{
u32 val;
val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE;
free_irq(port->irq, port);
}
int send_spi_head(u8 byte0,u8 byte1)
{
return 0;
}
static void n76e003_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
struct n76e003_port *n76e003_port = to_n76e003_port(port);
unsigned int baud;
tcflag_t cflag = termios->c_cflag;
unsigned long flags;
int ret = 0;
u8 byte0 = 0; //type
u8 byte1 = 0; //baud size / cs size
if (!n76e003_port->hw_flow_control)
cflag &= ~CRTSCTS;
baud = uart_get_baud_rate(port, termios, old, 0, 460800);
spin_lock_irqsave(&port->lock, flags);
byte0 = 1 << COMMAND_BIT;
byte0 |= 1 << BAUD_BIT;
/*set 2 stop bit*/
if (cflag & CSTOPB)
{
byte0 |= 1 << CSTOPB_BIT;
}
if (cflag & PARENB)
{
byte0 |= 1 << PARENB_BIT;
}
if ((cflag & CSIZE) == CS8)
{
byte0 |= 1 << CSIZE_BIT;
}
if (cflag & PARODD)
{
byte0 |= 1 << PARODD_BIT;
}
port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
if (cflag & CRTSCTS) {
port->status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS;
byte0 |= 1 << CRTSCTS_BIT;
}
uart_update_timeout(port, cflag, baud);
port->read_status_mask = USART_SR_ORE;
if (termios->c_iflag & INPCK)
port->read_status_mask |= USART_SR_PE | USART_SR_FE;
if (termios->c_iflag & (IGNBRK | BRKINT | PARMRK))
port->read_status_mask |= USART_SR_LBD;
/* Characters to ignore */
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask = USART_SR_PE | USART_SR_FE;
if (termios->c_iflag & IGNBRK) {
port->ignore_status_mask |= USART_SR_LBD;
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= USART_SR_ORE;
}
/* Ignore all characters if CREAD is not set */
if ((termios->c_cflag & CREAD) == 0)
port->ignore_status_mask |= USART_SR_DUMMY_RX;
spin_unlock_irqrestore(&port->lock, flags);
ret = send_spi_head(byte0,byte1);
if(ret < 0)
pr_err("spi send head failed\n");
}
static const char *n76e003_type(struct uart_port *port)
{
return (port->type == PORT_N76E003) ? DRIVER_NAME : NULL;
}
static void n76e003_release_port(struct uart_port *port)
{
}
static int n76e003_request_port(struct uart_port *port)
{
return 0;
}
static void n76e003_config_port(struct uart_port *port, int flags)
{
port->type = PORT_N76E003;
}
static int
n76e003_verify_port(struct uart_port *port, struct serial_struct *ser)
{
/* No user changeable parameters */
return 0;
}
static void n76e003_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
unsigned long flags = 0;
switch (state) {
case UART_PM_STATE_ON:
break;
case UART_PM_STATE_OFF:
spin_lock_irqsave(&port->lock, flags);
spin_unlock_irqrestore(&port->lock, flags);
break;
}
}
static const struct uart_ops n76e003_uart_ops = {
.tx_empty = n76e003_tx_empty,
.set_mctrl = n76e003_set_mctrl,
.get_mctrl = n76e003_get_mctrl,
.stop_tx = n76e003_stop_tx,
.start_tx = n76e003_start_tx,
.throttle = n76e003_throttle,
.unthrottle = n76e003_unthrottle,
.stop_rx = n76e003_stop_rx,
.break_ctl = n76e003_break_ctl,
.startup = n76e003_startup,
.shutdown = n76e003_shutdown,
.set_termios = n76e003_set_termios,
.pm = n76e003_pm,
.type = n76e003_type,
.release_port = n76e003_release_port,
.request_port = n76e003_request_port,
.config_port = n76e003_config_port,
.verify_port = n76e003_verify_port,
};
static int n76e003_init_port(struct n76e003_port *n76e003port,
struct platform_device *pdev)
{
struct uart_port *port = &n76e003port->port;
port->iotype = UPIO_MEM;
port->flags = UPF_BOOT_AUTOCONF;
port->ops = &n76e003_uart_ops;
port->dev = &pdev->dev;
port->irq = platform_get_irq(pdev, 0);
port->iobase = 1;
spin_lock_init(&port->lock);
return 0;
}
static struct n76e003_port *n76e003_of_get_n76e003_port(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
int id;
if (!np)
return NULL;
id = of_alias_get_id(np, "serial");
if (id < 0)
id = 0;
if (WARN_ON(id >= n76e003_MAX_PORTS))
return NULL;
n76e003_ports[id].hw_flow_control = of_property_read_bool(np,
"auto-flow-control");
n76e003_ports[id].port.line = id;
return &n76e003_ports[id];
}
#ifdef CONFIG_OF
static const struct of_device_id n76e003_match[] = {
{ .compatible = "st,n76e003-usart", },
{ .compatible = "st,n76e003-uart", },
{},
};
MODULE_DEVICE_TABLE(of, n76e003_match);
#endif
static int n76e003_serial_probe(struct platform_device *pdev)
{
int ret;
struct n76e003_port *n76e003port;
n76e003port = n76e003_of_get_n76e003_port(pdev);
if (!n76e003port)
return -ENODEV;
ret = n76e003_init_port(n76e003port, pdev);
if (ret)
return ret;
ret = uart_add_one_port(&n76e003_usart_driver, &n76e003port->port);
if (ret)
return ret;
platform_set_drvdata(pdev, &n76e003port->port);
return 0;
}
static int n76e003_serial_remove(struct platform_device *pdev)
{
struct uart_port *port = platform_get_drvdata(pdev);
return uart_remove_one_port(&n76e003_usart_driver, port);
}
#ifdef CONFIG_SERIAL_N76E003_CONSOLE
static void n76e003_console_putchar(struct uart_port *port, int ch)
{
printk("%s LINE = %d\n",__func__,__LINE__);
}
static void n76e003_console_write(struct console *co, const char *s, unsigned cnt)
{
struct uart_port *port = &n76e003_ports[co->index].port;
unsigned long flags;
u32 old_cr1, new_cr1;
int locked = 1;
local_irq_save(flags);
if (port->sysrq)
locked = 0;
else if (oops_in_progress)
locked = spin_trylock(&port->lock);
else
spin_lock(&port->lock);
uart_console_write(port, s, cnt, n76e003_console_putchar);
if (locked)
spin_unlock(&port->lock);
local_irq_restore(flags);
}
static int n76e003_console_setup(struct console *co, char *options)
{
struct n76e003_port *n76e003port;
int baud = 9600;
int bits = 8;
int parity = 'n';
int flow = 'n';
if (co->index >= n76e003_MAX_PORTS)
return -ENODEV;
n76e003port = &n76e003_ports[co->index];
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
return uart_set_options(&n76e003port->port, co, baud, parity, bits, flow);
}
static struct console n76e003_console = {
.name = n76e003_SERIAL_NAME,
.device = uart_console_device,
.write = n76e003_console_write,
.setup = n76e003_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &n76e003_usart_driver,
};
#define n76e003_SERIAL_CONSOLE (&n76e003_console)
#else
#define n76e003_SERIAL_CONSOLE NULL
#endif /* CONFIG_SERIAL_N76E003_CONSOLE */
static struct uart_driver n76e003_usart_driver = {
.driver_name = DRIVER_NAME,
.dev_name = n76e003_SERIAL_NAME,
.major = 0,
.minor = 0,
.nr = n76e003_MAX_PORTS,
.cons = n76e003_SERIAL_CONSOLE,
};
static struct platform_driver n76e003_serial_driver = {
.probe = n76e003_serial_probe,
.remove = n76e003_serial_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(n76e003_match),
},
};
#ifdef CONFIG_OF
static const struct of_device_id spi2uart_dt_match[] = {
{ .compatible = "sunmi_spi2uart", },
{},
};
MODULE_DEVICE_TABLE(of, rockchip_spi_test_dt_match);
#endif /* CONFIG_OF */
static int spi2uart_probe(struct spi_device *spi)
{
int ret;
int id = 0;
if (!spi)
return -ENOMEM;
if (!spi->dev.of_node)
return -ENOMEM;
spi2uart_data = (struct spi2uart_data *)kzalloc(sizeof(struct spi2uart_data), GFP_KERNEL);
if (!spi2uart_data) {
dev_err(&spi->dev, "ERR: no memory for spi_test_data\n");
return -ENOMEM;
}
spi->bits_per_word = 8;
spi->mode = SPI_MODE_0;//SPI_MODE_0;
spi2uart_data->spi = spi;
spi2uart_data->dev = &spi->dev;
spi2uart_data->speed_hz = 1000000;
spi2uart_data->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
if (!spi2uart_data->tx_buffer) {
dev_err(&spi2uart_data->spi->dev, "open/ENOMEM\n");
ret = -ENOMEM;
}
spi2uart_data->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
if (!spi2uart_data->rx_buffer) {
dev_err(&spi2uart_data->spi->dev, "open/ENOMEM\n");
ret = -ENOMEM;
}
ret = spi_setup(spi);
if (ret < 0) {
dev_err(spi2uart_data->dev, "ERR: fail to setup spi\n");
return -1;
}
if (of_property_read_u32(spi->dev.of_node, "id", &id)) {
dev_warn(&spi->dev, "fail to get id, default set 0\n");
id = 0;
}
printk("%s:name=%s,bus_num=%d,cs=%d,mode=%d,speed=%d\n", __func__, spi->modalias, spi->master->bus_num, spi->chip_select, spi->mode, spi->max_speed_hz);
return 0;
}
static int spi2uart_remove(struct spi_device *spi)
{
printk("%s\n", __func__);
return 0;
}
static struct spi_driver spi2uart_driver = {
.driver = {
.name = "spi2uart",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(spi2uart_dt_match),
},
.probe = spi2uart_probe,
.remove = spi2uart_remove,
};
static int __init usart_init(void)
{
static char banner[] __initdata = "n76e003 USART driver initialized";
int ret;
pr_info("%s\n", banner);
ret = spi_register_driver(&spi2uart_driver);
if (ret)
return ret;
ret = uart_register_driver(&n76e003_usart_driver);
if (ret)
return ret;
ret = platform_driver_register(&n76e003_serial_driver);
if (ret)
uart_unregister_driver(&n76e003_usart_driver);
return ret;
}
static void __exit usart_exit(void)
{
platform_driver_unregister(&n76e003_serial_driver);
uart_unregister_driver(&n76e003_usart_driver);
}
module_init(usart_init);
module_exit(usart_exit);
MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_DESCRIPTION("sunmi n76e003 serial port driver");
MODULE_LICENSE("GPL v2");
kernel\arch\arm64\boot\dts\rockchip\rk3399-sapphire-excavator-edp.dts
n76e003 {
status = "okay";
compatible = "st,n76e003-uart";
};
..........
&spi2{
status = "okay";
max-freq = <48000000>;
pinctrl-names = "default";
pinctrl-0 = <&spi2_clk &spi2_tx &spi2_rx &spi2_cs0>;
spi2uart@0 {
compatible ="sunmi_spi2uart";
reg = <0>;
spi-max-frequency = <1000000>;
status = "okay";
};
};