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