input: keyboard: imx_sc_pwrkey: add PWRON key driver
Add PWRON key driver which is based on scfw. Signed-off-by: Robin Gong <yibin.gong@nxp.com>5.4-rM2-2.2.x-imx-squashed
parent
127f1fb1f2
commit
dddcaeb960
|
@ -0,0 +1,22 @@
|
|||
Device-Tree bindings for input/keyboard/imx_sc_pwrkey.c poweron/off driver
|
||||
over SCU. On i.mx8QM/QXP poweron/off key is connected on SCU side, so need
|
||||
to get key event by MU.
|
||||
|
||||
Required properties:
|
||||
- compatible = "fsl,imx8-pwrkey";
|
||||
|
||||
Each button/key looked as the sub node:
|
||||
Required properties:
|
||||
- linux,code: the key value defined in
|
||||
include/dt-bindings/input/input.h
|
||||
Optional property:
|
||||
- wakeup-source: wakeup feature, the keys can wakeup from
|
||||
suspend if the keys with this property pressed.
|
||||
|
||||
Example nodes:
|
||||
sc_pwrkey: sc-powerkey {
|
||||
compatible = "fsl,imx8-pwrkey";
|
||||
linux,keycode = <KEY_POWER>;
|
||||
wakeup-source;
|
||||
};
|
||||
|
|
@ -470,6 +470,13 @@ config KEYBOARD_SNVS_PWRKEY
|
|||
To compile this driver as a module, choose M here; the
|
||||
module will be called snvs_pwrkey.
|
||||
|
||||
config KEYBOARD_IMX_SC_PWRKEY
|
||||
tristate "IMX SC Power Key Driver"
|
||||
depends on IMX_SCU
|
||||
help
|
||||
This is the virtual snvs powerkey driver for NXP i.mx8Q/QXP family
|
||||
whose SCU hold snvs inside.
|
||||
|
||||
config KEYBOARD_IMX
|
||||
tristate "IMX keypad support"
|
||||
depends on ARCH_MXC
|
||||
|
|
|
@ -58,6 +58,7 @@ 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
|
||||
obj-$(CONFIG_KEYBOARD_IMX_SC_PWRKEY) += imx_sc_pwrkey.o
|
||||
obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
|
||||
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Driver for the IMX SNVS ON/OFF Power Key over sc api
|
||||
* Copyright (C) 2017 NXP. All Rights Reserved.
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/firmware/imx/sci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define DEBOUNCE_TIME 100
|
||||
#define REPEAT_INTERVAL 60
|
||||
|
||||
#define SC_IRQ_BUTTON (1U << 0U) /* Button interrupt */
|
||||
#define SC_IRQ_GROUP_WAKE 3U /* Wakeup interrupts */
|
||||
|
||||
#define IMX_SC_MISC_FUNC_GET_BUTTON_STATUS 18U
|
||||
|
||||
struct pwrkey_drv_data {
|
||||
int keycode;
|
||||
bool keystate; /* 1: pressed, 0: release */
|
||||
bool delay_check;
|
||||
struct imx_sc_ipc *ipcHandle;
|
||||
int wakeup;
|
||||
struct delayed_work check_work;
|
||||
struct input_dev *input;
|
||||
};
|
||||
|
||||
struct imx_sc_msg_pwrkey {
|
||||
struct imx_sc_rpc_msg hdr;
|
||||
u8 state;
|
||||
};
|
||||
|
||||
static struct pwrkey_drv_data *pdata;
|
||||
|
||||
static int imx_sc_pwrkey_notify(struct notifier_block *nb,
|
||||
unsigned long event, void *group)
|
||||
{
|
||||
/* ignore other irqs */
|
||||
if (!(pdata && pdata->ipcHandle && (event & SC_IRQ_BUTTON) &&
|
||||
(*(u8 *)group == SC_IRQ_GROUP_WAKE)))
|
||||
return 0;
|
||||
|
||||
if (!pdata->delay_check) {
|
||||
pdata->delay_check = 1;
|
||||
schedule_delayed_work(&pdata->check_work,
|
||||
msecs_to_jiffies(REPEAT_INTERVAL));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void imx_sc_check_for_events(struct work_struct *work)
|
||||
{
|
||||
struct input_dev *input = pdata->input;
|
||||
struct imx_sc_msg_pwrkey msg;
|
||||
struct imx_sc_rpc_msg *hdr = &msg.hdr;
|
||||
bool state;
|
||||
int ret;
|
||||
|
||||
hdr->ver = IMX_SC_RPC_VERSION;
|
||||
hdr->svc = IMX_SC_RPC_SVC_MISC;
|
||||
hdr->func = IMX_SC_MISC_FUNC_GET_BUTTON_STATUS;
|
||||
hdr->size = 1;
|
||||
|
||||
ret = imx_scu_call_rpc(pdata->ipcHandle, &msg, false);
|
||||
if (ret) {
|
||||
dev_err(&input->dev, "read imx sc key failed, ret %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
state = msg.state;
|
||||
/*
|
||||
* restore status back if press interrupt received but pin's status
|
||||
* released, which caused by pressing so quickly.
|
||||
*/
|
||||
if (!state && !pdata->keystate)
|
||||
state = true;
|
||||
|
||||
if (state ^ pdata->keystate) {
|
||||
pm_wakeup_event(input->dev.parent, 0);
|
||||
pdata->keystate = !!state;
|
||||
input_event(input, EV_KEY, pdata->keycode, !!state);
|
||||
input_sync(input);
|
||||
if (!state)
|
||||
pdata->delay_check = 0;
|
||||
pm_relax(pdata->input->dev.parent);
|
||||
}
|
||||
/* repeat check if pressed long */
|
||||
if (state)
|
||||
schedule_delayed_work(&pdata->check_work,
|
||||
msecs_to_jiffies(DEBOUNCE_TIME));
|
||||
}
|
||||
|
||||
static struct notifier_block imx_sc_pwrkey_notifier = {
|
||||
.notifier_call = imx_sc_pwrkey_notify,
|
||||
};
|
||||
|
||||
static int imx_sc_pwrkey_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct input_dev *input = NULL;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int error;
|
||||
int ret;
|
||||
|
||||
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = imx_scu_get_handle(&pdata->ipcHandle);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
|
||||
pdata->keycode = KEY_POWER;
|
||||
dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&pdata->check_work, imx_sc_check_for_events);
|
||||
|
||||
pdata->wakeup = of_property_read_bool(np, "wakeup-source");
|
||||
|
||||
input = devm_input_allocate_device(&pdev->dev);
|
||||
|
||||
if (!input) {
|
||||
dev_err(&pdev->dev, "failed to allocate the input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input->name = pdev->name;
|
||||
input->phys = "imx-sc-pwrkey/input0";
|
||||
input->id.bustype = BUS_HOST;
|
||||
|
||||
input_set_capability(input, EV_KEY, pdata->keycode);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error < 0) {
|
||||
dev_err(&pdev->dev, "failed to register input device\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
pdata->input = input;
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
|
||||
device_init_wakeup(&pdev->dev, !!(pdata->wakeup));
|
||||
|
||||
|
||||
ret = imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE,
|
||||
SC_IRQ_BUTTON, true);
|
||||
if (ret) {
|
||||
dev_warn(&pdev->dev, "Enable irq failed.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = imx_scu_irq_register_notifier(&imx_sc_pwrkey_notifier);
|
||||
if (ret) {
|
||||
imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE,
|
||||
SC_IRQ_BUTTON, false);
|
||||
dev_warn(&pdev->dev, "reqister scu notifier failed.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id imx_sc_pwrkey_ids[] = {
|
||||
{ .compatible = "fsl,imx8-pwrkey" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_sc_pwrkey_ids);
|
||||
|
||||
static struct platform_driver imx_sc_pwrkey_driver = {
|
||||
.driver = {
|
||||
.name = "imx8-pwrkey",
|
||||
.of_match_table = imx_sc_pwrkey_ids,
|
||||
},
|
||||
.probe = imx_sc_pwrkey_probe,
|
||||
};
|
||||
module_platform_driver(imx_sc_pwrkey_driver);
|
||||
|
||||
MODULE_AUTHOR("Robin Gong <yibin.gong@nxp.com>");
|
||||
MODULE_DESCRIPTION("i.MX8 power key driver based on scu");
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Reference in New Issue