1 // SPDX-License-Identifier: GPL-2.0-only
3 * Cypress StreetFighter Touchkey Driver
5 * Copyright (c) 2021 Yassine Oudjana <y.oudjana@protonmail.com>
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>
16 #include <linux/regulator/consumer.h>
18 #define CYPRESS_SF_DEV_NAME "cypress-sf"
20 #define CYPRESS_SF_REG_BUTTON_STATUS 0x4a
22 struct cypress_sf_data {
23 struct i2c_client *client;
24 struct input_dev *input_dev;
25 struct regulator_bulk_data regulators[2];
27 unsigned long keystates;
31 static irqreturn_t cypress_sf_irq_handler(int irq, void *devid)
33 struct cypress_sf_data *touchkey = devid;
34 unsigned long keystates, changed;
38 val = i2c_smbus_read_byte_data(touchkey->client,
39 CYPRESS_SF_REG_BUTTON_STATUS);
41 dev_err(&touchkey->client->dev,
42 "Failed to read button status: %d", val);
47 bitmap_xor(&changed, &keystates, &touchkey->keystates,
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);
58 input_sync(touchkey->input_dev);
59 touchkey->keystates = keystates;
64 static int cypress_sf_probe(struct i2c_client *client)
66 struct cypress_sf_data *touchkey;
69 touchkey = devm_kzalloc(&client->dev, sizeof(*touchkey), GFP_KERNEL);
73 touchkey->client = client;
74 i2c_set_clientdata(client, touchkey);
76 touchkey->regulators[0].supply = "vdd";
77 touchkey->regulators[1].supply = "avdd";
79 error = devm_regulator_bulk_get(&client->dev,
80 ARRAY_SIZE(touchkey->regulators),
81 touchkey->regulators);
83 dev_err(&client->dev, "Failed to get regulators: %d\n", error);
87 touchkey->num_keys = device_property_read_u32_array(&client->dev,
90 if (touchkey->num_keys < 0) {
91 /* Default key count */
92 touchkey->num_keys = 2;
95 touchkey->keycodes = devm_kcalloc(&client->dev,
97 sizeof(*touchkey->keycodes),
99 if (!touchkey->keycodes)
102 error = device_property_read_u32_array(&client->dev, "linux,keycodes",
107 dev_warn(&client->dev,
108 "Failed to read keycodes: %d, using defaults\n",
111 /* Default keycodes */
112 touchkey->keycodes[0] = KEY_BACK;
113 touchkey->keycodes[1] = KEY_MENU;
116 error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
117 touchkey->regulators);
119 dev_err(&client->dev,
120 "Failed to enable regulators: %d\n", error);
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");
130 touchkey->input_dev->name = CYPRESS_SF_DEV_NAME;
131 touchkey->input_dev->id.bustype = BUS_I2C;
133 for (key = 0; key < touchkey->num_keys; ++key)
134 input_set_capability(touchkey->input_dev,
135 EV_KEY, touchkey->keycodes[key]);
137 error = input_register_device(touchkey->input_dev);
139 dev_err(&client->dev,
140 "Failed to register input device: %d\n", error);
144 error = devm_request_threaded_irq(&client->dev, client->irq,
145 NULL, cypress_sf_irq_handler,
147 CYPRESS_SF_DEV_NAME, touchkey);
149 dev_err(&client->dev,
150 "Failed to register threaded irq: %d", error);
157 static int __maybe_unused cypress_sf_suspend(struct device *dev)
159 struct i2c_client *client = to_i2c_client(dev);
160 struct cypress_sf_data *touchkey = i2c_get_clientdata(client);
163 disable_irq(client->irq);
165 error = regulator_bulk_disable(ARRAY_SIZE(touchkey->regulators),
166 touchkey->regulators);
168 dev_err(dev, "Failed to disable regulators: %d", error);
169 enable_irq(client->irq);
176 static int __maybe_unused cypress_sf_resume(struct device *dev)
178 struct i2c_client *client = to_i2c_client(dev);
179 struct cypress_sf_data *touchkey = i2c_get_clientdata(client);
182 error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
183 touchkey->regulators);
185 dev_err(dev, "Failed to enable regulators: %d", error);
189 enable_irq(client->irq);
194 static SIMPLE_DEV_PM_OPS(cypress_sf_pm_ops,
195 cypress_sf_suspend, cypress_sf_resume);
197 static struct i2c_device_id cypress_sf_id_table[] = {
198 { CYPRESS_SF_DEV_NAME, 0 },
201 MODULE_DEVICE_TABLE(i2c, cypress_sf_id_table);
204 static const struct of_device_id cypress_sf_of_match[] = {
205 { .compatible = "cypress,sf3155", },
208 MODULE_DEVICE_TABLE(of, cypress_sf_of_match);
211 static struct i2c_driver cypress_sf_driver = {
213 .name = CYPRESS_SF_DEV_NAME,
214 .pm = &cypress_sf_pm_ops,
215 .of_match_table = of_match_ptr(cypress_sf_of_match),
217 .id_table = cypress_sf_id_table,
218 .probe_new = cypress_sf_probe,
220 module_i2c_driver(cypress_sf_driver);
222 MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
223 MODULE_DESCRIPTION("Cypress StreetFighter Touchkey Driver");
224 MODULE_LICENSE("GPL v2");