summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Gong <yibin.gong@nxp.com>2019-08-08 01:40:16 +0800
committerDong Aisheng <aisheng.dong@nxp.com>2019-11-25 16:29:02 +0800
commit127f1fb1f29755093a1ad389a6f29c814ee792c1 (patch)
tree8ae0daae8983106175d5c9c51139e7cc452e0c8d
parent219d54332a09e8d8741c1e1982f5eae56099de85 (diff)
input: keyboard: rpmsg-keys: add rpmsg-keys driver
Add rpmsg-keys driver for i.mx7ulp. Signed-off-by: Robin Gong <yibin.gong@nxp.com>
-rw-r--r--Documentation/devicetree/bindings/input/rpmsg-keys.txt33
-rw-r--r--drivers/input/keyboard/Kconfig11
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/rpmsg-keys.c310
4 files changed, 355 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/input/rpmsg-keys.txt b/Documentation/devicetree/bindings/input/rpmsg-keys.txt
new file mode 100644
index 000000000000..d9279802cc9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/rpmsg-keys.txt
@@ -0,0 +1,33 @@
+Device-Tree bindings for input/keyboard/rpmsg-keys.c keys driver over
+rpmsg. On i.mx7ULP keys are connected on M4 side, so rpmsg-keys driver
+needed to get the key status from M4 side by rpmsg.
+
+Required properties:
+ - compatible = "fsl,rpmsg-keys";
+
+Each button/key looked as the sub node:
+Required properties:
+ - label: the key name
+ - linux,code: the key value defined in
+ include/dt-bindings/input/input.h
+Optional property:
+ - rpmsg-key,wakeup: wakeup feature, the keys can wakeup from
+ suspend if the keys with this property pressed.
+
+Example nodes:
+ rpmsg_keys: rpmsg-keys {
+ compatible = "fsl,rpmsg-keys";
+
+ volume-up {
+ label = "Volume Up";
+ rpmsg-key,wakeup;
+ linux,code = <KEY_VOLUMEUP>;
+ };
+
+ volume-down {
+ label = "Volume Down";
+ rpmsg-key,wakeup;
+ linux,code = <KEY_VOLUMEDOWN>;
+ };
+ };
+
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 8911bc2ec42a..41e2e0dc3efb 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -448,6 +448,17 @@ config KEYBOARD_MPR121
To compile this driver as a module, choose M here: the
module will be called mpr121_touchkey.
+config KEYBOARD_RPMSG
+ tristate "i.MX Rpmsg Keys Driver"
+ depends on (SOC_IMX7ULP)
+ depends on RPMSG
+ depends on OF
+ help
+ This is rpmsg keys driver on i.mx7ulp, because some keys located
+ in M4 side.
+
+
+
config KEYBOARD_SNVS_PWRKEY
tristate "IMX SNVS Power Key Driver"
depends on ARCH_MXC || COMPILE_TEST
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 9510325c0c5d..e8aa02b4733c 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
obj-$(CONFIG_KEYBOARD_QT1050) += qt1050.o
obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
+obj-$(CONFIG_KEYBOARD_RPMSG) += rpmsg-keys.o
obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY) += snvs_pwrkey.o
diff --git a/drivers/input/keyboard/rpmsg-keys.c b/drivers/input/keyboard/rpmsg-keys.c
new file mode 100644
index 000000000000..f6012e87f9a6
--- /dev/null
+++ b/drivers/input/keyboard/rpmsg-keys.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2017 NXP
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/imx_rpmsg.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_qos.h>
+#include <linux/rpmsg.h>
+#include <linux/uaccess.h>
+#include <linux/virtio.h>
+
+#define RPMSG_TIMEOUT 1000
+
+enum key_cmd_type {
+ KEY_RPMSG_SETUP,
+ KEY_RPMSG_REPLY,
+ KEY_RPMSG_NOTIFY,
+};
+
+enum keys_type {
+ KEY_PRESS = 1,
+ KEY_RELEASE,
+ KEY_BOTH,
+};
+
+struct key_rpmsg_data {
+ struct imx_rpmsg_head header;
+ u8 key_index;
+ union {
+ u8 event;
+ u8 retcode;
+ };
+ u8 wakeup;
+} __attribute__((packed));
+
+struct rpmsg_keys_button {
+ unsigned int code;
+ enum keys_type type;
+ int wakeup;
+ struct input_dev *input;
+};
+
+struct rpmsg_keys_drvdata {
+ struct input_dev *input;
+ struct rpmsg_device *rpdev;
+ struct device *dev;
+ struct key_rpmsg_data *msg;
+ bool ack;
+ struct pm_qos_request pm_qos_req;
+ struct delayed_work keysetup_work;
+ struct completion cmd_complete;
+ int nbuttons;
+ struct rpmsg_keys_button buttons[0];
+};
+
+static struct rpmsg_keys_drvdata *keys_rpmsg;
+
+static int key_send_message(struct key_rpmsg_data *msg,
+ struct rpmsg_keys_drvdata *info, bool ack)
+{
+ int err;
+
+ if (!info->rpdev) {
+ dev_dbg(info->dev,
+ "rpmsg channel not ready, m4 image ready?\n");
+ return -EINVAL;
+ }
+
+ pm_qos_add_request(&info->pm_qos_req,
+ PM_QOS_CPU_DMA_LATENCY, 0);
+
+ if (ack) {
+ info->ack = true;
+ reinit_completion(&info->cmd_complete);
+ }
+
+ err = rpmsg_send(info->rpdev->ept, (void *)msg,
+ sizeof(struct key_rpmsg_data));
+ if (err) {
+ dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", err);
+ goto err_out;
+ }
+
+ if (ack) {
+ err = wait_for_completion_timeout(&info->cmd_complete,
+ msecs_to_jiffies(RPMSG_TIMEOUT));
+ if (!err) {
+ dev_err(&info->rpdev->dev, "rpmsg_send timeout!\n");
+ err = -ETIMEDOUT;
+ goto err_out;
+ }
+
+ if (info->msg->retcode != 0) {
+ dev_err(&info->rpdev->dev, "rpmsg not ack %d!\n",
+ info->msg->retcode);
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ err = 0;
+ }
+
+err_out:
+ info->ack = true;
+ pm_qos_remove_request(&info->pm_qos_req);
+
+ return err;
+}
+
+static int keys_rpmsg_cb(struct rpmsg_device *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct key_rpmsg_data *msg = (struct key_rpmsg_data *)data;
+
+ if (msg->header.type == KEY_RPMSG_REPLY) {
+ keys_rpmsg->msg = msg;
+ complete(&keys_rpmsg->cmd_complete);
+ return 0;
+ } else if (msg->header.type == KEY_RPMSG_NOTIFY) {
+ keys_rpmsg->msg = msg;
+ keys_rpmsg->ack = false;
+ } else
+ dev_err(&keys_rpmsg->rpdev->dev, "wrong command type!\n");
+
+ input_event(keys_rpmsg->input, EV_KEY, msg->key_index, msg->event);
+ input_sync(keys_rpmsg->input);
+
+ return 0;
+}
+
+static void keys_init_handler(struct work_struct *work)
+{
+ struct key_rpmsg_data msg;
+ int i;
+
+ /* setup keys */
+ for (i = 0; i < keys_rpmsg->nbuttons; i++) {
+ struct rpmsg_keys_button *button = &keys_rpmsg->buttons[i];
+
+ msg.header.cate = IMX_RPMSG_KEY;
+ msg.header.major = IMX_RMPSG_MAJOR;
+ msg.header.minor = IMX_RMPSG_MINOR;
+ msg.header.type = KEY_RPMSG_SETUP;
+ msg.header.cmd = 0;
+ msg.key_index = button->code;
+ msg.wakeup = button->wakeup;
+ msg.event = button->type;
+ if (key_send_message(&msg, keys_rpmsg, true))
+ dev_err(&keys_rpmsg->rpdev->dev,
+ "key %d setup failed!\n", button->code);
+ }
+}
+
+static int keys_rpmsg_probe(struct rpmsg_device *rpdev)
+{
+ keys_rpmsg->rpdev = rpdev;
+
+ dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
+ rpdev->src, rpdev->dst);
+
+ init_completion(&keys_rpmsg->cmd_complete);
+
+ INIT_DELAYED_WORK(&keys_rpmsg->keysetup_work,
+ keys_init_handler);
+ schedule_delayed_work(&keys_rpmsg->keysetup_work,
+ msecs_to_jiffies(100));
+
+ return 0;
+}
+
+static struct rpmsg_device_id keys_rpmsg_id_table[] = {
+ { .name = "rpmsg-keypad-channel" },
+ { },
+};
+
+static struct rpmsg_driver keys_rpmsg_driver = {
+ .drv.name = "key_rpmsg",
+ .drv.owner = THIS_MODULE,
+ .id_table = keys_rpmsg_id_table,
+ .probe = keys_rpmsg_probe,
+ .callback = keys_rpmsg_cb,
+};
+
+static struct rpmsg_keys_drvdata *
+rpmsg_keys_get_devtree_pdata(struct device *dev)
+{
+ struct device_node *node, *pp;
+ struct rpmsg_keys_drvdata *ddata;
+ struct rpmsg_keys_button *button;
+ int nbuttons;
+ int i;
+
+ node = dev->of_node;
+ if (!node)
+ return ERR_PTR(-ENODEV);
+
+ nbuttons = of_get_child_count(node);
+ if (nbuttons == 0)
+ return ERR_PTR(-ENODEV);
+
+ ddata = devm_kzalloc(dev,
+ sizeof(*ddata) + nbuttons *
+ sizeof(struct rpmsg_keys_button),
+ GFP_KERNEL);
+ if (!ddata)
+ return ERR_PTR(-ENOMEM);
+
+ ddata->nbuttons = nbuttons;
+
+ i = 0;
+ for_each_child_of_node(node, pp) {
+ button = &ddata->buttons[i++];
+
+ if (of_property_read_u32(pp, "linux,code", &button->code)) {
+ dev_err(dev, "Button without keycode: 0x%x\n",
+ button->code);
+ return ERR_PTR(-EINVAL);
+ }
+
+ button->wakeup = !!of_get_property(pp, "rpmsg-key,wakeup",
+ NULL);
+ button->type = KEY_BOTH;
+ }
+
+ return ddata;
+}
+
+static int rpmsg_keys_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rpmsg_keys_drvdata *ddata;
+ int i, error;
+ struct input_dev *input;
+
+ ddata = rpmsg_keys_get_devtree_pdata(dev);
+ if (IS_ERR(ddata))
+ return PTR_ERR(ddata);
+
+ input = devm_input_allocate_device(dev);
+ if (!input) {
+ dev_err(dev, "failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ ddata->input = input;
+
+ keys_rpmsg = ddata;
+ platform_set_drvdata(pdev, ddata);
+
+ input->name = pdev->name;
+ input->phys = "rpmsg-keys/input0";
+ input->dev.parent = &pdev->dev;
+
+ input->id.bustype = BUS_HOST;
+
+ for (i = 0; i < ddata->nbuttons; i++) {
+ struct rpmsg_keys_button *button = &ddata->buttons[i];
+
+ input_set_capability(input, EV_KEY, button->code);
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(dev, "Unable to register input device, error: %d\n",
+ error);
+ goto err_out;
+ }
+
+ return register_rpmsg_driver(&keys_rpmsg_driver);
+err_out:
+ return error;
+}
+
+static const struct of_device_id rpmsg_keys_of_match[] = {
+ { .compatible = "fsl,rpmsg-keys", },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, rpmsg_keys_of_match);
+
+static struct platform_driver rpmsg_keys_device_driver = {
+ .probe = rpmsg_keys_probe,
+ .driver = {
+ .name = "rpmsg-keys",
+ .of_match_table = of_match_ptr(rpmsg_keys_of_match)
+ }
+};
+
+module_platform_driver(rpmsg_keys_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robin Gong <yibin.gong@nxp.com>");
+MODULE_DESCRIPTION("Keyboard driver based on rpmsg");