File Index
Files and directories
Sign in
FILE

imx-sm-bbm-key.c

File preview with syntax highlighting.

linux-6.19.14/drivers/input/keyboard/imx-sm-bbm-key.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright 2024 NXP.
  4 */
  5
  6#include <linux/input.h>
  7#include <linux/jiffies.h>
  8#include <linux/module.h>
  9#include <linux/of.h>
 10#include <linux/platform_device.h>
 11#include <linux/rtc.h>
 12#include <linux/scmi_protocol.h>
 13#include <linux/scmi_imx_protocol.h>
 14#include <linux/suspend.h>
 15
 16#define DEBOUNCE_TIME		30
 17#define REPEAT_INTERVAL		60
 18
 19struct scmi_imx_bbm {
 20	struct scmi_protocol_handle *ph;
 21	const struct scmi_imx_bbm_proto_ops *ops;
 22	struct notifier_block nb;
 23	int keycode;
 24	int keystate;  /* 1:pressed */
 25	bool suspended;
 26	struct delayed_work check_work;
 27	struct input_dev *input;
 28};
 29
 30static void scmi_imx_bbm_pwrkey_check_for_events(struct work_struct *work)
 31{
 32	struct scmi_imx_bbm *bbnsm = container_of(to_delayed_work(work),
 33						  struct scmi_imx_bbm, check_work);
 34	struct scmi_protocol_handle *ph = bbnsm->ph;
 35	struct input_dev *input = bbnsm->input;
 36	u32 state = 0;
 37	int ret;
 38
 39	ret = bbnsm->ops->button_get(ph, &state);
 40	if (ret) {
 41		pr_err("%s: %d\n", __func__, ret);
 42		return;
 43	}
 44
 45	pr_debug("%s: state: %d, keystate %d\n", __func__, state, bbnsm->keystate);
 46
 47	/* only report new event if status changed */
 48	if (state ^ bbnsm->keystate) {
 49		bbnsm->keystate = state;
 50		input_event(input, EV_KEY, bbnsm->keycode, state);
 51		input_sync(input);
 52		pm_relax(bbnsm->input->dev.parent);
 53		pr_debug("EV_KEY: %x\n", bbnsm->keycode);
 54	}
 55
 56	/* repeat check if pressed long */
 57	if (state)
 58		schedule_delayed_work(&bbnsm->check_work, msecs_to_jiffies(REPEAT_INTERVAL));
 59}
 60
 61static int scmi_imx_bbm_pwrkey_event(struct scmi_imx_bbm *bbnsm)
 62{
 63	struct input_dev *input = bbnsm->input;
 64
 65	pm_wakeup_event(input->dev.parent, 0);
 66
 67	/*
 68	 * Directly report key event after resume to make no key press
 69	 * event is missed.
 70	 */
 71	if (READ_ONCE(bbnsm->suspended)) {
 72		bbnsm->keystate = 1;
 73		input_event(input, EV_KEY, bbnsm->keycode, 1);
 74		input_sync(input);
 75		WRITE_ONCE(bbnsm->suspended, false);
 76	}
 77
 78	schedule_delayed_work(&bbnsm->check_work, msecs_to_jiffies(DEBOUNCE_TIME));
 79
 80	return 0;
 81}
 82
 83static void scmi_imx_bbm_pwrkey_act(void *pdata)
 84{
 85	struct scmi_imx_bbm *bbnsm = pdata;
 86
 87	cancel_delayed_work_sync(&bbnsm->check_work);
 88}
 89
 90static int scmi_imx_bbm_key_notifier(struct notifier_block *nb, unsigned long event, void *data)
 91{
 92	struct scmi_imx_bbm *bbnsm = container_of(nb, struct scmi_imx_bbm, nb);
 93	struct scmi_imx_bbm_notif_report *r = data;
 94
 95	if (r->is_button) {
 96		pr_debug("BBM Button Power key pressed\n");
 97		scmi_imx_bbm_pwrkey_event(bbnsm);
 98	} else {
 99		/* Should never reach here */
100		pr_err("Unexpected BBM event: %s\n", __func__);
101	}
102
103	return 0;
104}
105
106static int scmi_imx_bbm_pwrkey_init(struct scmi_device *sdev)
107{
108	const struct scmi_handle *handle = sdev->handle;
109	struct device *dev = &sdev->dev;
110	struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev);
111	struct input_dev *input;
112	int ret;
113
114	if (device_property_read_u32(dev, "linux,code", &bbnsm->keycode)) {
115		bbnsm->keycode = KEY_POWER;
116		dev_warn(dev, "key code is not specified, using default KEY_POWER\n");
117	}
118
119	INIT_DELAYED_WORK(&bbnsm->check_work, scmi_imx_bbm_pwrkey_check_for_events);
120
121	input = devm_input_allocate_device(dev);
122	if (!input) {
123		dev_err(dev, "failed to allocate the input device for SCMI IMX BBM\n");
124		return -ENOMEM;
125	}
126
127	input->name = dev_name(dev);
128	input->phys = "bbnsm-pwrkey/input0";
129	input->id.bustype = BUS_HOST;
130
131	input_set_capability(input, EV_KEY, bbnsm->keycode);
132
133	ret = devm_add_action_or_reset(dev, scmi_imx_bbm_pwrkey_act, bbnsm);
134	if (ret) {
135		dev_err(dev, "failed to register remove action\n");
136		return ret;
137	}
138
139	bbnsm->input = input;
140
141	bbnsm->nb.notifier_call = &scmi_imx_bbm_key_notifier;
142	ret = handle->notify_ops->devm_event_notifier_register(sdev, SCMI_PROTOCOL_IMX_BBM,
143							       SCMI_EVENT_IMX_BBM_BUTTON,
144							       NULL, &bbnsm->nb);
145
146	if (ret)
147		dev_err(dev, "Failed to register BBM Button Events %d:", ret);
148
149	ret = input_register_device(input);
150	if (ret) {
151		dev_err(dev, "failed to register input device\n");
152		return ret;
153	}
154
155	return 0;
156}
157
158static int scmi_imx_bbm_key_probe(struct scmi_device *sdev)
159{
160	const struct scmi_handle *handle = sdev->handle;
161	struct device *dev = &sdev->dev;
162	struct scmi_protocol_handle *ph;
163	struct scmi_imx_bbm *bbnsm;
164	int ret;
165
166	if (!handle)
167		return -ENODEV;
168
169	bbnsm = devm_kzalloc(dev, sizeof(*bbnsm), GFP_KERNEL);
170	if (!bbnsm)
171		return -ENOMEM;
172
173	bbnsm->ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_BBM, &ph);
174	if (IS_ERR(bbnsm->ops))
175		return PTR_ERR(bbnsm->ops);
176
177	bbnsm->ph = ph;
178
179	device_init_wakeup(dev, true);
180
181	dev_set_drvdata(dev, bbnsm);
182
183	ret = scmi_imx_bbm_pwrkey_init(sdev);
184	if (ret)
185		device_init_wakeup(dev, false);
186
187	return ret;
188}
189
190static int __maybe_unused scmi_imx_bbm_key_suspend(struct device *dev)
191{
192	struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev);
193
194	WRITE_ONCE(bbnsm->suspended, true);
195
196	return 0;
197}
198
199static int __maybe_unused scmi_imx_bbm_key_resume(struct device *dev)
200{
201	return 0;
202}
203
204static SIMPLE_DEV_PM_OPS(scmi_imx_bbm_pm_key_ops, scmi_imx_bbm_key_suspend,
205			 scmi_imx_bbm_key_resume);
206
207static const struct scmi_device_id scmi_id_table[] = {
208	{ SCMI_PROTOCOL_IMX_BBM, "imx-bbm-key" },
209	{ },
210};
211MODULE_DEVICE_TABLE(scmi, scmi_id_table);
212
213static struct scmi_driver scmi_imx_bbm_key_driver = {
214	.driver = {
215		.pm = &scmi_imx_bbm_pm_key_ops,
216	},
217	.name = "scmi-imx-bbm-key",
218	.probe = scmi_imx_bbm_key_probe,
219	.id_table = scmi_id_table,
220};
221module_scmi_driver(scmi_imx_bbm_key_driver);
222
223MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>");
224MODULE_DESCRIPTION("IMX SM BBM Key driver");
225MODULE_LICENSE("GPL");
~/ Read-only file preview.