diff --git a/drivers/misc/qrc/qrc_core.c b/drivers/misc/qrc/qrc_core.c index b4ab1e56a90a..af570b207e0f 100644 --- a/drivers/misc/qrc/qrc_core.c +++ b/drivers/misc/qrc/qrc_core.c @@ -13,6 +13,10 @@ #include #include #include +#include +#include +#include +#include #include "qrc_core.h" @@ -54,20 +58,114 @@ static int qrc_cdev_release(struct inode *inode, struct file *filp) return 0; } +/* GPIO control */ +static int +qrc_control_gpio_init(struct qrc_dev *qdev, struct device_node *node) +{ + int ret; + + /* QRC BOOT0 GPIO */ + qdev->qrc_boot0_gpio = of_get_named_gpio(node, + "qcom,qrc-boot-gpio", 0); + if (qdev->qrc_boot0_gpio < 0) + pr_err("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) + pr_err("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 (unlikely(ret)) { + pr_err("gpio request qrc_boot0_gpio failed for:%d\n", + qdev->qrc_boot0_gpio); + return ret; + } + } + + if (gpio_is_valid(qdev->qrc_reset_gpio)) { + ret = gpio_request(qdev->qrc_reset_gpio, "QRC_RESET_GPIO"); + if (unlikely(ret)) { + pr_err("gpio request qrc_reset_gpio failed for:%d\n", + qdev->qrc_reset_gpio); + return ret; + } + } + + ret = gpio_direction_output(qdev->qrc_reset_gpio, 0); + ret += gpio_export(qdev->qrc_reset_gpio, 0); + + if (ret) { + pr_err("Unable to configure GPIO%d (RESET)\n", + qdev->qrc_reset_gpio); + ret = -EBUSY; + gpio_free(qdev->qrc_reset_gpio); + return ret; + } + + ret = gpio_direction_output(qdev->qrc_boot0_gpio, 1); + ret += gpio_export(qdev->qrc_boot0_gpio, 0); + if (ret) { + pr_err("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,reset=0 */ + gpio_set_value(qdev->qrc_boot0_gpio, 1); + gpio_set_value(qdev->qrc_reset_gpio, 0); + + return 0; +} + +static void +qrc_control_gpio_uninit(struct qrc_dev *qdev) +{ + gpio_free(qdev->qrc_boot0_gpio); + gpio_free(qdev->qrc_reset_gpio); +} + +static void qrc_gpio_reboot(struct qrc_dev *qdev) +{ + gpio_set_value(qdev->qrc_reset_gpio, 0); + msleep(100); + gpio_set_value(qdev->qrc_reset_gpio, 1); +} static long qrc_cdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct qrc_dev *qrc; + struct qrc_dev *qdev; - qrc = filp->private_data; + qdev = filp->private_data; switch (cmd) { - case FIFO_CLEAR: - mutex_lock(&qrc->mutex); - //check kfifo if have data - mutex_unlock(&qrc->mutex); - - break; - + 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; default: return -EINVAL; } @@ -186,6 +284,7 @@ int qrc_register_device(struct qrc_dev *qdev, struct device *dev) 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) { @@ -211,5 +310,6 @@ int qrc_register_device(struct qrc_dev *qdev, struct device *dev) void qrc_unregister(struct qrc_dev *qdev) { device_destroy(qrc_class, qdev->dev->devt); + qrc_control_gpio_uninit(qdev); dev_info(qdev->dev, "qrc drv unregistered\n"); } diff --git a/drivers/misc/qrc/qrc_core.h b/drivers/misc/qrc/qrc_core.h index 4dab9c12104b..0196e0ed2f29 100644 --- a/drivers/misc/qrc/qrc_core.h +++ b/drivers/misc/qrc/qrc_core.h @@ -14,6 +14,7 @@ #include #include #include +#include #define QRC_NAME_SIZE 30 #define QRC_INTERFACE_SIZE 30 @@ -21,6 +22,19 @@ 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) + + enum qrcdev_state_t { __STATE_IDLE, __STATE_READING, @@ -62,6 +76,7 @@ struct qrc_device_ops { 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 */ @@ -76,6 +91,8 @@ struct qrc_dev { struct cdev cdev; struct device *dev; void *data; + int qrc_boot0_gpio; + int qrc_reset_gpio; }; /** diff --git a/drivers/misc/qrc/qrc_uart.c b/drivers/misc/qrc/qrc_uart.c index 4a71a84b61f2..af55e8c6acf7 100644 --- a/drivers/misc/qrc/qrc_uart.c +++ b/drivers/misc/qrc/qrc_uart.c @@ -168,6 +168,14 @@ static int qrcuart_data_status(struct qrc_dev *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) { @@ -220,6 +228,7 @@ static const struct qrc_device_ops qrcuart_qrc_ops = { .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) @@ -269,6 +278,7 @@ static int qrc_uart_probe(struct serdev_device *serdev) 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);