1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2022 Richtek Technology Corp. |
4 | * Author: ChiYuan Huang <cy_huang@richtek.com> |
5 | */ |
6 | |
7 | #include <linux/bits.h> |
8 | #include <linux/input.h> |
9 | #include <linux/interrupt.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/mod_devicetable.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/regmap.h> |
15 | |
16 | #define RT5120_REG_INTSTAT 0x1E |
17 | #define RT5120_PWRKEYSTAT_MASK BIT(7) |
18 | |
19 | struct rt5120_priv { |
20 | struct regmap *regmap; |
21 | struct input_dev *input; |
22 | }; |
23 | |
24 | static irqreturn_t rt5120_pwrkey_handler(int irq, void *devid) |
25 | { |
26 | struct rt5120_priv *priv = devid; |
27 | unsigned int stat; |
28 | int error; |
29 | |
30 | error = regmap_read(map: priv->regmap, RT5120_REG_INTSTAT, val: &stat); |
31 | if (error) |
32 | return IRQ_NONE; |
33 | |
34 | input_report_key(dev: priv->input, KEY_POWER, |
35 | value: !(stat & RT5120_PWRKEYSTAT_MASK)); |
36 | input_sync(dev: priv->input); |
37 | |
38 | return IRQ_HANDLED; |
39 | } |
40 | |
41 | static int rt5120_pwrkey_probe(struct platform_device *pdev) |
42 | { |
43 | struct rt5120_priv *priv; |
44 | struct device *dev = &pdev->dev; |
45 | int press_irq, release_irq; |
46 | int error; |
47 | |
48 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
49 | if (!priv) |
50 | return -ENOMEM; |
51 | |
52 | priv->regmap = dev_get_regmap(dev: dev->parent, NULL); |
53 | if (!priv->regmap) { |
54 | dev_err(dev, "Failed to init regmap\n" ); |
55 | return -ENODEV; |
56 | } |
57 | |
58 | press_irq = platform_get_irq_byname(pdev, "pwrkey-press" ); |
59 | if (press_irq < 0) |
60 | return press_irq; |
61 | |
62 | release_irq = platform_get_irq_byname(pdev, "pwrkey-release" ); |
63 | if (release_irq < 0) |
64 | return release_irq; |
65 | |
66 | /* Make input device be device resource managed */ |
67 | priv->input = devm_input_allocate_device(dev); |
68 | if (!priv->input) |
69 | return -ENOMEM; |
70 | |
71 | priv->input->name = "rt5120_pwrkey" ; |
72 | priv->input->phys = "rt5120_pwrkey/input0" ; |
73 | priv->input->id.bustype = BUS_I2C; |
74 | input_set_capability(dev: priv->input, EV_KEY, KEY_POWER); |
75 | |
76 | error = input_register_device(priv->input); |
77 | if (error) { |
78 | dev_err(dev, "Failed to register input device: %d\n" , error); |
79 | return error; |
80 | } |
81 | |
82 | error = devm_request_threaded_irq(dev, irq: press_irq, |
83 | NULL, thread_fn: rt5120_pwrkey_handler, |
84 | irqflags: 0, devname: "pwrkey-press" , dev_id: priv); |
85 | if (error) { |
86 | dev_err(dev, |
87 | "Failed to register pwrkey press irq: %d\n" , error); |
88 | return error; |
89 | } |
90 | |
91 | error = devm_request_threaded_irq(dev, irq: release_irq, |
92 | NULL, thread_fn: rt5120_pwrkey_handler, |
93 | irqflags: 0, devname: "pwrkey-release" , dev_id: priv); |
94 | if (error) { |
95 | dev_err(dev, |
96 | "Failed to register pwrkey release irq: %d\n" , error); |
97 | return error; |
98 | } |
99 | |
100 | return 0; |
101 | } |
102 | |
103 | static const struct of_device_id r5120_pwrkey_match_table[] = { |
104 | { .compatible = "richtek,rt5120-pwrkey" }, |
105 | {} |
106 | }; |
107 | MODULE_DEVICE_TABLE(of, r5120_pwrkey_match_table); |
108 | |
109 | static struct platform_driver rt5120_pwrkey_driver = { |
110 | .driver = { |
111 | .name = "rt5120-pwrkey" , |
112 | .of_match_table = r5120_pwrkey_match_table, |
113 | }, |
114 | .probe = rt5120_pwrkey_probe, |
115 | }; |
116 | module_platform_driver(rt5120_pwrkey_driver); |
117 | |
118 | MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>" ); |
119 | MODULE_DESCRIPTION("Richtek RT5120 power key driver" ); |
120 | MODULE_LICENSE("GPL" ); |
121 | |