misc: add qrc_core and qrc_uart
1, This driver is used as robotic controller. When robotic controller connect main board with uart bus, kernel need to provide a driver to load it. 2, This driver provides functions: (1) Read data from serial device port. (2) Write data to serial device port. Change-Id: Id53e8a898a11f1f0542a792ea2f8cf613628c301 Signed-off-by: jionzhao <jionzhao@codeaurora.org>
This commit is contained in:
committed by
Gerrit - the friendly Code Review server
parent
112597fd22
commit
459fb44e93
@@ -637,6 +637,7 @@ source "drivers/misc/cxl/Kconfig"
|
||||
source "drivers/misc/ocxl/Kconfig"
|
||||
source "drivers/misc/cardreader/Kconfig"
|
||||
source "drivers/misc/fpr_FingerprintCard/Kconfig"
|
||||
source "drivers/misc/qrc/Kconfig"
|
||||
endmenu
|
||||
|
||||
config OKL4_USER_VIPC
|
||||
|
||||
@@ -76,3 +76,4 @@ obj-$(CONFIG_WIGIG_SENSING_SPI) += wigig_sensing.o
|
||||
obj-$(CONFIG_QTI_MAXIM_FAN_CONTROLLER) += max31760.o
|
||||
obj-$(CONFIG_QTI_XR_SMRTVWR_MISC) += qxr-stdalonevwr.o
|
||||
obj-$(CONFIG_FPR_FPC) += fpr_FingerprintCard/
|
||||
obj-y += qrc/
|
||||
|
||||
25
drivers/misc/qrc/Kconfig
Normal file
25
drivers/misc/qrc/Kconfig
Normal file
@@ -0,0 +1,25 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# QRC device driver configuration
|
||||
#
|
||||
|
||||
menu "qrc device driver"
|
||||
|
||||
config QRC
|
||||
bool "QRC device driver for Robotic SDK MCU"
|
||||
help
|
||||
This kernel configuration is used to enable robotic controller
|
||||
device driver. Say Y here if you want to enable robotic
|
||||
controller device driver.
|
||||
When in doubt, say N.
|
||||
|
||||
config QRC_DEBUG
|
||||
bool "QRC Debugging"
|
||||
depends on QRC
|
||||
help
|
||||
Say Y here if you want the robotic controller to produce
|
||||
a bunch of debug messages to the system log. Select this if you
|
||||
are having a problem with robotic controller support and want
|
||||
to see more of what is going on.
|
||||
When in doubt, say N.
|
||||
endmenu
|
||||
9
drivers/misc/qrc/Makefile
Normal file
9
drivers/misc/qrc/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Makefile for the QRC bus specific drivers.
|
||||
|
||||
|
||||
obj-$(CONFIG_QRC) += qrc_core.o qrc_uart.o
|
||||
|
||||
|
||||
#ccflags-$(CONFIG_QRC_DEBUG) := -DDEBUG
|
||||
215
drivers/misc/qrc/qrc_core.c
Normal file
215
drivers/misc/qrc/qrc_core.c
Normal file
@@ -0,0 +1,215 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* driver/misc/qrc/qrc_core.c
|
||||
*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "qrc_core.h"
|
||||
|
||||
#define FIFO_CLEAR 0x1
|
||||
|
||||
#define QRC_DEVICE_NAME "qrc"
|
||||
|
||||
static dev_t qrc_devt;
|
||||
static struct class *qrc_class;
|
||||
|
||||
static int qrc_cdev_fasync(int fd, struct file *filp, int mode)
|
||||
{
|
||||
struct qrc_dev *qrc;
|
||||
|
||||
qrc = filp->private_data;
|
||||
return fasync_helper(fd, filp, mode, &qrc->async_queue);
|
||||
}
|
||||
|
||||
static int qrc_cdev_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct qrc_dev *qrc;
|
||||
|
||||
qrc = container_of(inode->i_cdev,
|
||||
struct qrc_dev, cdev);
|
||||
filp->private_data = qrc;
|
||||
if (qrc->qrc_ops != NULL)
|
||||
qrc->qrc_ops->qrcops_open(qrc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qrc_cdev_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct qrc_dev *qrc;
|
||||
|
||||
qrc = filp->private_data;
|
||||
if (qrc->qrc_ops != NULL)
|
||||
qrc->qrc_ops->qrcops_close(qrc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long qrc_cdev_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct qrc_dev *qrc;
|
||||
|
||||
qrc = filp->private_data;
|
||||
switch (cmd) {
|
||||
case FIFO_CLEAR:
|
||||
mutex_lock(&qrc->mutex);
|
||||
//check kfifo if have data
|
||||
mutex_unlock(&qrc->mutex);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int qrc_cdev_poll(struct file *filp, poll_table *wait)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
struct qrc_dev *qrc;
|
||||
|
||||
qrc = filp->private_data;
|
||||
mutex_lock(&qrc->mutex);
|
||||
|
||||
poll_wait(filp, &qrc->r_wait, wait);
|
||||
|
||||
if (qrc->qrc_ops->qrcops_data_status(qrc) != 0)
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
|
||||
mutex_unlock(&qrc->mutex);
|
||||
return mask;
|
||||
}
|
||||
|
||||
static ssize_t qrc_cdev_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int ret;
|
||||
struct qrc_dev *qrc;
|
||||
|
||||
qrc = filp->private_data;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
mutex_lock(&qrc->mutex);
|
||||
add_wait_queue(&qrc->r_wait, &wait);
|
||||
|
||||
while (qrc->qrc_ops->qrcops_data_status(qrc) == 0) {
|
||||
|
||||
if (filp->f_flags & O_NONBLOCK) {
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
__set_current_state(TASK_INTERRUPTIBLE);
|
||||
mutex_unlock(&qrc->mutex);
|
||||
|
||||
schedule();
|
||||
if (signal_pending(current)) {
|
||||
ret = -ERESTARTSYS;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
mutex_lock(&qrc->mutex);
|
||||
}
|
||||
|
||||
ret = qrc->qrc_ops->qrcops_receive(qrc, buf, count);
|
||||
|
||||
out:
|
||||
mutex_unlock(&qrc->mutex);
|
||||
out2:
|
||||
remove_wait_queue(&qrc->r_wait, &wait);
|
||||
set_current_state(TASK_RUNNING);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t qrc_cdev_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct qrc_dev *qrc;
|
||||
struct qrcuart *qrcuart;
|
||||
enum qrcdev_tx ret;
|
||||
|
||||
qrc = filp->private_data;
|
||||
ret = qrc->qrc_ops->qrcops_xmit(buf, count, qrc);
|
||||
if (ret == QRCDEV_TX_OK)
|
||||
return count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations qrc_cdev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = qrc_cdev_read,
|
||||
.write = qrc_cdev_write,
|
||||
.unlocked_ioctl = qrc_cdev_ioctl,
|
||||
.poll = qrc_cdev_poll,
|
||||
.fasync = qrc_cdev_fasync,
|
||||
.open = qrc_cdev_open,
|
||||
.release = qrc_cdev_release,
|
||||
};
|
||||
|
||||
/*-------Interface for qrc device ---------*/
|
||||
int qrc_register_device(struct qrc_dev *qdev, struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
dev_t devt;
|
||||
|
||||
if (!qdev)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&qdev->mutex);
|
||||
init_waitqueue_head(&qdev->r_wait);
|
||||
init_waitqueue_head(&qdev->w_wait);
|
||||
|
||||
//register cdev
|
||||
qrc_class = class_create(THIS_MODULE, "qrc_class");
|
||||
if (IS_ERR(qrc_class)) {
|
||||
pr_err("failed to allocate class\n");
|
||||
return PTR_ERR(qrc_class);
|
||||
}
|
||||
ret = alloc_chrdev_region(&qrc_devt, 0, 1, "qrc");
|
||||
if (ret < 0) {
|
||||
pr_err("failed to allocate char device region\n");
|
||||
class_destroy(qrc_class);
|
||||
return ret;
|
||||
}
|
||||
|
||||
devt = MKDEV(MAJOR(qrc_devt), 0);
|
||||
|
||||
cdev_init(&qdev->cdev, &qrc_cdev_fops);
|
||||
|
||||
ret = cdev_add(&qdev->cdev, devt, 1);
|
||||
if (ret) {
|
||||
pr_err("qrc failed to add char device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
qdev->dev = device_create(qrc_class, dev, devt, qdev,
|
||||
"qrc");
|
||||
if (IS_ERR(qdev->dev)) {
|
||||
ret = PTR_ERR(qdev->dev);
|
||||
goto del_cdev;
|
||||
}
|
||||
|
||||
dev_info(dev, "qrc device registered\n");
|
||||
return 0;
|
||||
|
||||
del_cdev:
|
||||
cdev_del(&qdev->cdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void qrc_unregister(struct qrc_dev *qdev)
|
||||
{
|
||||
device_destroy(qrc_class, qdev->dev->devt);
|
||||
dev_info(qdev->dev, "qrc drv unregistered\n");
|
||||
}
|
||||
123
drivers/misc/qrc/qrc_core.h
Normal file
123
drivers/misc/qrc/qrc_core.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* driver/misc/qrc/qrc_core.h
|
||||
*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _QRC_CORE_H
|
||||
#define _QRC_CORE_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define QRC_NAME_SIZE 30
|
||||
#define QRC_INTERFACE_SIZE 30
|
||||
#define QRC_FIFO_SIZE 0x1000
|
||||
|
||||
struct qrc_dev;
|
||||
|
||||
enum qrcdev_state_t {
|
||||
__STATE_IDLE,
|
||||
__STATE_READING,
|
||||
__STATE_WRITING,
|
||||
};
|
||||
|
||||
enum qrc_interface {
|
||||
UART = 0,
|
||||
SPI,
|
||||
};
|
||||
|
||||
enum qrcdev_tx {
|
||||
__QRCDEV_TX_MIN = INT_MIN, /* make sure enum is signed (-1)*/
|
||||
QRCDEV_TX_OK = 0x00, /* driver took care of packet */
|
||||
QRCDEV_TX_BUSY = 0x10, /* driver tx path was busy*/
|
||||
};
|
||||
|
||||
struct qrc_device_stats {
|
||||
unsigned long rx_bytes;
|
||||
unsigned long tx_bytes;
|
||||
unsigned long rx_errors;
|
||||
unsigned long tx_errors;
|
||||
unsigned long collisions;
|
||||
unsigned long rx_length_errors;
|
||||
unsigned long rx_over_errors;
|
||||
unsigned long rx_fifo_errors;
|
||||
};
|
||||
|
||||
struct qrc_device_ops {
|
||||
int (*qrcops_init)(struct qrc_dev *dev);
|
||||
void (*qrcops_uninit)(struct qrc_dev *dev);
|
||||
int (*qrcops_open)(struct qrc_dev *dev);
|
||||
int (*qrcops_close)(struct qrc_dev *dev);
|
||||
int (*qrcops_setup)(struct qrc_dev *dev);
|
||||
enum qrcdev_tx (*qrcops_xmit)(const char __user *buf,
|
||||
size_t data_length, struct qrc_dev *dev);
|
||||
int (*qrcops_receive)(struct qrc_dev *dev,
|
||||
char __user *buf, size_t count);
|
||||
int (*qrcops_data_status)
|
||||
(struct qrc_dev *dev);
|
||||
int (*qrcops_config)(struct qrc_dev *dev);
|
||||
};
|
||||
|
||||
/* qrc char device */
|
||||
struct qrc_dev {
|
||||
struct qrc_device_stats stats;
|
||||
/* qrc dev ops */
|
||||
struct qrc_device_ops *qrc_ops;
|
||||
struct mutex mutex;
|
||||
wait_queue_head_t r_wait;
|
||||
wait_queue_head_t w_wait;
|
||||
struct fasync_struct *async_queue;
|
||||
struct cdev cdev;
|
||||
struct device *dev;
|
||||
void *data;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct qrcuart - The qrcuart device structure.
|
||||
* @qrc_dev: This is robotic controller device structure.
|
||||
* It include interface for qrcuart.
|
||||
* @lock: spinlock for transmitting lock.
|
||||
* @tx_work: Flushes transmit TX buffer.
|
||||
* @serdev: Serial device bus structure.
|
||||
* @qrc_rx_fifo: Qrcuart receive buffer.
|
||||
* @tx_head: String head in XMIT queue.
|
||||
* @tx_left: Bytes left in XMIT queue.
|
||||
* @tx_buffer: XMIT buffer.
|
||||
* This structure is used to define robotic controller uart device.
|
||||
*/
|
||||
struct qrcuart {
|
||||
struct qrc_dev *qrc_dev;
|
||||
spinlock_t lock;
|
||||
struct work_struct tx_work;
|
||||
struct serdev_device *serdev;
|
||||
struct kfifo qrc_rx_fifo;
|
||||
unsigned char *tx_head;
|
||||
int tx_left;
|
||||
unsigned char *tx_buffer;
|
||||
};
|
||||
|
||||
struct qrcspi {
|
||||
struct qrc_dev *qrc_dev;
|
||||
spinlock_t lock; /* transmit lock */
|
||||
};
|
||||
|
||||
static inline void qrc_set_data(struct qrc_dev *dev, void *data)
|
||||
{
|
||||
dev->data = data;
|
||||
}
|
||||
static inline void *qrc_get_data(const struct qrc_dev *dev)
|
||||
{
|
||||
return dev->data;
|
||||
}
|
||||
|
||||
int qrc_register_device(struct qrc_dev *qdev, struct device *dev);
|
||||
void qrc_unregister(struct qrc_dev *qdev);
|
||||
|
||||
#endif
|
||||
|
||||
324
drivers/misc/qrc/qrc_uart.c
Normal file
324
drivers/misc/qrc/qrc_uart.c
Normal file
@@ -0,0 +1,324 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* driver/misc/qrc/qrc_uart.c
|
||||
*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/serdev.h>
|
||||
#include <linux/types.h>
|
||||
#include<linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "qrc_core.h"
|
||||
|
||||
#define QRC_RX_FIFO_SIZE 0x400
|
||||
#define QRC_TX_BUFF_SIZE 0x400
|
||||
#define QRCUART_DRV_NAME "qrcuart"
|
||||
#define QRC_DRV_VERSION "0.1.0"
|
||||
|
||||
|
||||
static int qrcuart_setup(struct qrc_dev *dev);
|
||||
|
||||
static int
|
||||
qrc_uart_receive(struct serdev_device *serdev, const unsigned char *data,
|
||||
size_t count)
|
||||
{
|
||||
struct qrcuart *qrc = serdev_device_get_drvdata(serdev);
|
||||
struct qrc_dev *qrc_dev = qrc->qrc_dev;
|
||||
int ret;
|
||||
|
||||
/* check count */
|
||||
ret = kfifo_avail(&qrc->qrc_rx_fifo);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
if (count > ret)
|
||||
count = ret;
|
||||
|
||||
ret = kfifo_in(&qrc->qrc_rx_fifo, data, count);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
wake_up_interruptible(&qrc_dev->r_wait);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Write out any remaining transmit buffer. Scheduled when tty is writable */
|
||||
static void qrcuart_transmit(struct work_struct *work)
|
||||
{
|
||||
struct qrcuart *qrc = container_of(work, struct qrcuart, tx_work);
|
||||
int written;
|
||||
|
||||
spin_lock_bh(&qrc->lock);
|
||||
|
||||
if (qrc->tx_left <= 0) {
|
||||
/* Now serial buffer is almost free & we can start
|
||||
* transmission of another packet
|
||||
*/
|
||||
spin_unlock_bh(&qrc->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
written = serdev_device_write_buf(qrc->serdev, qrc->tx_head,
|
||||
qrc->tx_left);
|
||||
if (written > 0) {
|
||||
qrc->tx_left -= written;
|
||||
qrc->tx_head += written;
|
||||
}
|
||||
spin_unlock_bh(&qrc->lock);
|
||||
}
|
||||
|
||||
/* Called by the driver when there's room for more data.
|
||||
* Schedule the transmit.
|
||||
*/
|
||||
static void qrc_uart_wakeup(struct serdev_device *serdev)
|
||||
{
|
||||
struct qrcuart *qrc = serdev_device_get_drvdata(serdev);
|
||||
|
||||
schedule_work(&qrc->tx_work);
|
||||
}
|
||||
|
||||
static struct serdev_device_ops qrc_serdev_ops = {
|
||||
.receive_buf = qrc_uart_receive,
|
||||
.write_wakeup = qrc_uart_wakeup,
|
||||
};
|
||||
|
||||
/*----------------Interface to QRC core -----------------------------*/
|
||||
|
||||
static int qrcuart_open(struct qrc_dev *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qrcuart_close(struct qrc_dev *dev)
|
||||
{
|
||||
struct qrcuart *qrc = qrc_get_data(dev);
|
||||
|
||||
flush_work(&qrc->tx_work);
|
||||
|
||||
spin_lock_bh(&qrc->lock);
|
||||
qrc->tx_left = 0;
|
||||
spin_unlock_bh(&qrc->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qrcuart_init(struct qrc_dev *dev)
|
||||
{
|
||||
struct qrcuart *qrc = qrc_get_data(dev);
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
/* Finish setting up the device info. */
|
||||
len = QRC_TX_BUFF_SIZE;
|
||||
qrc->tx_buffer = devm_kmalloc(&qrc->serdev->dev, len, GFP_KERNEL);
|
||||
|
||||
if (!qrc->tx_buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
qrc->tx_head = qrc->tx_buffer;
|
||||
qrc->tx_left = 0;
|
||||
|
||||
ret = kfifo_alloc(&qrc->qrc_rx_fifo, QRC_RX_FIFO_SIZE,
|
||||
GFP_KERNEL);
|
||||
if (ret)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static void qrcuart_uninit(struct qrc_dev *dev)
|
||||
{
|
||||
struct qrcuart *qrc = qrc_get_data(dev);
|
||||
|
||||
kfifo_free(&qrc->qrc_rx_fifo);
|
||||
}
|
||||
|
||||
/*put data from kfifo to qrc fifo */
|
||||
static int qrcuart_receive(struct qrc_dev *dev, char __user *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct qrcuart *qrc = qrc_get_data(dev);
|
||||
u32 fifo_len, trans_len;
|
||||
|
||||
if (!kfifo_is_empty(&qrc->qrc_rx_fifo)) {
|
||||
fifo_len = kfifo_len(&qrc->qrc_rx_fifo);
|
||||
if (count > fifo_len)
|
||||
count = fifo_len;
|
||||
if (kfifo_to_user(&qrc->qrc_rx_fifo,
|
||||
(void *)buf, count, &trans_len))
|
||||
return -EFAULT;
|
||||
return trans_len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qrcuart_data_status(struct qrc_dev *dev)
|
||||
{
|
||||
struct qrcuart *qrc = qrc_get_data(dev);
|
||||
|
||||
return kfifo_len(&qrc->qrc_rx_fifo);
|
||||
}
|
||||
|
||||
static enum qrcdev_tx qrcuart_xmit(const char __user *buf,
|
||||
size_t data_length, struct qrc_dev *dev)
|
||||
{
|
||||
struct qrcuart *qrc = qrc_get_data(dev);
|
||||
struct qrc_device_stats *n_stats = &dev->stats;
|
||||
size_t written;
|
||||
u8 *pos;
|
||||
|
||||
WARN_ON(qrc->tx_left);
|
||||
|
||||
pos = qrc->tx_buffer + qrc->tx_left;
|
||||
if ((data_length + qrc->tx_left) > QRC_TX_BUFF_SIZE) {
|
||||
pr_err("qrcuart transmit date overflow %d\n", data_length);
|
||||
return __QRCDEV_TX_MIN;
|
||||
}
|
||||
|
||||
if (copy_from_user(pos, buf, data_length))
|
||||
return __QRCDEV_TX_MIN;
|
||||
|
||||
pos += data_length;
|
||||
|
||||
spin_lock(&qrc->lock);
|
||||
|
||||
written = serdev_device_write_buf(qrc->serdev, qrc->tx_buffer,
|
||||
pos - qrc->tx_buffer);
|
||||
if (written > 0) {
|
||||
qrc->tx_left = (pos - qrc->tx_buffer) - written;
|
||||
qrc->tx_head = qrc->tx_buffer + written;
|
||||
n_stats->tx_bytes += written;
|
||||
}
|
||||
|
||||
spin_unlock(&qrc->lock);
|
||||
|
||||
return QRCDEV_TX_OK;
|
||||
}
|
||||
|
||||
static int qrcuart_config(struct qrc_dev *dev)
|
||||
{
|
||||
//baudrate,wordlength ... config
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct qrc_device_ops qrcuart_qrc_ops = {
|
||||
.qrcops_open = qrcuart_open,
|
||||
.qrcops_close = qrcuart_close,
|
||||
.qrcops_init = qrcuart_init,
|
||||
.qrcops_uninit = qrcuart_uninit,
|
||||
.qrcops_xmit = qrcuart_xmit,
|
||||
.qrcops_receive = qrcuart_receive,
|
||||
.qrcops_config = qrcuart_config,
|
||||
.qrcops_setup = qrcuart_setup,
|
||||
.qrcops_data_status = qrcuart_data_status,
|
||||
};
|
||||
|
||||
static int qrcuart_setup(struct qrc_dev *dev)
|
||||
{
|
||||
dev->qrc_ops = &qrcuart_qrc_ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qrc_uart_probe(struct serdev_device *serdev)
|
||||
{
|
||||
struct qrc_dev *qdev;
|
||||
struct qrcuart *qrc;
|
||||
const char *mac;
|
||||
u32 speed = 115200;
|
||||
int ret;
|
||||
|
||||
qdev = kmalloc(sizeof(*qdev), GFP_KERNEL);
|
||||
qrc = kmalloc(sizeof(*qrc), GFP_KERNEL);
|
||||
if ((!qrc) || (!qdev)) {
|
||||
pr_err("qrc_uart: Fail to retrieve private structure\n");
|
||||
goto free;
|
||||
}
|
||||
qrc_set_data(qdev, qrc);
|
||||
|
||||
qrc->qrc_dev = qdev;
|
||||
qrc->serdev = serdev;
|
||||
spin_lock_init(&qrc->lock);
|
||||
INIT_WORK(&qrc->tx_work, qrcuart_transmit);
|
||||
qrcuart_setup(qdev);
|
||||
ret = qrcuart_init(qdev);
|
||||
if (ret) {
|
||||
qrcuart_uninit(qdev);
|
||||
pr_err("qrcuart: Fail to init qrc structure\n");
|
||||
return ret;
|
||||
}
|
||||
serdev_device_set_drvdata(serdev, qrc);
|
||||
serdev_device_set_client_ops(serdev, &qrc_serdev_ops);
|
||||
|
||||
ret = serdev_device_open(serdev);
|
||||
if (ret) {
|
||||
pr_err("qrcuart :Unable to open device\n");
|
||||
ret = -ENOMEM;
|
||||
goto free;
|
||||
}
|
||||
|
||||
speed = serdev_device_set_baudrate(serdev, speed);
|
||||
serdev_device_set_flow_control(serdev, false);
|
||||
|
||||
ret = qrc_register_device(qdev, &serdev->dev);
|
||||
if (ret) {
|
||||
pr_err("qrcuart: Unable to register qrc device %s\n");
|
||||
serdev_device_close(serdev);
|
||||
cancel_work_sync(&qrc->tx_work);
|
||||
goto free;
|
||||
}
|
||||
dev_info(&serdev->dev, "qrcuart drv probed\n");
|
||||
|
||||
return 0;
|
||||
|
||||
free:
|
||||
kfree(qdev);
|
||||
kfree(qrc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qrc_uart_remove(struct serdev_device *serdev)
|
||||
{
|
||||
struct qrcuart *qrc = serdev_device_get_drvdata(serdev);
|
||||
|
||||
serdev_device_close(serdev);
|
||||
qrcuart_uninit(qrc->qrc_dev);
|
||||
cancel_work_sync(&qrc->tx_work);
|
||||
qrc_unregister(qrc->qrc_dev);
|
||||
kfree(qrc->qrc_dev);
|
||||
kfree(qrc);
|
||||
dev_info(&serdev->dev, "qrcuart drv removed\n");
|
||||
}
|
||||
|
||||
static const struct of_device_id qrc_uart_of_match[] = {
|
||||
{
|
||||
.compatible = "qcom,qrc-uart",
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qrc_of_match);
|
||||
|
||||
|
||||
static struct serdev_device_driver qrc_uart_driver = {
|
||||
.probe = qrc_uart_probe,
|
||||
.remove = qrc_uart_remove,
|
||||
.driver = {
|
||||
.name = QRCUART_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(qrc_uart_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_serdev_device_driver(qrc_uart_driver);
|
||||
|
||||
/**********************************************/
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. QRC Uart Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
Reference in New Issue
Block a user