Message ID | 20240304-qcom_qrc-v1-1-2a709f95fd61@quicinc.com (mailing list archive) |
---|---|
State | Not Applicable |
Headers | show |
Series | QCOM QRC device driver | expand |
On Mon, Mar 04, 2024 at 12:53:16AM +0800, Canfeng Zhuang wrote: > QRC Driver support functions: What is "QRC"? > - Read data from serial device port. > - Write data to serial device port. Shouldn't you be doing that in userspace? Why is this a kernel driver? > - Pin control reset robotic controller. > > Signed-off-by: Canfeng Zhuang <quic_czhuang@quicinc.com> > --- > drivers/misc/Kconfig | 1 + > drivers/misc/Makefile | 1 + > drivers/misc/qrc/Kconfig | 16 ++ > drivers/misc/qrc/Makefile | 6 + > drivers/misc/qrc/qrc_core.c | 336 ++++++++++++++++++++++++++++++++++++++++++ > drivers/misc/qrc/qrc_core.h | 143 ++++++++++++++++++ Why do you have a .h file for a single driver? > drivers/misc/qrc/qrc_uart.c | 345 ++++++++++++++++++++++++++++++++++++++++++++ > 7 files changed, 848 insertions(+) > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig > index 4fb291f0bf7c..a43108af6fde 100644 > --- a/drivers/misc/Kconfig > +++ b/drivers/misc/Kconfig > @@ -591,4 +591,5 @@ source "drivers/misc/cardreader/Kconfig" > source "drivers/misc/uacce/Kconfig" > source "drivers/misc/pvpanic/Kconfig" > source "drivers/misc/mchp_pci1xxxx/Kconfig" > +source "drivers/misc/qrc/Kconfig" > endmenu > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile > index ea6ea5bbbc9c..ab3b2c4d99fa 100644 > --- a/drivers/misc/Makefile > +++ b/drivers/misc/Makefile > @@ -68,3 +68,4 @@ obj-$(CONFIG_TMR_INJECT) += xilinx_tmr_inject.o > obj-$(CONFIG_TPS6594_ESM) += tps6594-esm.o > obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o > obj-$(CONFIG_NSM) += nsm.o > +obj-$(CONFIG_QCOM_QRC) += qrc/ > diff --git a/drivers/misc/qrc/Kconfig b/drivers/misc/qrc/Kconfig > new file mode 100644 > index 000000000000..994985d7c320 > --- /dev/null > +++ b/drivers/misc/qrc/Kconfig > @@ -0,0 +1,16 @@ > +# SPDX-License-Identifier: GPL-2.0-only > +# > +# QRC device driver configuration > +# > + > +menu "QCOM QRC device driver" > + > +config QCOM_QRC > + tristate "QCOM QRC device driver for Robotic SDK MCU" What is "QCOM", "QRC", SDK", and "MCU"? > + help > + This kernel configuration is used to enable robotic controller > + device driver. Say M here if you want to enable robotic > + controller device driver. Please write more here, this doesn't really describe what is happening. > + When in doubt, say N. > + > +endmenu > diff --git a/drivers/misc/qrc/Makefile b/drivers/misc/qrc/Makefile > new file mode 100644 > index 000000000000..da2cf81f3c59 > --- /dev/null > +++ b/drivers/misc/qrc/Makefile > @@ -0,0 +1,6 @@ > +# SPDX-License-Identifier: GPL-2.0-only > +# > +# Makefile for the QRC bus specific drivers. > + > + > +obj-$(CONFIG_QCOM_QRC) += qrc_core.o qrc_uart.o Why is a single driver 2 different files? > diff --git a/drivers/misc/qrc/qrc_core.c b/drivers/misc/qrc/qrc_core.c > new file mode 100644 > index 000000000000..5cedb050dac4 > --- /dev/null > +++ b/drivers/misc/qrc/qrc_core.c > @@ -0,0 +1,336 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* driver/misc/qrc/qrc_core.c Why is the filename here? > + * > + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. 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 <linux/of.h> > +#include <linux/of_gpio.h> > +#include <linux/gpio.h> > +#include <linux/delay.h> > + > +#include "qrc_core.h" > + > +#define QRC_DEVICE_NAME "qrc" KBUILD_MODNAME. > +static dev_t qrc_devt; > +static struct class *qrc_class; Please use class_register(), not class_create(). I've stopped here, please get someone in your group to review this before sending it out again, you have internal experience with kernel drivers, please take advantage of it. thanks, greg k-h
On 03/03/2024 17:53, Canfeng Zhuang wrote: > QRC Driver support functions: > - Read data from serial device port. > - Write data to serial device port. > - Pin control reset robotic controller. > > Signed-off-by: Canfeng Zhuang <quic_czhuang@quicinc.com> > --- > drivers/misc/Kconfig | 1 + > drivers/misc/Makefile | 1 + > drivers/misc/qrc/Kconfig | 16 ++ > drivers/misc/qrc/Makefile | 6 + > drivers/misc/qrc/qrc_core.c | 336 ++++++++++++++++++++++++++++++++++++++++++ > drivers/misc/qrc/qrc_core.h | 143 ++++++++++++++++++ > drivers/misc/qrc/qrc_uart.c | 345 ++++++++++++++++++++++++++++++++++++++++++++ > 7 files changed, 848 insertions(+) > > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig > index 4fb291f0bf7c..a43108af6fde 100644 > --- a/drivers/misc/Kconfig > +++ b/drivers/misc/Kconfig > @@ -591,4 +591,5 @@ source "drivers/misc/cardreader/Kconfig" > source "drivers/misc/uacce/Kconfig" > source "drivers/misc/pvpanic/Kconfig" > source "drivers/misc/mchp_pci1xxxx/Kconfig" > +source "drivers/misc/qrc/Kconfig" > endmenu > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile > index ea6ea5bbbc9c..ab3b2c4d99fa 100644 > --- a/drivers/misc/Makefile > +++ b/drivers/misc/Makefile > @@ -68,3 +68,4 @@ obj-$(CONFIG_TMR_INJECT) += xilinx_tmr_inject.o > obj-$(CONFIG_TPS6594_ESM) += tps6594-esm.o > obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o > obj-$(CONFIG_NSM) += nsm.o > +obj-$(CONFIG_QCOM_QRC) += qrc/ > diff --git a/drivers/misc/qrc/Kconfig b/drivers/misc/qrc/Kconfig > new file mode 100644 > index 000000000000..994985d7c320 > --- /dev/null > +++ b/drivers/misc/qrc/Kconfig > @@ -0,0 +1,16 @@ > +# SPDX-License-Identifier: GPL-2.0-only > +# > +# QRC device driver configuration > +# > + > +menu "QCOM QRC device driver" > + > +config QCOM_QRC > + tristate "QCOM QRC device driver for Robotic SDK MCU" > + help > + This kernel configuration is used to enable robotic controller > + device driver. Say M here if you want to enable robotic > + controller device driver. > + When in doubt, say N. > + > +endmenu > diff --git a/drivers/misc/qrc/Makefile b/drivers/misc/qrc/Makefile > new file mode 100644 > index 000000000000..da2cf81f3c59 > --- /dev/null > +++ b/drivers/misc/qrc/Makefile > @@ -0,0 +1,6 @@ > +# SPDX-License-Identifier: GPL-2.0-only > +# > +# Makefile for the QRC bus specific drivers. QRC bus? Nothing anywhere suggested this is a bus. Limited review because this really looks like some vendor code, not cleaned for upstream submission. > + > + > +obj-$(CONFIG_QCOM_QRC) += qrc_core.o qrc_uart.o ... > + > +static int qrcuart_config(struct qrc_dev *dev) > +{ > + /*baudrate,wordlength ... config*/ > + return 0; > +} > + > +static struct qrc_device_ops qrcuart_qrc_ops = { What is this and why do you need it? Don't define your ops without need. Just call functions directly. > + .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, > + .qrcops_data_clean = qrcuart_data_clean, > +}; > + > +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; > + int ret = 0; > + > + qrc = kmalloc(sizeof(*qrc), GFP_KERNEL); > + if (!qrc) > + return -ENOMEM; > + qdev = kmalloc(sizeof(*qdev), GFP_KERNEL); Just use devm*. What is this code? Ancient vendor, 20 year old stuff? > + if (!qdev) { > + kfree(qrc); > + return -ENOMEM; > + } > + qdev->dev = &serdev->dev; > + 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) { > + dev_err(qdev->dev, "qrcuart: Fail to init qrc structure\n"); > + kfree(qdev); > + kfree(qrc); > + return ret; > + } > + serdev_device_set_drvdata(serdev, qrc); > + serdev_device_set_client_ops(serdev, &qrc_serdev_ops); > + > + ret = serdev_device_open(serdev); > + if (ret) { > + dev_err(qdev->dev, "qrcuart :Unable to open device\n"); Whitespace typos. > + goto free; > + } > + serdev_device_close(serdev); > + qrc->is_open = false; > + > + ret = qrc_register_device(qdev, &serdev->dev); > + > + if (ret) { > + dev_err(qdev->dev, "qrcuart: Unable to register qrc device\n"); > + cancel_work_sync(&qrc->tx_work); > + goto free; > + } > + > + return 0; > + > +free: free or uninint? Your error handling is messy. > + qrcuart_uninit(qdev); > + kfree(qdev); > + kfree(qrc); > + return ret; > +} > + > +static void qrc_uart_remove(struct serdev_device *serdev) > +{ > + struct qrcuart *qrc = serdev_device_get_drvdata(serdev); > + > + if (qrc->is_open) > + 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"); Drop such simple function entry/exit messages. Not needed and not helpful. > +} > + > +static const struct of_device_id qrc_uart_of_match[] = { > + { .compatible = "qcom,qrc-uart", }, > + {} > +}; > + > +MODULE_DEVICE_TABLE(of, qrc_uart_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), Drop of_match_ptr. You have warnings here. > + }, > +}; > + > +module_serdev_device_driver(qrc_uart_driver); > + > +/**********************************************/ Drop > + > +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. QRC Uart Driver"); > +MODULE_LICENSE("GPL"); > Best regards, Krzysztof
Hi Canfeng, kernel test robot noticed the following build warnings: [auto build test WARNING on 805d849d7c3cc1f38efefd48b2480d62b7b5dcb7] url: https://github.com/intel-lab-lkp/linux/commits/Canfeng-Zhuang/misc-qualcomm-QRC-driver-for-Robotic-SDK-MCU/20240304-005601 base: 805d849d7c3cc1f38efefd48b2480d62b7b5dcb7 patch link: https://lore.kernel.org/r/20240304-qcom_qrc-v1-1-2a709f95fd61%40quicinc.com patch subject: [PATCH 1/2] misc: qualcomm: QRC driver for Robotic SDK MCU config: hexagon-allyesconfig (https://download.01.org/0day-ci/archive/20240304/202403040603.Dtb2fV1b-lkp@intel.com/config) compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project 325f51237252e6dab8e4e1ea1fa7acbb4faee1cd) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240304/202403040603.Dtb2fV1b-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202403040603.Dtb2fV1b-lkp@intel.com/ All warnings (new ones prefixed by >>): In file included from drivers/misc/qrc/qrc_uart.c:10: In file included from include/linux/serdev.h:10: In file included from include/linux/iopoll.h:14: In file included from include/linux/io.h:13: In file included from arch/hexagon/include/asm/io.h:328: include/asm-generic/io.h:547:31: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 547 | val = __raw_readb(PCI_IOBASE + addr); | ~~~~~~~~~~ ^ include/asm-generic/io.h:560:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 560 | val = __le16_to_cpu((__le16 __force)__raw_readw(PCI_IOBASE + addr)); | ~~~~~~~~~~ ^ include/uapi/linux/byteorder/little_endian.h:37:51: note: expanded from macro '__le16_to_cpu' 37 | #define __le16_to_cpu(x) ((__force __u16)(__le16)(x)) | ^ In file included from drivers/misc/qrc/qrc_uart.c:10: In file included from include/linux/serdev.h:10: In file included from include/linux/iopoll.h:14: In file included from include/linux/io.h:13: In file included from arch/hexagon/include/asm/io.h:328: include/asm-generic/io.h:573:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 573 | val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr)); | ~~~~~~~~~~ ^ include/uapi/linux/byteorder/little_endian.h:35:51: note: expanded from macro '__le32_to_cpu' 35 | #define __le32_to_cpu(x) ((__force __u32)(__le32)(x)) | ^ In file included from drivers/misc/qrc/qrc_uart.c:10: In file included from include/linux/serdev.h:10: In file included from include/linux/iopoll.h:14: In file included from include/linux/io.h:13: In file included from arch/hexagon/include/asm/io.h:328: include/asm-generic/io.h:584:33: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 584 | __raw_writeb(value, PCI_IOBASE + addr); | ~~~~~~~~~~ ^ include/asm-generic/io.h:594:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 594 | __raw_writew((u16 __force)cpu_to_le16(value), PCI_IOBASE + addr); | ~~~~~~~~~~ ^ include/asm-generic/io.h:604:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 604 | __raw_writel((u32 __force)cpu_to_le32(value), PCI_IOBASE + addr); | ~~~~~~~~~~ ^ In file included from drivers/misc/qrc/qrc_uart.c:13: In file included from drivers/misc/qrc/qrc_core.h:14: In file included from include/linux/kfifo.h:42: In file included from include/linux/scatterlist.h:8: In file included from include/linux/mm.h:2188: include/linux/vmstat.h:522:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion] 522 | return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_" | ~~~~~~~~~~~ ^ ~~~ >> drivers/misc/qrc/qrc_uart.c:203:61: warning: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Wformat] 203 | dev_err(dev->dev, "qrcuart transmit date overflow %ld\n", data_length); | ~~~ ^~~~~~~~~~~ | %zu include/linux/dev_printk.h:144:65: note: expanded from macro 'dev_err' 144 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ~~~ ^~~~~~~~~~~ include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ~~~ ^~~~~~~~~~~ 8 warnings generated. vim +203 drivers/misc/qrc/qrc_uart.c > 13 #include "qrc_core.h" 14 15 #define QRC_RX_FIFO_SIZE 0x400 16 #define QRC_TX_BUFF_SIZE 0x400 17 #define QRCUART_DRV_NAME "qrcuart" 18 #define QRC_DRV_VERSION "0.1.0" 19 20 static int qrcuart_setup(struct qrc_dev *dev); 21 22 static int 23 qrc_uart_receive(struct serdev_device *serdev, const unsigned char *data, 24 size_t count) 25 { 26 struct qrcuart *qrc = serdev_device_get_drvdata(serdev); 27 struct qrc_dev *qrc_dev = qrc->qrc_dev; 28 int ret; 29 30 /* check count */ 31 ret = kfifo_avail(&qrc->qrc_rx_fifo); 32 if (!ret) 33 return 0; 34 35 if (count > ret) 36 count = ret; 37 38 ret = kfifo_in(&qrc->qrc_rx_fifo, data, count); 39 if (!ret) 40 return 0; 41 42 wake_up_interruptible(&qrc_dev->r_wait); 43 44 return count; 45 } 46 47 /* Write out any remaining transmit buffer. Scheduled when tty is writable */ 48 static void qrcuart_transmit(struct work_struct *work) 49 { 50 struct qrcuart *qrc = container_of(work, struct qrcuart, tx_work); 51 int written; 52 53 spin_lock_bh(&qrc->lock); 54 55 if (qrc->tx_left <= 0) { 56 /* Now serial buffer is almost free & we can start 57 * transmission of another packet 58 */ 59 spin_unlock_bh(&qrc->lock); 60 return; 61 } 62 63 written = serdev_device_write_buf(qrc->serdev, qrc->tx_head, 64 qrc->tx_left); 65 if (written > 0) { 66 qrc->tx_left -= written; 67 qrc->tx_head += written; 68 } 69 spin_unlock_bh(&qrc->lock); 70 } 71 72 /* Called by the driver when there's room for more data. 73 * Schedule the transmit. 74 */ 75 static void qrc_uart_wakeup(struct serdev_device *serdev) 76 { 77 struct qrcuart *qrc = serdev_device_get_drvdata(serdev); 78 79 schedule_work(&qrc->tx_work); 80 } 81 82 static struct serdev_device_ops qrc_serdev_ops = { 83 .receive_buf = qrc_uart_receive, 84 .write_wakeup = qrc_uart_wakeup, 85 }; 86 87 /*----------------Interface to QRC core -----------------------------*/ 88 89 static int qrcuart_open(struct qrc_dev *dev) 90 { 91 struct qrcuart *qrc = qrc_get_data(dev); 92 struct serdev_device *serdev = qrc->serdev; 93 int ret; 94 95 if (!qrc->is_open) { 96 ret = serdev_device_open(serdev); 97 if (ret) { 98 dev_err(dev->dev, "qrcuart :Unable to open device\n"); 99 return ret; 100 } 101 serdev_device_set_baudrate(serdev, 115200); 102 serdev_device_set_flow_control(serdev, false); 103 qrc->is_open = true; 104 } 105 106 return 0; 107 } 108 109 static int qrcuart_close(struct qrc_dev *dev) 110 { 111 struct qrcuart *qrc = qrc_get_data(dev); 112 struct serdev_device *serdev = qrc->serdev; 113 114 flush_work(&qrc->tx_work); 115 spin_lock_bh(&qrc->lock); 116 qrc->tx_left = 0; 117 spin_unlock_bh(&qrc->lock); 118 119 if (qrc->is_open) { 120 serdev_device_close(serdev); 121 qrc->is_open = false; 122 } 123 124 return 0; 125 } 126 127 static int qrcuart_init(struct qrc_dev *dev) 128 { 129 struct qrcuart *qrc = qrc_get_data(dev); 130 size_t len; 131 int ret; 132 133 /* Finish setting up the device info. */ 134 len = QRC_TX_BUFF_SIZE; 135 qrc->tx_buffer = devm_kmalloc(&qrc->serdev->dev, len, GFP_KERNEL); 136 137 if (!qrc->tx_buffer) 138 return -ENOMEM; 139 140 qrc->tx_head = qrc->tx_buffer; 141 qrc->tx_left = 0; 142 143 ret = kfifo_alloc(&qrc->qrc_rx_fifo, QRC_RX_FIFO_SIZE, 144 GFP_KERNEL); 145 if (ret) 146 return -ENOMEM; 147 148 return 0; 149 } 150 151 static void qrcuart_uninit(struct qrc_dev *dev) 152 { 153 struct qrcuart *qrc = qrc_get_data(dev); 154 155 kfifo_free(&qrc->qrc_rx_fifo); 156 } 157 158 /*put data from kfifo to qrc fifo */ 159 static int qrcuart_receive(struct qrc_dev *dev, char __user *buf, 160 size_t count) 161 { 162 struct qrcuart *qrc = qrc_get_data(dev); 163 u32 fifo_len, trans_len; 164 165 if (!kfifo_is_empty(&qrc->qrc_rx_fifo)) { 166 fifo_len = kfifo_len(&qrc->qrc_rx_fifo); 167 if (count > fifo_len) 168 count = fifo_len; 169 if (kfifo_to_user(&qrc->qrc_rx_fifo, 170 (void *)buf, count, &trans_len)) 171 return -EFAULT; 172 return trans_len; 173 } 174 return 0; 175 } 176 177 static int qrcuart_data_status(struct qrc_dev *dev) 178 { 179 struct qrcuart *qrc = qrc_get_data(dev); 180 181 return kfifo_len(&qrc->qrc_rx_fifo); 182 } 183 184 static void qrcuart_data_clean(struct qrc_dev *dev) 185 { 186 struct qrcuart *qrc = qrc_get_data(dev); 187 188 kfifo_reset(&qrc->qrc_rx_fifo); 189 } 190 191 static enum qrcdev_tx qrcuart_xmit(const char __user *buf, 192 size_t data_length, struct qrc_dev *dev) 193 { 194 struct qrcuart *qrc = qrc_get_data(dev); 195 struct qrc_device_stats *n_stats = &dev->stats; 196 size_t written; 197 u8 *pos; 198 199 WARN_ON(qrc->tx_left); 200 201 pos = qrc->tx_buffer + qrc->tx_left; 202 if ((data_length + qrc->tx_left) > QRC_TX_BUFF_SIZE) { > 203 dev_err(dev->dev, "qrcuart transmit date overflow %ld\n", data_length); 204 return __QRCDEV_TX_MIN; 205 } 206 207 if (copy_from_user(pos, buf, data_length)) 208 return __QRCDEV_TX_MIN; 209 210 pos += data_length; 211 212 spin_lock(&qrc->lock); 213 214 written = serdev_device_write_buf(qrc->serdev, qrc->tx_buffer, 215 pos - qrc->tx_buffer); 216 if (written > 0) { 217 qrc->tx_left = (pos - qrc->tx_buffer) - written; 218 qrc->tx_head = qrc->tx_buffer + written; 219 n_stats->tx_bytes += written; 220 } 221 222 spin_unlock(&qrc->lock); 223 224 return QRCDEV_TX_OK; 225 } 226
Hi Canfeng, kernel test robot noticed the following build warnings: [auto build test WARNING on 805d849d7c3cc1f38efefd48b2480d62b7b5dcb7] url: https://github.com/intel-lab-lkp/linux/commits/Canfeng-Zhuang/misc-qualcomm-QRC-driver-for-Robotic-SDK-MCU/20240304-005601 base: 805d849d7c3cc1f38efefd48b2480d62b7b5dcb7 patch link: https://lore.kernel.org/r/20240304-qcom_qrc-v1-1-2a709f95fd61%40quicinc.com patch subject: [PATCH 1/2] misc: qualcomm: QRC driver for Robotic SDK MCU config: sh-allmodconfig (https://download.01.org/0day-ci/archive/20240304/202403040745.kD48ZDkC-lkp@intel.com/config) compiler: sh4-linux-gcc (GCC) 13.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240304/202403040745.kD48ZDkC-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202403040745.kD48ZDkC-lkp@intel.com/ All warnings (new ones prefixed by >>): In file included from include/linux/device.h:15, from include/linux/serdev.h:9, from drivers/misc/qrc/qrc_uart.c:10: drivers/misc/qrc/qrc_uart.c: In function 'qrcuart_xmit': >> drivers/misc/qrc/qrc_uart.c:203:35: warning: format '%ld' expects argument of type 'long int', but argument 3 has type 'size_t' {aka 'unsigned int'} [-Wformat=] 203 | dev_err(dev->dev, "qrcuart transmit date overflow %ld\n", data_length); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~ include/linux/dev_printk.h:144:56: note: in expansion of macro 'dev_fmt' 144 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~ drivers/misc/qrc/qrc_uart.c:203:17: note: in expansion of macro 'dev_err' 203 | dev_err(dev->dev, "qrcuart transmit date overflow %ld\n", data_length); | ^~~~~~~ drivers/misc/qrc/qrc_uart.c:203:69: note: format string is defined here 203 | dev_err(dev->dev, "qrcuart transmit date overflow %ld\n", data_length); | ~~^ | | | long int | %d vim +203 drivers/misc/qrc/qrc_uart.c 190 191 static enum qrcdev_tx qrcuart_xmit(const char __user *buf, 192 size_t data_length, struct qrc_dev *dev) 193 { 194 struct qrcuart *qrc = qrc_get_data(dev); 195 struct qrc_device_stats *n_stats = &dev->stats; 196 size_t written; 197 u8 *pos; 198 199 WARN_ON(qrc->tx_left); 200 201 pos = qrc->tx_buffer + qrc->tx_left; 202 if ((data_length + qrc->tx_left) > QRC_TX_BUFF_SIZE) { > 203 dev_err(dev->dev, "qrcuart transmit date overflow %ld\n", data_length); 204 return __QRCDEV_TX_MIN; 205 } 206 207 if (copy_from_user(pos, buf, data_length)) 208 return __QRCDEV_TX_MIN; 209 210 pos += data_length; 211 212 spin_lock(&qrc->lock); 213 214 written = serdev_device_write_buf(qrc->serdev, qrc->tx_buffer, 215 pos - qrc->tx_buffer); 216 if (written > 0) { 217 qrc->tx_left = (pos - qrc->tx_buffer) - written; 218 qrc->tx_head = qrc->tx_buffer + written; 219 n_stats->tx_bytes += written; 220 } 221 222 spin_unlock(&qrc->lock); 223 224 return QRCDEV_TX_OK; 225 } 226
Hi Canfeng, kernel test robot noticed the following build errors: [auto build test ERROR on 805d849d7c3cc1f38efefd48b2480d62b7b5dcb7] url: https://github.com/intel-lab-lkp/linux/commits/Canfeng-Zhuang/misc-qualcomm-QRC-driver-for-Robotic-SDK-MCU/20240304-005601 base: 805d849d7c3cc1f38efefd48b2480d62b7b5dcb7 patch link: https://lore.kernel.org/r/20240304-qcom_qrc-v1-1-2a709f95fd61%40quicinc.com patch subject: [PATCH 1/2] misc: qualcomm: QRC driver for Robotic SDK MCU config: s390-allyesconfig (https://download.01.org/0day-ci/archive/20240304/202403040831.fk22ur0f-lkp@intel.com/config) compiler: s390-linux-gcc (GCC) 13.2.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240304/202403040831.fk22ur0f-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202403040831.fk22ur0f-lkp@intel.com/ All errors (new ones prefixed by >>): >> drivers/misc/qrc/qrc_uart.c:83:24: error: initialization of 'ssize_t (*)(struct serdev_device *, const u8 *, size_t)' {aka 'long int (*)(struct serdev_device *, const unsigned char *, long unsigned int)'} from incompatible pointer type 'int (*)(struct serdev_device *, const unsigned char *, size_t)' {aka 'int (*)(struct serdev_device *, const unsigned char *, long unsigned int)'} [-Werror=incompatible-pointer-types] 83 | .receive_buf = qrc_uart_receive, | ^~~~~~~~~~~~~~~~ drivers/misc/qrc/qrc_uart.c:83:24: note: (near initialization for 'qrc_serdev_ops.receive_buf') cc1: some warnings being treated as errors vim +83 drivers/misc/qrc/qrc_uart.c 81 82 static struct serdev_device_ops qrc_serdev_ops = { > 83 .receive_buf = qrc_uart_receive, 84 .write_wakeup = qrc_uart_wakeup, 85 }; 86
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 4fb291f0bf7c..a43108af6fde 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -591,4 +591,5 @@ source "drivers/misc/cardreader/Kconfig" source "drivers/misc/uacce/Kconfig" source "drivers/misc/pvpanic/Kconfig" source "drivers/misc/mchp_pci1xxxx/Kconfig" +source "drivers/misc/qrc/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index ea6ea5bbbc9c..ab3b2c4d99fa 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -68,3 +68,4 @@ obj-$(CONFIG_TMR_INJECT) += xilinx_tmr_inject.o obj-$(CONFIG_TPS6594_ESM) += tps6594-esm.o obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o obj-$(CONFIG_NSM) += nsm.o +obj-$(CONFIG_QCOM_QRC) += qrc/ diff --git a/drivers/misc/qrc/Kconfig b/drivers/misc/qrc/Kconfig new file mode 100644 index 000000000000..994985d7c320 --- /dev/null +++ b/drivers/misc/qrc/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# QRC device driver configuration +# + +menu "QCOM QRC device driver" + +config QCOM_QRC + tristate "QCOM QRC device driver for Robotic SDK MCU" + help + This kernel configuration is used to enable robotic controller + device driver. Say M here if you want to enable robotic + controller device driver. + When in doubt, say N. + +endmenu diff --git a/drivers/misc/qrc/Makefile b/drivers/misc/qrc/Makefile new file mode 100644 index 000000000000..da2cf81f3c59 --- /dev/null +++ b/drivers/misc/qrc/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the QRC bus specific drivers. + + +obj-$(CONFIG_QCOM_QRC) += qrc_core.o qrc_uart.o diff --git a/drivers/misc/qrc/qrc_core.c b/drivers/misc/qrc/qrc_core.c new file mode 100644 index 000000000000..5cedb050dac4 --- /dev/null +++ b/drivers/misc/qrc/qrc_core.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* driver/misc/qrc/qrc_core.c + * + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. 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 <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/gpio.h> +#include <linux/delay.h> + +#include "qrc_core.h" + +#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) + 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) + qrc->qrc_ops->qrcops_close(qrc); + + return 0; +} + +/* GPIO control */ +static int +qrc_control_gpio_init(struct qrc_dev *qdev, struct device_node *node) +{ + int ret; + struct gpio_desc *qrc_boot0_gpiod; + struct gpio_desc *qrc_reset_gpiod; + + /* QRC BOOT0 GPIO */ + qdev->qrc_boot0_gpio = of_get_named_gpio(node, + "qcom,qrc-boot-gpio", 0); + if (qdev->qrc_boot0_gpio < 0) + dev_err(qdev->dev, "qrc_boot0_gpio is not available\n"); + + /* UART RESET GPIO */ + qdev->qrc_reset_gpio = of_get_named_gpio(node, + "qcom,qrc-reset-gpio", 0); + if (qdev->qrc_reset_gpio < 0) + dev_err(qdev->dev, "qrc_reset_gpio is not available\n"); + + if (gpio_is_valid(qdev->qrc_boot0_gpio)) { + ret = gpio_request(qdev->qrc_boot0_gpio, "QRC_BOOT0_GPIO"); + if (ret) { + dev_err(qdev->dev, "gpio request qrc_boot0_gpio failed for:%d\n", + qdev->qrc_boot0_gpio); + return ret; + } + + ret = gpio_direction_output(qdev->qrc_boot0_gpio, 1); + qrc_boot0_gpiod = gpio_to_desc(qdev->qrc_boot0_gpio); + ret += gpiod_export(qrc_boot0_gpiod, 0); + if (ret) { + dev_err(qdev->dev, "Unable to configure GPIO%d (BOOT0)\n", + qdev->qrc_boot0_gpio); + ret = -EBUSY; + gpio_free(qdev->qrc_boot0_gpio); + return ret; + } + + /* default config gpio status.boot=1 */ + gpio_set_value(qdev->qrc_boot0_gpio, 1); + } + + if (gpio_is_valid(qdev->qrc_reset_gpio)) { + ret = gpio_request(qdev->qrc_reset_gpio, "QRC_RESET_GPIO"); + if (ret) { + dev_err(qdev->dev, "gpio request qrc_reset_gpio failed for:%d\n", + qdev->qrc_reset_gpio); + return ret; + } + + ret = gpio_direction_output(qdev->qrc_reset_gpio, 0); + qrc_reset_gpiod = gpio_to_desc(qdev->qrc_reset_gpio); + ret += gpiod_export(qrc_reset_gpiod, 0); + + if (ret) { + dev_err(qdev->dev, "Unable to configure GPIO%d (RESET)\n", + qdev->qrc_reset_gpio); + ret = -EBUSY; + gpio_free(qdev->qrc_reset_gpio); + return ret; + } + + /* default config gpio status.reset=0 */ + gpio_set_value(qdev->qrc_reset_gpio, 0); + } + + return 0; +} + +static void +qrc_control_gpio_uninit(struct qrc_dev *qdev) +{ + if (gpio_is_valid(qdev->qrc_boot0_gpio)) + gpio_free(qdev->qrc_boot0_gpio); + + if (gpio_is_valid(qdev->qrc_reset_gpio)) + gpio_free(qdev->qrc_reset_gpio); +} + +static void qrc_gpio_reboot(struct qrc_dev *qdev) +{ + gpio_set_value(qdev->qrc_reset_gpio, 1); + msleep(100); + gpio_set_value(qdev->qrc_reset_gpio, 0); +} + +static long qrc_cdev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct qrc_dev *qdev; + void __user *argp = (void __user *)arg; + unsigned int readable_size; + + qdev = filp->private_data; + switch (cmd) { + case QRC_FIFO_CLEAR: + mutex_lock(&qdev->mutex); + qdev->qrc_ops->qrcops_data_clean(qdev); + mutex_unlock(&qdev->mutex); + return 0; + case QRC_REBOOT: + if (gpio_is_valid(qdev->qrc_reset_gpio)) { + qrc_gpio_reboot(qdev); + return 0; + } else { + return -EFAULT; + } + case QRC_BOOT_TO_MEM: + if (gpio_is_valid(qdev->qrc_boot0_gpio)) { + gpio_set_value(qdev->qrc_boot0_gpio, 1); + qrc_gpio_reboot(qdev); + return 0; + } else { + return -EFAULT; + } + case QRC_BOOT_TO_FLASH: + if (gpio_is_valid(qdev->qrc_boot0_gpio)) { + gpio_set_value(qdev->qrc_boot0_gpio, 0); + qrc_gpio_reboot(qdev); + return 0; + } else { + return -EFAULT; + } + case QRC_FIONREAD: + readable_size = qdev->qrc_ops->qrcops_data_status(qdev); + if (copy_to_user(argp, &readable_size, sizeof(unsigned int))) { + dev_err(qdev->dev, "copy_to_user failed\n"); + return -EFAULT; + } + return 0; + default: + return -EINVAL; + } +} + +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; + DECLARE_WAITQUEUE(wait, current); + + qrc = filp->private_data; + + 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 remove; + } + + mutex_lock(&qrc->mutex); + } + + ret = qrc->qrc_ops->qrcops_receive(qrc, buf, count); + +out: + mutex_unlock(&qrc->mutex); +remove: + 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; + 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("qrc_class"); + if (IS_ERR(qrc_class)) { + dev_err(dev, "failed to allocate class\n"); + return PTR_ERR(qrc_class); + } + ret = alloc_chrdev_region(&qrc_devt, 0, 1, "qrc"); + if (ret < 0) { + dev_err(dev, "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 = qrc_control_gpio_init(qdev, dev->of_node); + + ret = cdev_add(&qdev->cdev, devt, 1); + if (ret) { + dev_err(dev, "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; + } + + return 0; + +del_cdev: + cdev_del(&qdev->cdev); + return ret; +} +EXPORT_SYMBOL_GPL(qrc_register_device); + +void qrc_unregister(struct qrc_dev *qdev) +{ + device_destroy(qrc_class, qdev->dev->devt); + qrc_control_gpio_uninit(qdev); + dev_err(qdev->dev, "qrc drv unregistered\n"); +} +EXPORT_SYMBOL_GPL(qrc_unregister); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. QRC Uart Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/qrc/qrc_core.h b/drivers/misc/qrc/qrc_core.h new file mode 100644 index 000000000000..398343e43cfa --- /dev/null +++ b/drivers/misc/qrc/qrc_core.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* driver/misc/qrc/qrc_core.h + * + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. 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> +#include <linux/ioctl.h> + +#define QRC_NAME_SIZE 30 +#define QRC_INTERFACE_SIZE 30 +#define QRC_FIFO_SIZE 0x1000 + +struct qrc_dev; + +/* IOCTL commands */ +#define QRC_IOC_MAGIC 'q' + +/* Clear read fifo */ +#define QRC_FIFO_CLEAR _IO(QRC_IOC_MAGIC, 1) +/* Reboot QRC controller */ +#define QRC_REBOOT _IO(QRC_IOC_MAGIC, 2) +/* QRC boot from memory */ +#define QRC_BOOT_TO_MEM _IO(QRC_IOC_MAGIC, 3) +/* QRC boot from flash */ +#define QRC_BOOT_TO_FLASH _IO(QRC_IOC_MAGIC, 4) +/* QRC get read buffer size */ +#define QRC_FIONREAD _IO(QRC_IOC_MAGIC, 5) + +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); + void (*qrcops_data_clean)(struct qrc_dev *dev); +}; + +/* qrc char device */ +struct qrc_dev { + struct qrc_device_stats stats; + struct qrc_device_ops *qrc_ops; /* qrc dev ops */ + struct mutex mutex; /* protect kfifo access */ + 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; + int qrc_boot0_gpio; + int qrc_reset_gpio; +}; + +/** + * 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. + * @is_open: Flag shows if the device is open. + * This structure is used to define robotic controller uart device. + */ +struct qrcuart { + struct qrc_dev *qrc_dev; + spinlock_t lock; /* spinlock for transmitting 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; + bool is_open; +}; + +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 + diff --git a/drivers/misc/qrc/qrc_uart.c b/drivers/misc/qrc/qrc_uart.c new file mode 100644 index 000000000000..74143d88c967 --- /dev/null +++ b/drivers/misc/qrc/qrc_uart.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* driver/misc/qrc/qrc_uart.c + * + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/serdev.h> +#include <linux/init.h> +#include <linux/of.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) +{ + struct qrcuart *qrc = qrc_get_data(dev); + struct serdev_device *serdev = qrc->serdev; + int ret; + + if (!qrc->is_open) { + ret = serdev_device_open(serdev); + if (ret) { + dev_err(dev->dev, "qrcuart :Unable to open device\n"); + return ret; + } + serdev_device_set_baudrate(serdev, 115200); + serdev_device_set_flow_control(serdev, false); + qrc->is_open = true; + } + + return 0; +} + +static int qrcuart_close(struct qrc_dev *dev) +{ + struct qrcuart *qrc = qrc_get_data(dev); + struct serdev_device *serdev = qrc->serdev; + + flush_work(&qrc->tx_work); + spin_lock_bh(&qrc->lock); + qrc->tx_left = 0; + spin_unlock_bh(&qrc->lock); + + if (qrc->is_open) { + serdev_device_close(serdev); + qrc->is_open = false; + } + + 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 void qrcuart_data_clean(struct qrc_dev *dev) +{ + struct qrcuart *qrc = qrc_get_data(dev); + + kfifo_reset(&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) { + dev_err(dev->dev, "qrcuart transmit date overflow %ld\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 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, + .qrcops_data_clean = qrcuart_data_clean, +}; + +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; + int ret = 0; + + qrc = kmalloc(sizeof(*qrc), GFP_KERNEL); + if (!qrc) + return -ENOMEM; + qdev = kmalloc(sizeof(*qdev), GFP_KERNEL); + if (!qdev) { + kfree(qrc); + return -ENOMEM; + } + qdev->dev = &serdev->dev; + 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) { + dev_err(qdev->dev, "qrcuart: Fail to init qrc structure\n"); + kfree(qdev); + kfree(qrc); + return ret; + } + serdev_device_set_drvdata(serdev, qrc); + serdev_device_set_client_ops(serdev, &qrc_serdev_ops); + + ret = serdev_device_open(serdev); + if (ret) { + dev_err(qdev->dev, "qrcuart :Unable to open device\n"); + goto free; + } + serdev_device_close(serdev); + qrc->is_open = false; + + ret = qrc_register_device(qdev, &serdev->dev); + + if (ret) { + dev_err(qdev->dev, "qrcuart: Unable to register qrc device\n"); + cancel_work_sync(&qrc->tx_work); + goto free; + } + + return 0; + +free: + qrcuart_uninit(qdev); + kfree(qdev); + kfree(qrc); + return ret; +} + +static void qrc_uart_remove(struct serdev_device *serdev) +{ + struct qrcuart *qrc = serdev_device_get_drvdata(serdev); + + if (qrc->is_open) + 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_uart_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");
QRC Driver support functions: - Read data from serial device port. - Write data to serial device port. - Pin control reset robotic controller. Signed-off-by: Canfeng Zhuang <quic_czhuang@quicinc.com> --- drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/qrc/Kconfig | 16 ++ drivers/misc/qrc/Makefile | 6 + drivers/misc/qrc/qrc_core.c | 336 ++++++++++++++++++++++++++++++++++++++++++ drivers/misc/qrc/qrc_core.h | 143 ++++++++++++++++++ drivers/misc/qrc/qrc_uart.c | 345 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 848 insertions(+)