1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * TM2 touchkey device driver
4  *
5  * Copyright 2005 Phil Blundell
6  * Copyright 2016 Samsung Electronics Co., Ltd.
7  *
8  * Author: Beomho Seo <beomho.seo@samsung.com>
9  * Author: Jaechul Lee <jcsing.lee@samsung.com>
10  */
11 
12 #include <linux/bitops.h>
13 #include <linux/delay.h>
14 #include <linux/device.h>
15 #include <linux/i2c.h>
16 #include <linux/input.h>
17 #include <linux/interrupt.h>
18 #include <linux/irq.h>
19 #include <linux/leds.h>
20 #include <linux/module.h>
21 #include <linux/of.h>
22 #include <linux/of_device.h>
23 #include <linux/pm.h>
24 #include <linux/regulator/consumer.h>
25 
26 #define TM2_TOUCHKEY_DEV_NAME		"tm2-touchkey"
27 
28 #define ARIES_TOUCHKEY_CMD_LED_ON	0x1
29 #define ARIES_TOUCHKEY_CMD_LED_OFF	0x2
30 #define TM2_TOUCHKEY_CMD_LED_ON		0x10
31 #define TM2_TOUCHKEY_CMD_LED_OFF	0x20
32 #define TM2_TOUCHKEY_BIT_PRESS_EV	BIT(3)
33 #define TM2_TOUCHKEY_BIT_KEYCODE	GENMASK(2, 0)
34 #define TM2_TOUCHKEY_LED_VOLTAGE_MIN	2500000
35 #define TM2_TOUCHKEY_LED_VOLTAGE_MAX	3300000
36 
37 struct touchkey_variant {
38 	u8 keycode_reg;
39 	u8 base_reg;
40 	u8 cmd_led_on;
41 	u8 cmd_led_off;
42 	bool no_reg;
43 	bool fixed_regulator;
44 };
45 
46 struct tm2_touchkey_data {
47 	struct i2c_client *client;
48 	struct input_dev *input_dev;
49 	struct led_classdev led_dev;
50 	struct regulator *vdd;
51 	struct regulator_bulk_data regulators[2];
52 	const struct touchkey_variant *variant;
53 	u32 keycodes[4];
54 	int num_keycodes;
55 };
56 
57 static const struct touchkey_variant tm2_touchkey_variant = {
58 	.keycode_reg = 0x03,
59 	.base_reg = 0x00,
60 	.cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON,
61 	.cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF,
62 };
63 
64 static const struct touchkey_variant midas_touchkey_variant = {
65 	.keycode_reg = 0x00,
66 	.base_reg = 0x00,
67 	.cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON,
68 	.cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF,
69 };
70 
71 static struct touchkey_variant aries_touchkey_variant = {
72 	.no_reg = true,
73 	.fixed_regulator = true,
74 	.cmd_led_on = ARIES_TOUCHKEY_CMD_LED_ON,
75 	.cmd_led_off = ARIES_TOUCHKEY_CMD_LED_OFF,
76 };
77 
78 static int tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
79 					    enum led_brightness brightness)
80 {
81 	struct tm2_touchkey_data *touchkey =
82 		container_of(led_dev, struct tm2_touchkey_data, led_dev);
83 	u32 volt;
84 	u8 data;
85 
86 	if (brightness == LED_OFF) {
87 		volt = TM2_TOUCHKEY_LED_VOLTAGE_MIN;
88 		data = touchkey->variant->cmd_led_off;
89 	} else {
90 		volt = TM2_TOUCHKEY_LED_VOLTAGE_MAX;
91 		data = touchkey->variant->cmd_led_on;
92 	}
93 
94 	if (!touchkey->variant->fixed_regulator)
95 		regulator_set_voltage(touchkey->vdd, volt, volt);
96 
97 	return touchkey->variant->no_reg ?
98 		i2c_smbus_write_byte(touchkey->client, data) :
99 		i2c_smbus_write_byte_data(touchkey->client,
100 					  touchkey->variant->base_reg, data);
101 }
102 
103 static int tm2_touchkey_power_enable(struct tm2_touchkey_data *touchkey)
104 {
105 	int error;
106 
107 	error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
108 				      touchkey->regulators);
109 	if (error)
110 		return error;
111 
112 	/* waiting for device initialization, at least 150ms */
113 	msleep(150);
114 
115 	return 0;
116 }
117 
118 static void tm2_touchkey_power_disable(void *data)
119 {
120 	struct tm2_touchkey_data *touchkey = data;
121 
122 	regulator_bulk_disable(ARRAY_SIZE(touchkey->regulators),
123 			       touchkey->regulators);
124 }
125 
126 static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid)
127 {
128 	struct tm2_touchkey_data *touchkey = devid;
129 	int data;
130 	int index;
131 	int i;
132 
133 	if (touchkey->variant->no_reg)
134 		data = i2c_smbus_read_byte(touchkey->client);
135 	else
136 		data = i2c_smbus_read_byte_data(touchkey->client,
137 						touchkey->variant->keycode_reg);
138 	if (data < 0) {
139 		dev_err(&touchkey->client->dev,
140 			"failed to read i2c data: %d\n", data);
141 		goto out;
142 	}
143 
144 	index = (data & TM2_TOUCHKEY_BIT_KEYCODE) - 1;
145 	if (index < 0 || index >= touchkey->num_keycodes) {
146 		dev_warn(&touchkey->client->dev,
147 			 "invalid keycode index %d\n", index);
148 		goto out;
149 	}
150 
151 	if (data & TM2_TOUCHKEY_BIT_PRESS_EV) {
152 		for (i = 0; i < touchkey->num_keycodes; i++)
153 			input_report_key(touchkey->input_dev,
154 					 touchkey->keycodes[i], 0);
155 	} else {
156 		input_report_key(touchkey->input_dev,
157 				 touchkey->keycodes[index], 1);
158 	}
159 
160 	input_sync(touchkey->input_dev);
161 
162 out:
163 	if (touchkey->variant->fixed_regulator &&
164 				data & TM2_TOUCHKEY_BIT_PRESS_EV) {
165 		/* touch turns backlight on, so make sure we're in sync */
166 		if (touchkey->led_dev.brightness == LED_OFF)
167 			tm2_touchkey_led_brightness_set(&touchkey->led_dev,
168 							LED_OFF);
169 	}
170 
171 	return IRQ_HANDLED;
172 }
173 
174 static int tm2_touchkey_probe(struct i2c_client *client,
175 			      const struct i2c_device_id *id)
176 {
177 	struct device_node *np = client->dev.of_node;
178 	struct tm2_touchkey_data *touchkey;
179 	int error;
180 	int i;
181 
182 	if (!i2c_check_functionality(client->adapter,
183 			I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA)) {
184 		dev_err(&client->dev, "incompatible I2C adapter\n");
185 		return -EIO;
186 	}
187 
188 	touchkey = devm_kzalloc(&client->dev, sizeof(*touchkey), GFP_KERNEL);
189 	if (!touchkey)
190 		return -ENOMEM;
191 
192 	touchkey->client = client;
193 	i2c_set_clientdata(client, touchkey);
194 
195 	touchkey->variant = of_device_get_match_data(&client->dev);
196 
197 	touchkey->regulators[0].supply = "vcc";
198 	touchkey->regulators[1].supply = "vdd";
199 	error = devm_regulator_bulk_get(&client->dev,
200 					ARRAY_SIZE(touchkey->regulators),
201 					touchkey->regulators);
202 	if (error) {
203 		dev_err(&client->dev, "failed to get regulators: %d\n", error);
204 		return error;
205 	}
206 
207 	/* Save VDD for easy access */
208 	touchkey->vdd = touchkey->regulators[1].consumer;
209 
210 	touchkey->num_keycodes = of_property_read_variable_u32_array(np,
211 					"linux,keycodes", touchkey->keycodes, 0,
212 					ARRAY_SIZE(touchkey->keycodes));
213 	if (touchkey->num_keycodes <= 0) {
214 		/* default keycodes */
215 		touchkey->keycodes[0] = KEY_PHONE;
216 		touchkey->keycodes[1] = KEY_BACK;
217 		touchkey->num_keycodes = 2;
218 	}
219 
220 	error = tm2_touchkey_power_enable(touchkey);
221 	if (error) {
222 		dev_err(&client->dev, "failed to power up device: %d\n", error);
223 		return error;
224 	}
225 
226 	error = devm_add_action_or_reset(&client->dev,
227 					 tm2_touchkey_power_disable, touchkey);
228 	if (error) {
229 		dev_err(&client->dev,
230 			"failed to install poweroff handler: %d\n", error);
231 		return error;
232 	}
233 
234 	/* input device */
235 	touchkey->input_dev = devm_input_allocate_device(&client->dev);
236 	if (!touchkey->input_dev) {
237 		dev_err(&client->dev, "failed to allocate input device\n");
238 		return -ENOMEM;
239 	}
240 
241 	touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME;
242 	touchkey->input_dev->id.bustype = BUS_I2C;
243 
244 	for (i = 0; i < touchkey->num_keycodes; i++)
245 		input_set_capability(touchkey->input_dev, EV_KEY,
246 				     touchkey->keycodes[i]);
247 
248 	error = input_register_device(touchkey->input_dev);
249 	if (error) {
250 		dev_err(&client->dev,
251 			"failed to register input device: %d\n", error);
252 		return error;
253 	}
254 
255 	error = devm_request_threaded_irq(&client->dev, client->irq,
256 					  NULL, tm2_touchkey_irq_handler,
257 					  IRQF_ONESHOT,
258 					  TM2_TOUCHKEY_DEV_NAME, touchkey);
259 	if (error) {
260 		dev_err(&client->dev,
261 			"failed to request threaded irq: %d\n", error);
262 		return error;
263 	}
264 
265 	/* led device */
266 	touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME;
267 	touchkey->led_dev.brightness = LED_ON;
268 	touchkey->led_dev.max_brightness = LED_ON;
269 	touchkey->led_dev.brightness_set_blocking =
270 					tm2_touchkey_led_brightness_set;
271 
272 	error = devm_led_classdev_register(&client->dev, &touchkey->led_dev);
273 	if (error) {
274 		dev_err(&client->dev,
275 			"failed to register touchkey led: %d\n", error);
276 		return error;
277 	}
278 
279 	if (touchkey->variant->fixed_regulator)
280 		tm2_touchkey_led_brightness_set(&touchkey->led_dev, LED_ON);
281 
282 	return 0;
283 }
284 
285 static int __maybe_unused tm2_touchkey_suspend(struct device *dev)
286 {
287 	struct i2c_client *client = to_i2c_client(dev);
288 	struct tm2_touchkey_data *touchkey = i2c_get_clientdata(client);
289 
290 	disable_irq(client->irq);
291 	tm2_touchkey_power_disable(touchkey);
292 
293 	return 0;
294 }
295 
296 static int __maybe_unused tm2_touchkey_resume(struct device *dev)
297 {
298 	struct i2c_client *client = to_i2c_client(dev);
299 	struct tm2_touchkey_data *touchkey = i2c_get_clientdata(client);
300 	int ret;
301 
302 	enable_irq(client->irq);
303 
304 	ret = tm2_touchkey_power_enable(touchkey);
305 	if (ret)
306 		dev_err(dev, "failed to enable power: %d\n", ret);
307 
308 	return ret;
309 }
310 
311 static SIMPLE_DEV_PM_OPS(tm2_touchkey_pm_ops,
312 			 tm2_touchkey_suspend, tm2_touchkey_resume);
313 
314 static const struct i2c_device_id tm2_touchkey_id_table[] = {
315 	{ TM2_TOUCHKEY_DEV_NAME, 0 },
316 	{ },
317 };
318 MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table);
319 
320 static const struct of_device_id tm2_touchkey_of_match[] = {
321 	{
322 		.compatible = "cypress,tm2-touchkey",
323 		.data = &tm2_touchkey_variant,
324 	}, {
325 		.compatible = "cypress,midas-touchkey",
326 		.data = &midas_touchkey_variant,
327 	}, {
328 		.compatible = "cypress,aries-touchkey",
329 		.data = &aries_touchkey_variant,
330 	},
331 	{ },
332 };
333 MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match);
334 
335 static struct i2c_driver tm2_touchkey_driver = {
336 	.driver = {
337 		.name = TM2_TOUCHKEY_DEV_NAME,
338 		.pm = &tm2_touchkey_pm_ops,
339 		.of_match_table = of_match_ptr(tm2_touchkey_of_match),
340 	},
341 	.probe = tm2_touchkey_probe,
342 	.id_table = tm2_touchkey_id_table,
343 };
344 module_i2c_driver(tm2_touchkey_driver);
345 
346 MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
347 MODULE_AUTHOR("Jaechul Lee <jcsing.lee@samsung.com>");
348 MODULE_DESCRIPTION("Samsung touchkey driver");
349 MODULE_LICENSE("GPL v2");
350