blob: a6e76080af5f0effbb609055ea0e46ea3dca4dec [file] [log] [blame]
Binghua Duan9b5f9532013-06-02 23:38:46 -07001/*
2 * Power key driver for SiRF PrimaII
3 *
4 * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company.
5 *
6 * Licensed under GPLv2 or later.
7 */
8
9#include <linux/module.h>
Binghua Duan9b5f9532013-06-02 23:38:46 -070010#include <linux/interrupt.h>
11#include <linux/delay.h>
12#include <linux/platform_device.h>
13#include <linux/input.h>
14#include <linux/rtc/sirfsoc_rtciobrg.h>
15#include <linux/of.h>
Xianglong Dua1a75212014-02-15 12:30:58 -080016#include <linux/workqueue.h>
Binghua Duan9b5f9532013-06-02 23:38:46 -070017
18struct sirfsoc_pwrc_drvdata {
19 u32 pwrc_base;
20 struct input_dev *input;
Xianglong Dua1a75212014-02-15 12:30:58 -080021 struct delayed_work work;
Binghua Duan9b5f9532013-06-02 23:38:46 -070022};
23
24#define PWRC_ON_KEY_BIT (1 << 0)
25
26#define PWRC_INT_STATUS 0xc
27#define PWRC_INT_MASK 0x10
Xianglong Dua1a75212014-02-15 12:30:58 -080028#define PWRC_PIN_STATUS 0x14
29#define PWRC_KEY_DETECT_UP_TIME 20 /* ms*/
30
31static int sirfsoc_pwrc_is_on_key_down(struct sirfsoc_pwrc_drvdata *pwrcdrv)
32{
33 u32 state = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base +
34 PWRC_PIN_STATUS);
35 return !(state & PWRC_ON_KEY_BIT); /* ON_KEY is active low */
36}
37
38static void sirfsoc_pwrc_report_event(struct work_struct *work)
39{
40 struct sirfsoc_pwrc_drvdata *pwrcdrv =
41 container_of(work, struct sirfsoc_pwrc_drvdata, work.work);
42
43 if (sirfsoc_pwrc_is_on_key_down(pwrcdrv)) {
44 schedule_delayed_work(&pwrcdrv->work,
45 msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME));
46 } else {
47 input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 0);
48 input_sync(pwrcdrv->input);
49 }
50}
Binghua Duan9b5f9532013-06-02 23:38:46 -070051
52static irqreturn_t sirfsoc_pwrc_isr(int irq, void *dev_id)
53{
54 struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_id;
55 u32 int_status;
56
57 int_status = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base +
58 PWRC_INT_STATUS);
59 sirfsoc_rtc_iobrg_writel(int_status & ~PWRC_ON_KEY_BIT,
60 pwrcdrv->pwrc_base + PWRC_INT_STATUS);
61
Xianglong Dua1a75212014-02-15 12:30:58 -080062 input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 1);
Binghua Duan9b5f9532013-06-02 23:38:46 -070063 input_sync(pwrcdrv->input);
Xianglong Dua1a75212014-02-15 12:30:58 -080064 schedule_delayed_work(&pwrcdrv->work,
65 msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME));
Binghua Duan9b5f9532013-06-02 23:38:46 -070066
67 return IRQ_HANDLED;
68}
69
Dmitry Torokhov864d3c02014-02-13 23:38:43 -080070static void sirfsoc_pwrc_toggle_interrupts(struct sirfsoc_pwrc_drvdata *pwrcdrv,
71 bool enable)
72{
73 u32 int_mask;
74
75 int_mask = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + PWRC_INT_MASK);
76 if (enable)
77 int_mask |= PWRC_ON_KEY_BIT;
78 else
79 int_mask &= ~PWRC_ON_KEY_BIT;
80 sirfsoc_rtc_iobrg_writel(int_mask, pwrcdrv->pwrc_base + PWRC_INT_MASK);
81}
82
83static int sirfsoc_pwrc_open(struct input_dev *input)
84{
85 struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input);
86
87 sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true);
88
89 return 0;
90}
91
92static void sirfsoc_pwrc_close(struct input_dev *input)
93{
94 struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input);
95
96 sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false);
Xianglong Dua1a75212014-02-15 12:30:58 -080097 cancel_delayed_work_sync(&pwrcdrv->work);
Dmitry Torokhov864d3c02014-02-13 23:38:43 -080098}
99
Binghua Duan9b5f9532013-06-02 23:38:46 -0700100static const struct of_device_id sirfsoc_pwrc_of_match[] = {
101 { .compatible = "sirf,prima2-pwrc" },
102 {},
103}
104MODULE_DEVICE_TABLE(of, sirfsoc_pwrc_of_match);
105
106static int sirfsoc_pwrc_probe(struct platform_device *pdev)
107{
108 struct device_node *np = pdev->dev.of_node;
109 struct sirfsoc_pwrc_drvdata *pwrcdrv;
110 int irq;
111 int error;
112
113 pwrcdrv = devm_kzalloc(&pdev->dev, sizeof(struct sirfsoc_pwrc_drvdata),
114 GFP_KERNEL);
115 if (!pwrcdrv) {
116 dev_info(&pdev->dev, "Not enough memory for the device data\n");
117 return -ENOMEM;
118 }
119
120 /*
Dmitry Torokhov864d3c02014-02-13 23:38:43 -0800121 * We can't use of_iomap because pwrc is not mapped in memory,
Binghua Duan9b5f9532013-06-02 23:38:46 -0700122 * the so-called base address is only offset in rtciobrg
123 */
124 error = of_property_read_u32(np, "reg", &pwrcdrv->pwrc_base);
125 if (error) {
126 dev_err(&pdev->dev,
127 "unable to find base address of pwrc node in dtb\n");
128 return error;
129 }
130
131 pwrcdrv->input = devm_input_allocate_device(&pdev->dev);
132 if (!pwrcdrv->input)
133 return -ENOMEM;
134
135 pwrcdrv->input->name = "sirfsoc pwrckey";
136 pwrcdrv->input->phys = "pwrc/input0";
Xianglong Dua1a75212014-02-15 12:30:58 -0800137 pwrcdrv->input->evbit[0] = BIT_MASK(EV_KEY);
138
139 INIT_DELAYED_WORK(&pwrcdrv->work, sirfsoc_pwrc_report_event);
Binghua Duan9b5f9532013-06-02 23:38:46 -0700140
Dmitry Torokhov864d3c02014-02-13 23:38:43 -0800141 pwrcdrv->input->open = sirfsoc_pwrc_open;
142 pwrcdrv->input->close = sirfsoc_pwrc_close;
143
144 input_set_drvdata(pwrcdrv->input, pwrcdrv);
145
146 /* Make sure the device is quiesced */
147 sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false);
148
Binghua Duan9b5f9532013-06-02 23:38:46 -0700149 irq = platform_get_irq(pdev, 0);
150 error = devm_request_irq(&pdev->dev, irq,
Barry Song50998172014-02-14 08:43:05 -0800151 sirfsoc_pwrc_isr, 0,
Binghua Duan9b5f9532013-06-02 23:38:46 -0700152 "sirfsoc_pwrc_int", pwrcdrv);
153 if (error) {
154 dev_err(&pdev->dev, "unable to claim irq %d, error: %d\n",
155 irq, error);
156 return error;
157 }
158
Binghua Duan9b5f9532013-06-02 23:38:46 -0700159 error = input_register_device(pwrcdrv->input);
160 if (error) {
161 dev_err(&pdev->dev,
162 "unable to register input device, error: %d\n",
163 error);
164 return error;
165 }
166
Xianglong Dua5e46642014-02-14 08:45:56 -0800167 dev_set_drvdata(&pdev->dev, pwrcdrv);
Binghua Duan9b5f9532013-06-02 23:38:46 -0700168 device_init_wakeup(&pdev->dev, 1);
169
170 return 0;
171}
172
173static int sirfsoc_pwrc_remove(struct platform_device *pdev)
174{
175 device_init_wakeup(&pdev->dev, 0);
176
177 return 0;
178}
179
180#ifdef CONFIG_PM_SLEEP
Xianglong Du3a800352014-02-14 08:45:13 -0800181static int sirfsoc_pwrc_resume(struct device *dev)
Binghua Duan9b5f9532013-06-02 23:38:46 -0700182{
Xianglong Dua5e46642014-02-14 08:45:56 -0800183 struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_get_drvdata(dev);
Dmitry Torokhov864d3c02014-02-13 23:38:43 -0800184 struct input_dev *input = pwrcdrv->input;
Binghua Duan9b5f9532013-06-02 23:38:46 -0700185
186 /*
187 * Do not mask pwrc interrupt as we want pwrc work as a wakeup source
188 * if users touch X_ONKEY_B, see arch/arm/mach-prima2/pm.c
189 */
Dmitry Torokhov864d3c02014-02-13 23:38:43 -0800190 mutex_lock(&input->mutex);
191 if (input->users)
192 sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true);
193 mutex_unlock(&input->mutex);
Binghua Duan9b5f9532013-06-02 23:38:46 -0700194
195 return 0;
196}
197#endif
198
Xianglong Du3a800352014-02-14 08:45:13 -0800199static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, sirfsoc_pwrc_resume);
Binghua Duan9b5f9532013-06-02 23:38:46 -0700200
201static struct platform_driver sirfsoc_pwrc_driver = {
202 .probe = sirfsoc_pwrc_probe,
203 .remove = sirfsoc_pwrc_remove,
204 .driver = {
205 .name = "sirfsoc-pwrc",
206 .owner = THIS_MODULE,
207 .pm = &sirfsoc_pwrc_pm_ops,
Sachin Kamatdee964c2013-10-06 00:51:28 -0700208 .of_match_table = sirfsoc_pwrc_of_match,
Binghua Duan9b5f9532013-06-02 23:38:46 -0700209 }
210};
211
212module_platform_driver(sirfsoc_pwrc_driver);
213
214MODULE_LICENSE("GPLv2");
215MODULE_AUTHOR("Binghua Duan <Binghua.Duan@csr.com>, Xianglong Du <Xianglong.Du@csr.com>");
216MODULE_DESCRIPTION("CSR Prima2 PWRC Driver");
217MODULE_ALIAS("platform:sirfsoc-pwrc");