xref: /rk3399_rockchip-uboot/drivers/input/key-uclass.c (revision 5a54baa79344007142206f4dab5dd46d8ea1dc91)
1b398a9a7SJoseph Chen /*
2b398a9a7SJoseph Chen  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3b398a9a7SJoseph Chen  *
4b398a9a7SJoseph Chen  * SPDX-License-Identifier:     GPL-2.0+
5b398a9a7SJoseph Chen  */
6b398a9a7SJoseph Chen 
764048c53SJoseph Chen #include <common.h>
864048c53SJoseph Chen #include <adc.h>
9b398a9a7SJoseph Chen #include <dm.h>
10b398a9a7SJoseph Chen #include <key.h>
11*5a54baa7SJoseph Chen #include <dm/lists.h>
12*5a54baa7SJoseph Chen #include <irq-generic.h>
1364048c53SJoseph Chen 
14*5a54baa7SJoseph Chen #define KEY_WARN(fmt, args...)	printf("Key Warn: "fmt, ##args)
15*5a54baa7SJoseph Chen #define KEY_ERR(fmt, args...)	printf("Key Error: "fmt, ##args)
16*5a54baa7SJoseph Chen #define KEY_DBG(fmt, args...)	 debug("Key Debug: "fmt, ##args)
17adba3792SJoseph Chen 
18a2df9606SJoseph Chen static inline uint64_t arch_counter_get_cntpct(void)
19a2df9606SJoseph Chen {
20a2df9606SJoseph Chen 	uint64_t cval = 0;
21a2df9606SJoseph Chen 
22a2df9606SJoseph Chen 	isb();
23a2df9606SJoseph Chen #ifdef CONFIG_ARM64
24a2df9606SJoseph Chen 	asm volatile("mrs %0, cntpct_el0" : "=r" (cval));
25a2df9606SJoseph Chen #else
26a2df9606SJoseph Chen 	asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval));
27a2df9606SJoseph Chen #endif
28a2df9606SJoseph Chen 	return cval;
29a2df9606SJoseph Chen }
30a2df9606SJoseph Chen 
3164048c53SJoseph Chen uint64_t key_timer(uint64_t base)
32a2df9606SJoseph Chen {
33a2df9606SJoseph Chen 	uint64_t cntpct;
34a2df9606SJoseph Chen 
35a2df9606SJoseph Chen 	cntpct = arch_counter_get_cntpct() / 24000UL;
36a2df9606SJoseph Chen 	return (cntpct > base) ? (cntpct - base) : 0;
37a2df9606SJoseph Chen }
38a2df9606SJoseph Chen 
39*5a54baa7SJoseph Chen static int key_adc_event(struct dm_key_uclass_platdata *uc_key, int adcval)
40a2df9606SJoseph Chen {
41*5a54baa7SJoseph Chen 	return (adcval <= uc_key->max && adcval >= uc_key->min) ?
42*5a54baa7SJoseph Chen 		KEY_PRESS_DOWN : KEY_PRESS_NONE;
433fb84000SJoseph Chen }
443fb84000SJoseph Chen 
45*5a54baa7SJoseph Chen static int key_gpio_event(struct dm_key_uclass_platdata *uc_key)
463fb84000SJoseph Chen {
47*5a54baa7SJoseph Chen 	if (!dm_gpio_is_valid(&uc_key->gpio)) {
48*5a54baa7SJoseph Chen 		KEY_ERR("'%s' Invalid gpio\n", uc_key->name);
4964048c53SJoseph Chen 		return KEY_PRESS_NONE;
5064048c53SJoseph Chen 	}
5164048c53SJoseph Chen 
52*5a54baa7SJoseph Chen 	return dm_gpio_get_value(&uc_key->gpio) ?
53*5a54baa7SJoseph Chen 	       KEY_PRESS_DOWN : KEY_PRESS_NONE;
5464048c53SJoseph Chen }
5564048c53SJoseph Chen 
56*5a54baa7SJoseph Chen static int key_gpio_interrupt_event(struct dm_key_uclass_platdata *uc_key)
5764048c53SJoseph Chen {
58*5a54baa7SJoseph Chen 	int event;
593fb84000SJoseph Chen 
603fb84000SJoseph Chen 	debug("%s: %s: up=%llu, down=%llu, delta=%llu\n",
61*5a54baa7SJoseph Chen 	      __func__, uc_key->name, uc_key->rise_ms, uc_key->fall_ms,
62*5a54baa7SJoseph Chen 	      uc_key->rise_ms - uc_key->fall_ms);
633fb84000SJoseph Chen 
643fb84000SJoseph Chen 	/* Possible this is machine power-on long pressed, so ignore this */
65*5a54baa7SJoseph Chen 	if (uc_key->fall_ms == 0 && uc_key->rise_ms != 0) {
66*5a54baa7SJoseph Chen 		event = KEY_PRESS_NONE;
673fb84000SJoseph Chen 		goto out;
683fb84000SJoseph Chen 	}
693fb84000SJoseph Chen 
70*5a54baa7SJoseph Chen 	if ((uc_key->rise_ms > uc_key->fall_ms) &&
71*5a54baa7SJoseph Chen 	    (uc_key->rise_ms - uc_key->fall_ms) >= KEY_LONG_DOWN_MS) {
72*5a54baa7SJoseph Chen 		uc_key->rise_ms = 0;
73*5a54baa7SJoseph Chen 		uc_key->fall_ms = 0;
74*5a54baa7SJoseph Chen 		event = KEY_PRESS_LONG_DOWN;
75*5a54baa7SJoseph Chen 		KEY_DBG("%s key long pressed..\n", uc_key->name);
76*5a54baa7SJoseph Chen 	} else if (uc_key->fall_ms &&
77*5a54baa7SJoseph Chen 		   key_timer(uc_key->fall_ms) >= KEY_LONG_DOWN_MS) {
78*5a54baa7SJoseph Chen 		uc_key->rise_ms = 0;
79*5a54baa7SJoseph Chen 		uc_key->fall_ms = 0;
80*5a54baa7SJoseph Chen 		event = KEY_PRESS_LONG_DOWN;
81*5a54baa7SJoseph Chen 		KEY_DBG("%s key long pressed(hold)..\n", uc_key->name);
82*5a54baa7SJoseph Chen 	} else if ((uc_key->rise_ms > uc_key->fall_ms) &&
83*5a54baa7SJoseph Chen 		   (uc_key->rise_ms - uc_key->fall_ms) < KEY_LONG_DOWN_MS) {
84*5a54baa7SJoseph Chen 		uc_key->rise_ms = 0;
85*5a54baa7SJoseph Chen 		uc_key->fall_ms = 0;
86*5a54baa7SJoseph Chen 		event = KEY_PRESS_DOWN;
87*5a54baa7SJoseph Chen 		KEY_DBG("%s key short pressed..\n", uc_key->name);
8864048c53SJoseph Chen 	/* Possible in charge animation, we enable irq after fuel gauge updated */
89*5a54baa7SJoseph Chen 	} else if (uc_key->rise_ms && uc_key->fall_ms &&
90*5a54baa7SJoseph Chen 		   (uc_key->rise_ms == uc_key->fall_ms)) {
91*5a54baa7SJoseph Chen 		uc_key->rise_ms = 0;
92*5a54baa7SJoseph Chen 		uc_key->fall_ms = 0;
93*5a54baa7SJoseph Chen 		event = KEY_PRESS_DOWN;
94*5a54baa7SJoseph Chen 		KEY_DBG("%s key short pressed..\n", uc_key->name);
953fb84000SJoseph Chen 	} else {
96*5a54baa7SJoseph Chen 		event = KEY_PRESS_NONE;
973fb84000SJoseph Chen 	}
983fb84000SJoseph Chen 
993fb84000SJoseph Chen out:
100*5a54baa7SJoseph Chen 	return event;
10164048c53SJoseph Chen }
10264048c53SJoseph Chen 
103*5a54baa7SJoseph Chen int key_is_pressed(int event)
10464048c53SJoseph Chen {
105*5a54baa7SJoseph Chen 	return (event == KEY_PRESS_DOWN || event == KEY_PRESS_LONG_DOWN);
10664048c53SJoseph Chen }
10764048c53SJoseph Chen 
108*5a54baa7SJoseph Chen static int key_core_read(struct dm_key_uclass_platdata *uc_key)
10964048c53SJoseph Chen {
11064048c53SJoseph Chen 	unsigned int adcval;
11164048c53SJoseph Chen 
112*5a54baa7SJoseph Chen 	if (uc_key->type == ADC_KEY) {
113*5a54baa7SJoseph Chen 		if (adc_channel_single_shot("saradc",
114*5a54baa7SJoseph Chen 					    uc_key->channel, &adcval)) {
115*5a54baa7SJoseph Chen 			KEY_ERR("%s failed to read saradc\n", uc_key->name);
116*5a54baa7SJoseph Chen 			return KEY_NOT_EXIST;
11764048c53SJoseph Chen 		}
11864048c53SJoseph Chen 
119*5a54baa7SJoseph Chen 		return key_adc_event(uc_key, adcval);
120*5a54baa7SJoseph Chen 	}
12164048c53SJoseph Chen 
122*5a54baa7SJoseph Chen 	return (uc_key->code == KEY_POWER) ?
123*5a54baa7SJoseph Chen 		key_gpio_interrupt_event(uc_key) :
124*5a54baa7SJoseph Chen 		key_gpio_event(uc_key);
1259f1dd9dfSJoseph Chen }
1269f1dd9dfSJoseph Chen 
1279f1dd9dfSJoseph Chen int key_read(int code)
1289f1dd9dfSJoseph Chen {
129*5a54baa7SJoseph Chen 	struct dm_key_uclass_platdata *uc_key;
1309f1dd9dfSJoseph Chen 	struct udevice *dev;
131*5a54baa7SJoseph Chen 	struct uclass *uc;
132*5a54baa7SJoseph Chen 	bool allow_pre_reloc = false;
133*5a54baa7SJoseph Chen 	int ret, event = KEY_NOT_EXIST;
1349f1dd9dfSJoseph Chen 
135*5a54baa7SJoseph Chen 	ret = uclass_get(UCLASS_KEY, &uc);
136*5a54baa7SJoseph Chen 	if (ret)
137*5a54baa7SJoseph Chen 		return ret;
138*5a54baa7SJoseph Chen 
139*5a54baa7SJoseph Chen try_again:
1409f1dd9dfSJoseph Chen 	for (uclass_first_device(UCLASS_KEY, &dev);
1419f1dd9dfSJoseph Chen 	     dev;
1429f1dd9dfSJoseph Chen 	     uclass_next_device(&dev)) {
143*5a54baa7SJoseph Chen 		uc_key = dev_get_uclass_platdata(dev);
1449f1dd9dfSJoseph Chen 
145*5a54baa7SJoseph Chen 		if (!allow_pre_reloc && uc_key->pre_reloc)
1469f1dd9dfSJoseph Chen 			continue;
1479f1dd9dfSJoseph Chen 
148*5a54baa7SJoseph Chen 		if (uc_key->code != code)
1499f1dd9dfSJoseph Chen 			continue;
1509f1dd9dfSJoseph Chen 
151*5a54baa7SJoseph Chen 		event = key_core_read(uc_key);
152*5a54baa7SJoseph Chen 		if (key_is_pressed(event))
153*5a54baa7SJoseph Chen 			return event;
154*5a54baa7SJoseph Chen 	}
155*5a54baa7SJoseph Chen 
156*5a54baa7SJoseph Chen 	/* If not find valid key node from kernel, try from u-boot */
157*5a54baa7SJoseph Chen 	if (event == KEY_NOT_EXIST && !allow_pre_reloc) {
158*5a54baa7SJoseph Chen 		allow_pre_reloc = true;
159*5a54baa7SJoseph Chen 		goto try_again;
160*5a54baa7SJoseph Chen 	}
161*5a54baa7SJoseph Chen 
162*5a54baa7SJoseph Chen 	return event;
163*5a54baa7SJoseph Chen }
164*5a54baa7SJoseph Chen 
165*5a54baa7SJoseph Chen #ifdef CONFIG_IRQ
166*5a54baa7SJoseph Chen static void gpio_irq_handler(int irq, void *data)
167*5a54baa7SJoseph Chen {
168*5a54baa7SJoseph Chen 	struct dm_key_uclass_platdata *uc_key = data;
169*5a54baa7SJoseph Chen 
170*5a54baa7SJoseph Chen 	if (uc_key->irq != irq)
171*5a54baa7SJoseph Chen 		return;
172*5a54baa7SJoseph Chen 
173*5a54baa7SJoseph Chen 	if (irq_get_gpio_level(irq)) {
174*5a54baa7SJoseph Chen 		uc_key->rise_ms = key_timer(0);
175*5a54baa7SJoseph Chen 		KEY_DBG("%s: key dn: %llu ms\n", uc_key->name, uc_key->fall_ms);
176*5a54baa7SJoseph Chen 	} else {
177*5a54baa7SJoseph Chen 		uc_key->fall_ms = key_timer(0);
178*5a54baa7SJoseph Chen 		KEY_DBG("%s: key up: %llu ms\n", uc_key->name, uc_key->rise_ms);
179*5a54baa7SJoseph Chen 	}
180*5a54baa7SJoseph Chen 
181*5a54baa7SJoseph Chen 	/* Must delay */
182*5a54baa7SJoseph Chen 	mdelay(10);
183*5a54baa7SJoseph Chen 	irq_revert_irq_type(irq);
184*5a54baa7SJoseph Chen }
185*5a54baa7SJoseph Chen #endif
186*5a54baa7SJoseph Chen 
187*5a54baa7SJoseph Chen int key_bind_children(struct udevice *dev, const char *drv_name)
188*5a54baa7SJoseph Chen {
189*5a54baa7SJoseph Chen 	const char *name;
190*5a54baa7SJoseph Chen 	ofnode node;
191*5a54baa7SJoseph Chen 	int ret;
192*5a54baa7SJoseph Chen 
193*5a54baa7SJoseph Chen 	dev_for_each_subnode(node, dev) {
194*5a54baa7SJoseph Chen 		/*
195*5a54baa7SJoseph Chen 		 * If this node has "compatible" property, this is not
196*5a54baa7SJoseph Chen 		 * a amp subnode, but a normal device. skip.
197*5a54baa7SJoseph Chen 		 */
198*5a54baa7SJoseph Chen 		ofnode_get_property(node, "compatible", &ret);
199*5a54baa7SJoseph Chen 		if (ret >= 0)
200*5a54baa7SJoseph Chen 			continue;
201*5a54baa7SJoseph Chen 
202*5a54baa7SJoseph Chen 		if (ret != -FDT_ERR_NOTFOUND)
203*5a54baa7SJoseph Chen 			return ret;
204*5a54baa7SJoseph Chen 
205*5a54baa7SJoseph Chen 		name = ofnode_get_name(node);
206*5a54baa7SJoseph Chen 		if (!name)
207*5a54baa7SJoseph Chen 			return -EINVAL;
208*5a54baa7SJoseph Chen 		ret = device_bind_driver_to_node(dev, drv_name, name,
209*5a54baa7SJoseph Chen 						 node, NULL);
210*5a54baa7SJoseph Chen 		if (ret)
211*5a54baa7SJoseph Chen 			return ret;
212*5a54baa7SJoseph Chen 	}
213*5a54baa7SJoseph Chen 
214*5a54baa7SJoseph Chen 	return 0;
215*5a54baa7SJoseph Chen }
216*5a54baa7SJoseph Chen 
217*5a54baa7SJoseph Chen static int key_post_probe(struct udevice *dev)
218*5a54baa7SJoseph Chen {
219*5a54baa7SJoseph Chen 	struct dm_key_uclass_platdata *uc_key;
220*5a54baa7SJoseph Chen 	int margin = 30;
221*5a54baa7SJoseph Chen 	int ret;
222*5a54baa7SJoseph Chen 
223*5a54baa7SJoseph Chen 	uc_key = dev_get_uclass_platdata(dev);
224*5a54baa7SJoseph Chen 	if (!uc_key)
225*5a54baa7SJoseph Chen 		return -ENXIO;
226*5a54baa7SJoseph Chen 
227*5a54baa7SJoseph Chen 	/* True from U-Boot key node */
228*5a54baa7SJoseph Chen 	uc_key->pre_reloc = dev_read_bool(dev, "u-boot,dm-pre-reloc");
229*5a54baa7SJoseph Chen 
230*5a54baa7SJoseph Chen 	if (uc_key->type == ADC_KEY) {
231*5a54baa7SJoseph Chen 		uc_key->max = uc_key->adcval + margin;
232*5a54baa7SJoseph Chen 		uc_key->min = uc_key->adcval > margin ?
233*5a54baa7SJoseph Chen 					uc_key->adcval - margin : 0;
234*5a54baa7SJoseph Chen 	} else {
235*5a54baa7SJoseph Chen 		if (uc_key->code == KEY_POWER) {
236*5a54baa7SJoseph Chen 			/* The gpio irq has been setup by key driver */
237*5a54baa7SJoseph Chen 			if (uc_key->irq)
238*5a54baa7SJoseph Chen 				goto finish;
239*5a54baa7SJoseph Chen #ifdef CONFIG_IRQ
240*5a54baa7SJoseph Chen 			int irq;
241*5a54baa7SJoseph Chen 
242*5a54baa7SJoseph Chen 			irq = phandle_gpio_to_irq(uc_key->gpios[0],
243*5a54baa7SJoseph Chen 						  uc_key->gpios[1]);
244*5a54baa7SJoseph Chen 			if (irq < 0) {
245*5a54baa7SJoseph Chen 				KEY_ERR("%s: failed to request irq, ret=%d\n",
246*5a54baa7SJoseph Chen 					uc_key->name, irq);
247*5a54baa7SJoseph Chen 				return irq;
248*5a54baa7SJoseph Chen 			}
249*5a54baa7SJoseph Chen 
250*5a54baa7SJoseph Chen 			uc_key->irq = irq;
251*5a54baa7SJoseph Chen 			irq_install_handler(irq, gpio_irq_handler, uc_key);
252*5a54baa7SJoseph Chen 			irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
253*5a54baa7SJoseph Chen 			irq_handler_enable(irq);
254*5a54baa7SJoseph Chen #else
255*5a54baa7SJoseph Chen 			KEY_WARN("%s: no IRQ framework available\n", uc_key->name);
256*5a54baa7SJoseph Chen #endif
257*5a54baa7SJoseph Chen 		} else {
258*5a54baa7SJoseph Chen 			ret = gpio_request_by_name(dev, "gpios", 0,
259*5a54baa7SJoseph Chen 						   &uc_key->gpio, GPIOD_IS_IN);
260*5a54baa7SJoseph Chen 			if (ret) {
261*5a54baa7SJoseph Chen 				KEY_ERR("%s: failed to request gpio, ret=%d\n",
262*5a54baa7SJoseph Chen 					uc_key->name, ret);
263*5a54baa7SJoseph Chen 				return ret;
264*5a54baa7SJoseph Chen 			}
2659f1dd9dfSJoseph Chen 		}
266adba3792SJoseph Chen 	}
267adba3792SJoseph Chen 
268*5a54baa7SJoseph Chen finish:
269*5a54baa7SJoseph Chen #ifdef DEBUG
270*5a54baa7SJoseph Chen 	printf("[%s] (%s, %s, %s):\n", uc_key->name,
271*5a54baa7SJoseph Chen 	       uc_key->type == ADC_KEY ? "ADC" : "GPIO",
272*5a54baa7SJoseph Chen 	       uc_key->pre_reloc ? "U-Boot" : "Kernel",
273*5a54baa7SJoseph Chen 	       dev->parent->name);
274*5a54baa7SJoseph Chen 
275*5a54baa7SJoseph Chen 	if (uc_key->type == ADC_KEY) {
276*5a54baa7SJoseph Chen 		printf("    adcval: %d (%d, %d)\n", uc_key->adcval,
277*5a54baa7SJoseph Chen 		       uc_key->min, uc_key->max);
278*5a54baa7SJoseph Chen 		printf("   channel: %d\n\n", uc_key->channel);
279*5a54baa7SJoseph Chen 	} else {
280*5a54baa7SJoseph Chen 		const char *gpio_name =
281*5a54baa7SJoseph Chen 		     ofnode_get_name(ofnode_get_by_phandle(uc_key->gpios[0]));
282*5a54baa7SJoseph Chen 
283*5a54baa7SJoseph Chen 		printf("       irq: %d\n", uc_key->irq);
284*5a54baa7SJoseph Chen 		printf("   gpio[0]: %s\n", gpio_name);
285*5a54baa7SJoseph Chen 		printf("   gpio[1]: %d\n\n", uc_key->gpios[1]);
286*5a54baa7SJoseph Chen 	}
287*5a54baa7SJoseph Chen #endif
288*5a54baa7SJoseph Chen 
289*5a54baa7SJoseph Chen 	return 0;
2903fb84000SJoseph Chen }
2913fb84000SJoseph Chen 
292b398a9a7SJoseph Chen UCLASS_DRIVER(key) = {
293b398a9a7SJoseph Chen 	.id		= UCLASS_KEY,
294b398a9a7SJoseph Chen 	.name		= "key",
295*5a54baa7SJoseph Chen 	.post_probe	= key_post_probe,
296*5a54baa7SJoseph Chen 	.per_device_platdata_auto_alloc_size =
297*5a54baa7SJoseph Chen 			sizeof(struct dm_key_uclass_platdata),
298b398a9a7SJoseph Chen };
299