2017-09-18 11:41:54 +00:00
|
|
|
/*
|
|
|
|
|
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
|
|
|
*/
|
|
|
|
|
|
2018-01-30 07:34:49 +00:00
|
|
|
#include <common.h>
|
2018-07-19 11:43:59 +00:00
|
|
|
#include <adc.h>
|
2018-01-30 07:34:49 +00:00
|
|
|
#include <dm.h>
|
2018-07-19 11:43:59 +00:00
|
|
|
#include <key.h>
|
|
|
|
|
|
|
|
|
|
static LIST_HEAD(key_list);
|
2017-09-18 11:41:54 +00:00
|
|
|
|
2018-12-17 09:19:20 +00:00
|
|
|
const char *evt_name[] = {
|
|
|
|
|
"Not down",
|
|
|
|
|
"Down",
|
|
|
|
|
"Long down",
|
|
|
|
|
"Not exist",
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-30 07:34:49 +00:00
|
|
|
static inline uint64_t arch_counter_get_cntpct(void)
|
2017-09-18 11:41:54 +00:00
|
|
|
{
|
2018-01-30 07:34:49 +00:00
|
|
|
uint64_t cval = 0;
|
2017-09-18 11:41:54 +00:00
|
|
|
|
2018-01-30 07:34:49 +00:00
|
|
|
isb();
|
|
|
|
|
#ifdef CONFIG_ARM64
|
|
|
|
|
asm volatile("mrs %0, cntpct_el0" : "=r" (cval));
|
|
|
|
|
#else
|
|
|
|
|
asm volatile ("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval));
|
|
|
|
|
#endif
|
|
|
|
|
return cval;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-19 11:43:59 +00:00
|
|
|
uint64_t key_timer(uint64_t base)
|
2018-01-30 07:34:49 +00:00
|
|
|
{
|
|
|
|
|
uint64_t cntpct;
|
|
|
|
|
|
|
|
|
|
cntpct = arch_counter_get_cntpct() / 24000UL;
|
|
|
|
|
return (cntpct > base) ? (cntpct - base) : 0;
|
|
|
|
|
}
|
2017-09-18 11:41:54 +00:00
|
|
|
|
2018-07-19 11:43:59 +00:00
|
|
|
/*
|
|
|
|
|
* What's simple and complex event mean?
|
|
|
|
|
*
|
|
|
|
|
* simple event: key press down or none;
|
|
|
|
|
* complext event: key press down, long down or none;
|
|
|
|
|
*/
|
|
|
|
|
static int key_read_adc_simple_event(struct input_key *key, unsigned int adcval)
|
2018-02-28 06:21:44 +00:00
|
|
|
{
|
2018-07-19 11:43:59 +00:00
|
|
|
int max, min, margin = 30;
|
|
|
|
|
int keyval;
|
2018-02-28 06:21:44 +00:00
|
|
|
|
|
|
|
|
/* Get min, max */
|
2018-07-19 11:43:59 +00:00
|
|
|
max = key->adcval + margin;
|
|
|
|
|
if (key->adcval > margin)
|
|
|
|
|
min = key->adcval - margin;
|
2018-02-28 06:21:44 +00:00
|
|
|
else
|
2018-07-19 11:43:59 +00:00
|
|
|
min = 0;
|
2018-02-28 06:21:44 +00:00
|
|
|
|
2018-12-17 09:19:20 +00:00
|
|
|
debug("%s: '%s' configure adc=%d: range[%d~%d]; hw adcval=%d\n",
|
|
|
|
|
__func__, key->name, key->adcval, min, max, adcval);
|
2018-03-06 02:58:23 +00:00
|
|
|
|
2018-02-28 06:21:44 +00:00
|
|
|
/* Check */
|
|
|
|
|
if ((adcval <= max) && (adcval >= min)) {
|
2018-07-19 11:43:59 +00:00
|
|
|
keyval = KEY_PRESS_DOWN;
|
|
|
|
|
debug("%s key pressed..\n", key->name);
|
2018-02-28 06:21:44 +00:00
|
|
|
} else {
|
2018-07-19 11:43:59 +00:00
|
|
|
keyval = KEY_PRESS_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return keyval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int key_read_gpio_simple_event(struct input_key *key)
|
|
|
|
|
{
|
|
|
|
|
if (!dm_gpio_is_valid(&key->gpio)) {
|
|
|
|
|
printf("%s: invalid gpio\n", key->name);
|
|
|
|
|
return KEY_PRESS_NONE;
|
2018-02-28 06:21:44 +00:00
|
|
|
}
|
|
|
|
|
|
2018-07-19 11:43:59 +00:00
|
|
|
return dm_gpio_get_value(&key->gpio) ? KEY_PRESS_DOWN : KEY_PRESS_NONE;
|
2018-02-28 06:21:44 +00:00
|
|
|
}
|
|
|
|
|
|
2018-07-19 11:43:59 +00:00
|
|
|
static int key_read_gpio_complex_event(struct input_key *key)
|
2018-02-28 06:21:44 +00:00
|
|
|
{
|
2018-07-19 11:43:59 +00:00
|
|
|
int keyval;
|
2018-02-28 06:21:44 +00:00
|
|
|
|
|
|
|
|
debug("%s: %s: up=%llu, down=%llu, delta=%llu\n",
|
|
|
|
|
__func__, key->name, key->up_t, key->down_t,
|
|
|
|
|
key->up_t - key->down_t);
|
|
|
|
|
|
|
|
|
|
/* Possible this is machine power-on long pressed, so ignore this */
|
|
|
|
|
if (key->down_t == 0 && key->up_t != 0) {
|
2018-07-19 11:43:59 +00:00
|
|
|
keyval = KEY_PRESS_NONE;
|
2018-02-28 06:21:44 +00:00
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((key->up_t > key->down_t) &&
|
|
|
|
|
(key->up_t - key->down_t) >= KEY_LONG_DOWN_MS) {
|
|
|
|
|
key->up_t = 0;
|
|
|
|
|
key->down_t = 0;
|
2018-07-19 11:43:59 +00:00
|
|
|
keyval = KEY_PRESS_LONG_DOWN;
|
|
|
|
|
debug("%s key long pressed..\n", key->name);
|
2018-02-28 06:21:44 +00:00
|
|
|
} else if (key->down_t &&
|
2018-07-19 11:43:59 +00:00
|
|
|
key_timer(key->down_t) >= KEY_LONG_DOWN_MS) {
|
2018-02-28 06:21:44 +00:00
|
|
|
key->up_t = 0;
|
|
|
|
|
key->down_t = 0;
|
2018-07-19 11:43:59 +00:00
|
|
|
keyval = KEY_PRESS_LONG_DOWN;
|
|
|
|
|
debug("%s key long pressed(hold)..\n", key->name);
|
2018-02-28 06:21:44 +00:00
|
|
|
} else if ((key->up_t > key->down_t) &&
|
|
|
|
|
(key->up_t - key->down_t) < KEY_LONG_DOWN_MS) {
|
|
|
|
|
key->up_t = 0;
|
|
|
|
|
key->down_t = 0;
|
2018-07-19 11:43:59 +00:00
|
|
|
keyval = KEY_PRESS_DOWN;
|
|
|
|
|
debug("%s key short pressed..\n", key->name);
|
|
|
|
|
/* Possible in charge animation, we enable irq after fuel gauge updated */
|
|
|
|
|
} else if (key->up_t && key->down_t && (key->up_t == key->down_t)){
|
|
|
|
|
key->up_t = 0;
|
|
|
|
|
key->down_t = 0;
|
|
|
|
|
keyval = KEY_PRESS_DOWN;
|
|
|
|
|
debug("%s key short pressed..\n", key->name);
|
2018-02-28 06:21:44 +00:00
|
|
|
} else {
|
2018-07-19 11:43:59 +00:00
|
|
|
keyval = KEY_PRESS_NONE;
|
2018-02-28 06:21:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
2018-07-19 11:43:59 +00:00
|
|
|
return keyval;
|
2018-02-28 06:21:44 +00:00
|
|
|
}
|
|
|
|
|
|
2018-07-19 11:43:59 +00:00
|
|
|
static int key_read_gpio_interrupt_event(struct input_key *key)
|
|
|
|
|
{
|
|
|
|
|
debug("%s: %s\n", __func__, key->name);
|
|
|
|
|
|
|
|
|
|
return key_read_gpio_complex_event(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int key_is_pressed(int keyval)
|
|
|
|
|
{
|
|
|
|
|
return (keyval == KEY_PRESS_DOWN || keyval == KEY_PRESS_LONG_DOWN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void key_add(struct input_key *key)
|
|
|
|
|
{
|
|
|
|
|
if (!key)
|
|
|
|
|
return;
|
|
|
|
|
|
2018-12-18 08:13:21 +00:00
|
|
|
if (!key->parent) {
|
|
|
|
|
printf("Err: Can't find key(code=%d) device\n", key->code);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key->pre_reloc = dev_read_bool(key->parent, "u-boot,dm-pre-reloc");
|
2018-07-19 11:43:59 +00:00
|
|
|
list_add_tail(&key->link, &key_list);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-18 08:13:21 +00:00
|
|
|
static int __key_read(struct input_key *key)
|
|
|
|
|
{
|
|
|
|
|
unsigned int adcval;
|
|
|
|
|
int keyval = KEY_NOT_EXIST;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
/* Is a adc key? */
|
|
|
|
|
if (key->type & ADC_KEY) {
|
|
|
|
|
ret = adc_channel_single_shot("saradc",
|
|
|
|
|
key->channel, &adcval);
|
|
|
|
|
if (ret)
|
|
|
|
|
printf("%s: failed to read saradc, ret=%d\n",
|
|
|
|
|
key->name, ret);
|
|
|
|
|
else
|
|
|
|
|
keyval = key_read_adc_simple_event(key, adcval);
|
|
|
|
|
/* Is a gpio key? */
|
|
|
|
|
} else if (key->type & GPIO_KEY) {
|
|
|
|
|
/* All pwrkey must register as an interrupt event */
|
|
|
|
|
if (key->code == KEY_POWER)
|
|
|
|
|
keyval = key_read_gpio_interrupt_event(key);
|
|
|
|
|
else
|
|
|
|
|
keyval = key_read_gpio_simple_event(key);
|
|
|
|
|
} else {
|
|
|
|
|
printf("%s: invalid key type!\n", __func__);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
debug("%s: '%s'(code=%d) is %s\n",
|
|
|
|
|
__func__, key->name, key->code, evt_name[keyval]);
|
|
|
|
|
|
|
|
|
|
return keyval;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-19 11:43:59 +00:00
|
|
|
int key_read(int code)
|
2017-10-27 09:42:18 +00:00
|
|
|
{
|
2018-01-30 07:34:49 +00:00
|
|
|
struct udevice *dev;
|
2018-07-19 11:43:59 +00:00
|
|
|
struct input_key *key;
|
|
|
|
|
static int initialized;
|
|
|
|
|
int keyval = KEY_NOT_EXIST;
|
|
|
|
|
|
|
|
|
|
/* Initialize all key drivers */
|
|
|
|
|
if (!initialized) {
|
|
|
|
|
for (uclass_first_device(UCLASS_KEY, &dev);
|
|
|
|
|
dev;
|
|
|
|
|
uclass_next_device(&dev)) {
|
2018-12-17 09:19:20 +00:00
|
|
|
debug("%s: have found key driver '%s'\n\n",
|
|
|
|
|
__func__, dev->name);
|
2018-07-19 11:43:59 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-18 08:13:21 +00:00
|
|
|
/* The key from kernel dtb has higher priority */
|
|
|
|
|
debug("Reading key from kernel\n");
|
2018-07-19 11:43:59 +00:00
|
|
|
list_for_each_entry(key, &key_list, link) {
|
2018-12-18 08:13:21 +00:00
|
|
|
if (key->pre_reloc || (key->code != code))
|
2018-12-17 09:19:20 +00:00
|
|
|
continue;
|
|
|
|
|
|
2018-12-18 08:13:21 +00:00
|
|
|
keyval = __key_read(key);
|
|
|
|
|
if (key_is_pressed(keyval))
|
|
|
|
|
return keyval;
|
|
|
|
|
}
|
2017-10-27 09:42:18 +00:00
|
|
|
|
2018-12-18 08:13:21 +00:00
|
|
|
/* If not found any key from kernel dtb, reading from U-Boot dtb */
|
|
|
|
|
if (keyval == KEY_NOT_EXIST) {
|
|
|
|
|
debug("Reading key from U-Boot\n");
|
|
|
|
|
list_for_each_entry(key, &key_list, link) {
|
|
|
|
|
if (!key->pre_reloc || (key->code != code))
|
|
|
|
|
continue;
|
2018-12-17 09:19:20 +00:00
|
|
|
|
2018-12-18 08:13:21 +00:00
|
|
|
keyval = __key_read(key);
|
|
|
|
|
if (key_is_pressed(keyval))
|
|
|
|
|
return keyval;
|
|
|
|
|
}
|
2018-12-17 09:19:20 +00:00
|
|
|
}
|
2018-07-19 11:43:59 +00:00
|
|
|
|
|
|
|
|
return keyval;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-18 11:41:54 +00:00
|
|
|
UCLASS_DRIVER(key) = {
|
|
|
|
|
.id = UCLASS_KEY,
|
|
|
|
|
.name = "key",
|
|
|
|
|
};
|