1 // SPDX-License-Identifier: GPL-2.0+
2 //
3 // Driver for the IMX SNVS ON/OFF Power Key
4 // Copyright (C) 2015 Freescale Semiconductor, Inc. All Rights Reserved.
5 
6 #include <linux/device.h>
7 #include <linux/err.h>
8 #include <linux/init.h>
9 #include <linux/input.h>
10 #include <linux/interrupt.h>
11 #include <linux/io.h>
12 #include <linux/jiffies.h>
13 #include <linux/kernel.h>
14 #include <linux/module.h>
15 #include <linux/of.h>
16 #include <linux/of_address.h>
17 #include <linux/platform_device.h>
18 #include <linux/mfd/syscon.h>
19 #include <linux/regmap.h>
20 
21 #define SNVS_LPSR_REG	0x4C	/* LP Status Register */
22 #define SNVS_LPCR_REG	0x38	/* LP Control Register */
23 #define SNVS_HPSR_REG	0x14
24 #define SNVS_HPSR_BTN	BIT(6)
25 #define SNVS_LPSR_SPO	BIT(18)
26 #define SNVS_LPCR_DEP_EN BIT(5)
27 
28 #define DEBOUNCE_TIME 30
29 #define REPEAT_INTERVAL 60
30 
31 struct pwrkey_drv_data {
32 	struct regmap *snvs;
33 	int irq;
34 	int keycode;
35 	int keystate;  /* 1:pressed */
36 	int wakeup;
37 	struct timer_list check_timer;
38 	struct input_dev *input;
39 };
40 
41 static void imx_imx_snvs_check_for_events(struct timer_list *t)
42 {
43 	struct pwrkey_drv_data *pdata = from_timer(pdata, t, check_timer);
44 	struct input_dev *input = pdata->input;
45 	u32 state;
46 
47 	regmap_read(pdata->snvs, SNVS_HPSR_REG, &state);
48 	state = state & SNVS_HPSR_BTN ? 1 : 0;
49 
50 	/* only report new event if status changed */
51 	if (state ^ pdata->keystate) {
52 		pdata->keystate = state;
53 		input_event(input, EV_KEY, pdata->keycode, state);
54 		input_sync(input);
55 		pm_relax(pdata->input->dev.parent);
56 	}
57 
58 	/* repeat check if pressed long */
59 	if (state) {
60 		mod_timer(&pdata->check_timer,
61 			  jiffies + msecs_to_jiffies(REPEAT_INTERVAL));
62 	}
63 }
64 
65 static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
66 {
67 	struct platform_device *pdev = dev_id;
68 	struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
69 	u32 lp_status;
70 
71 	pm_wakeup_event(pdata->input->dev.parent, 0);
72 
73 	regmap_read(pdata->snvs, SNVS_LPSR_REG, &lp_status);
74 	if (lp_status & SNVS_LPSR_SPO)
75 		mod_timer(&pdata->check_timer, jiffies + msecs_to_jiffies(DEBOUNCE_TIME));
76 
77 	/* clear SPO status */
78 	regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
79 
80 	return IRQ_HANDLED;
81 }
82 
83 static void imx_snvs_pwrkey_act(void *pdata)
84 {
85 	struct pwrkey_drv_data *pd = pdata;
86 
87 	del_timer_sync(&pd->check_timer);
88 }
89 
90 static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
91 {
92 	struct pwrkey_drv_data *pdata = NULL;
93 	struct input_dev *input = NULL;
94 	struct device_node *np;
95 	int error;
96 
97 	/* Get SNVS register Page */
98 	np = pdev->dev.of_node;
99 	if (!np)
100 		return -ENODEV;
101 
102 	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
103 	if (!pdata)
104 		return -ENOMEM;
105 
106 	pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap");
107 	if (IS_ERR(pdata->snvs)) {
108 		dev_err(&pdev->dev, "Can't get snvs syscon\n");
109 		return PTR_ERR(pdata->snvs);
110 	}
111 
112 	if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
113 		pdata->keycode = KEY_POWER;
114 		dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
115 	}
116 
117 	pdata->wakeup = of_property_read_bool(np, "wakeup-source");
118 
119 	pdata->irq = platform_get_irq(pdev, 0);
120 	if (pdata->irq < 0) {
121 		dev_err(&pdev->dev, "no irq defined in platform data\n");
122 		return -EINVAL;
123 	}
124 
125 	regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_DEP_EN, SNVS_LPCR_DEP_EN);
126 
127 	/* clear the unexpected interrupt before driver ready */
128 	regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
129 
130 	timer_setup(&pdata->check_timer, imx_imx_snvs_check_for_events, 0);
131 
132 	input = devm_input_allocate_device(&pdev->dev);
133 	if (!input) {
134 		dev_err(&pdev->dev, "failed to allocate the input device\n");
135 		return -ENOMEM;
136 	}
137 
138 	input->name = pdev->name;
139 	input->phys = "snvs-pwrkey/input0";
140 	input->id.bustype = BUS_HOST;
141 
142 	input_set_capability(input, EV_KEY, pdata->keycode);
143 
144 	/* input customer action to cancel release timer */
145 	error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
146 	if (error) {
147 		dev_err(&pdev->dev, "failed to register remove action\n");
148 		return error;
149 	}
150 
151 	error = devm_request_irq(&pdev->dev, pdata->irq,
152 			       imx_snvs_pwrkey_interrupt,
153 			       0, pdev->name, pdev);
154 
155 	if (error) {
156 		dev_err(&pdev->dev, "interrupt not available.\n");
157 		return error;
158 	}
159 
160 	error = input_register_device(input);
161 	if (error < 0) {
162 		dev_err(&pdev->dev, "failed to register input device\n");
163 		return error;
164 	}
165 
166 	pdata->input = input;
167 	platform_set_drvdata(pdev, pdata);
168 
169 	device_init_wakeup(&pdev->dev, pdata->wakeup);
170 
171 	return 0;
172 }
173 
174 static int __maybe_unused imx_snvs_pwrkey_suspend(struct device *dev)
175 {
176 	struct platform_device *pdev = to_platform_device(dev);
177 	struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
178 
179 	if (device_may_wakeup(&pdev->dev))
180 		enable_irq_wake(pdata->irq);
181 
182 	return 0;
183 }
184 
185 static int __maybe_unused imx_snvs_pwrkey_resume(struct device *dev)
186 {
187 	struct platform_device *pdev = to_platform_device(dev);
188 	struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
189 
190 	if (device_may_wakeup(&pdev->dev))
191 		disable_irq_wake(pdata->irq);
192 
193 	return 0;
194 }
195 
196 static const struct of_device_id imx_snvs_pwrkey_ids[] = {
197 	{ .compatible = "fsl,sec-v4.0-pwrkey" },
198 	{ /* sentinel */ }
199 };
200 MODULE_DEVICE_TABLE(of, imx_snvs_pwrkey_ids);
201 
202 static SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops, imx_snvs_pwrkey_suspend,
203 				imx_snvs_pwrkey_resume);
204 
205 static struct platform_driver imx_snvs_pwrkey_driver = {
206 	.driver = {
207 		.name = "snvs_pwrkey",
208 		.pm     = &imx_snvs_pwrkey_pm_ops,
209 		.of_match_table = imx_snvs_pwrkey_ids,
210 	},
211 	.probe = imx_snvs_pwrkey_probe,
212 };
213 module_platform_driver(imx_snvs_pwrkey_driver);
214 
215 MODULE_AUTHOR("Freescale Semiconductor");
216 MODULE_DESCRIPTION("i.MX snvs power key Driver");
217 MODULE_LICENSE("GPL");
218