Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf
[linux-2.6-microblaze.git] / drivers / input / keyboard / cypress-sf.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Cypress StreetFighter Touchkey Driver
4  *
5  * Copyright (c) 2021 Yassine Oudjana <y.oudjana@protonmail.com>
6  */
7
8 #include <linux/bitmap.h>
9 #include <linux/bitops.h>
10 #include <linux/device.h>
11 #include <linux/i2c.h>
12 #include <linux/input.h>
13 #include <linux/interrupt.h>
14 #include <linux/module.h>
15 #include <linux/pm.h>
16 #include <linux/regulator/consumer.h>
17
18 #define CYPRESS_SF_DEV_NAME "cypress-sf"
19
20 #define CYPRESS_SF_REG_BUTTON_STATUS    0x4a
21
22 struct cypress_sf_data {
23         struct i2c_client *client;
24         struct input_dev *input_dev;
25         struct regulator_bulk_data regulators[2];
26         u32 *keycodes;
27         unsigned long keystates;
28         int num_keys;
29 };
30
31 static irqreturn_t cypress_sf_irq_handler(int irq, void *devid)
32 {
33         struct cypress_sf_data *touchkey = devid;
34         unsigned long keystates, changed;
35         bool new_state;
36         int val, key;
37
38         val = i2c_smbus_read_byte_data(touchkey->client,
39                                        CYPRESS_SF_REG_BUTTON_STATUS);
40         if (val < 0) {
41                 dev_err(&touchkey->client->dev,
42                         "Failed to read button status: %d", val);
43                 return IRQ_NONE;
44         }
45         keystates = val;
46
47         bitmap_xor(&changed, &keystates, &touchkey->keystates,
48                    touchkey->num_keys);
49
50         for_each_set_bit(key, &changed, touchkey->num_keys) {
51                 new_state = keystates & BIT(key);
52                 dev_dbg(&touchkey->client->dev,
53                         "Key %d changed to %d", key, new_state);
54                 input_report_key(touchkey->input_dev,
55                                  touchkey->keycodes[key], new_state);
56         }
57
58         input_sync(touchkey->input_dev);
59         touchkey->keystates = keystates;
60
61         return IRQ_HANDLED;
62 }
63
64 static int cypress_sf_probe(struct i2c_client *client)
65 {
66         struct cypress_sf_data *touchkey;
67         int key, error;
68
69         touchkey = devm_kzalloc(&client->dev, sizeof(*touchkey), GFP_KERNEL);
70         if (!touchkey)
71                 return -ENOMEM;
72
73         touchkey->client = client;
74         i2c_set_clientdata(client, touchkey);
75
76         touchkey->regulators[0].supply = "vdd";
77         touchkey->regulators[1].supply = "avdd";
78
79         error = devm_regulator_bulk_get(&client->dev,
80                                         ARRAY_SIZE(touchkey->regulators),
81                                         touchkey->regulators);
82         if (error) {
83                 dev_err(&client->dev, "Failed to get regulators: %d\n", error);
84                 return error;
85         }
86
87         touchkey->num_keys = device_property_read_u32_array(&client->dev,
88                                                             "linux,keycodes",
89                                                             NULL, 0);
90         if (touchkey->num_keys < 0) {
91                 /* Default key count */
92                 touchkey->num_keys = 2;
93         }
94
95         touchkey->keycodes = devm_kcalloc(&client->dev,
96                                           touchkey->num_keys,
97                                           sizeof(*touchkey->keycodes),
98                                           GFP_KERNEL);
99         if (!touchkey->keycodes)
100                 return -ENOMEM;
101
102         error = device_property_read_u32_array(&client->dev, "linux,keycodes",
103                                                touchkey->keycodes,
104                                                touchkey->num_keys);
105
106         if (error) {
107                 dev_warn(&client->dev,
108                          "Failed to read keycodes: %d, using defaults\n",
109                          error);
110
111                 /* Default keycodes */
112                 touchkey->keycodes[0] = KEY_BACK;
113                 touchkey->keycodes[1] = KEY_MENU;
114         }
115
116         error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
117                                       touchkey->regulators);
118         if (error) {
119                 dev_err(&client->dev,
120                         "Failed to enable regulators: %d\n", error);
121                 return error;
122         }
123
124         touchkey->input_dev = devm_input_allocate_device(&client->dev);
125         if (!touchkey->input_dev) {
126                 dev_err(&client->dev, "Failed to allocate input device\n");
127                 return -ENOMEM;
128         }
129
130         touchkey->input_dev->name = CYPRESS_SF_DEV_NAME;
131         touchkey->input_dev->id.bustype = BUS_I2C;
132
133         for (key = 0; key < touchkey->num_keys; ++key)
134                 input_set_capability(touchkey->input_dev,
135                                      EV_KEY, touchkey->keycodes[key]);
136
137         error = input_register_device(touchkey->input_dev);
138         if (error) {
139                 dev_err(&client->dev,
140                         "Failed to register input device: %d\n", error);
141                 return error;
142         }
143
144         error = devm_request_threaded_irq(&client->dev, client->irq,
145                                           NULL, cypress_sf_irq_handler,
146                                           IRQF_ONESHOT,
147                                           CYPRESS_SF_DEV_NAME, touchkey);
148         if (error) {
149                 dev_err(&client->dev,
150                         "Failed to register threaded irq: %d", error);
151                 return error;
152         }
153
154         return 0;
155 };
156
157 static int __maybe_unused cypress_sf_suspend(struct device *dev)
158 {
159         struct i2c_client *client = to_i2c_client(dev);
160         struct cypress_sf_data *touchkey = i2c_get_clientdata(client);
161         int error;
162
163         disable_irq(client->irq);
164
165         error = regulator_bulk_disable(ARRAY_SIZE(touchkey->regulators),
166                                        touchkey->regulators);
167         if (error) {
168                 dev_err(dev, "Failed to disable regulators: %d", error);
169                 enable_irq(client->irq);
170                 return error;
171         }
172
173         return 0;
174 }
175
176 static int __maybe_unused cypress_sf_resume(struct device *dev)
177 {
178         struct i2c_client *client = to_i2c_client(dev);
179         struct cypress_sf_data *touchkey = i2c_get_clientdata(client);
180         int error;
181
182         error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
183                                       touchkey->regulators);
184         if (error) {
185                 dev_err(dev, "Failed to enable regulators: %d", error);
186                 return error;
187         }
188
189         enable_irq(client->irq);
190
191         return 0;
192 }
193
194 static SIMPLE_DEV_PM_OPS(cypress_sf_pm_ops,
195                          cypress_sf_suspend, cypress_sf_resume);
196
197 static struct i2c_device_id cypress_sf_id_table[] = {
198         { CYPRESS_SF_DEV_NAME, 0 },
199         { }
200 };
201 MODULE_DEVICE_TABLE(i2c, cypress_sf_id_table);
202
203 #ifdef CONFIG_OF
204 static const struct of_device_id cypress_sf_of_match[] = {
205         { .compatible = "cypress,sf3155", },
206         { },
207 };
208 MODULE_DEVICE_TABLE(of, cypress_sf_of_match);
209 #endif
210
211 static struct i2c_driver cypress_sf_driver = {
212         .driver = {
213                 .name = CYPRESS_SF_DEV_NAME,
214                 .pm = &cypress_sf_pm_ops,
215                 .of_match_table = of_match_ptr(cypress_sf_of_match),
216         },
217         .id_table = cypress_sf_id_table,
218         .probe_new = cypress_sf_probe,
219 };
220 module_i2c_driver(cypress_sf_driver);
221
222 MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
223 MODULE_DESCRIPTION("Cypress StreetFighter Touchkey Driver");
224 MODULE_LICENSE("GPL v2");