dm: key: refactor code
- use standard dm framework to implement all key drivers; - all key node to be children and attach to key bus; - dm key uclass takes over most work; - reduce a lot of driver code size; Change-Id: I9ea4515249d493eb4434890b90350f694c07404f Signed-off-by: Joseph Chen <chenjh@rock-chips.com>
This commit is contained in:
parent
a0ceee9227
commit
5a54baa793
|
|
@ -47,7 +47,7 @@ config I8042_KEYB
|
|||
|
||||
config RK8XX_PWRKEY
|
||||
bool "Enable RK805/816/817 pwrkey support"
|
||||
depends on DM_KEY && PMIC_RK8XX
|
||||
depends on DM_KEY && PMIC_RK8XX && IRQ
|
||||
help
|
||||
This adds a driver for the RK805/816/817 pwrkey support.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,92 +4,77 @@
|
|||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include <dm/read.h>
|
||||
#include <adc.h>
|
||||
#include <common.h>
|
||||
#include <console.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <malloc.h>
|
||||
#include <dm.h>
|
||||
#include <key.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
static int adc_keys_ofdata_to_platdata(struct udevice *dev)
|
||||
static int adc_key_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct input_key *key;
|
||||
u32 adc_channels[2], microvolt;
|
||||
struct dm_key_uclass_platdata *uc_key;
|
||||
u32 chn[2], mV;
|
||||
int vref, ret;
|
||||
ofnode node;
|
||||
|
||||
/* Get vref */
|
||||
vref = dev_read_u32_default(dev, "keyup-threshold-microvolt", -1);
|
||||
if (vref < 0) {
|
||||
printf("failed to read 'keyup-threshold-microvolt', ret=%d\n",
|
||||
vref);
|
||||
return -EINVAL;
|
||||
}
|
||||
uc_key = dev_get_uclass_platdata(dev);
|
||||
if (!uc_key)
|
||||
return -ENXIO;
|
||||
|
||||
/* Get IO channel */
|
||||
ret = dev_read_u32_array(dev, "io-channels", adc_channels, 2);
|
||||
uc_key->type = ADC_KEY;
|
||||
uc_key->name = dev_read_string(dev, "label");
|
||||
ret = dev_read_u32_array(dev_get_parent(dev),
|
||||
"io-channels", chn, ARRAY_SIZE(chn));
|
||||
if (ret) {
|
||||
printf("failed to read 'io-channels', ret=%d\n", ret);
|
||||
printf("%s: read 'io-channels' failed, ret=%d\n",
|
||||
uc_key->name, ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Parse every adc key data */
|
||||
dev_for_each_subnode(node, dev) {
|
||||
key = calloc(1, sizeof(struct input_key));
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
|
||||
key->parent = dev;
|
||||
key->type = ADC_KEY;
|
||||
key->vref = vref;
|
||||
key->channel = adc_channels[1];
|
||||
key->name = ofnode_read_string(node, "label");
|
||||
ret = ofnode_read_u32(node, "linux,code", &key->code);
|
||||
if (ret) {
|
||||
printf("%s: failed to read 'linux,code', ret=%d\n",
|
||||
key->name, ret);
|
||||
free(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = ofnode_read_u32(node, "press-threshold-microvolt",
|
||||
µvolt);
|
||||
if (ret) {
|
||||
printf("%s: failed to read 'press-threshold-microvolt', ret=%d\n",
|
||||
key->name, ret);
|
||||
free(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Convert microvolt to adc value */
|
||||
key->adcval = microvolt / (key->vref / 1024);
|
||||
key_add(key);
|
||||
|
||||
debug("%s: name=%s: code=%d, vref=%d, channel=%d, microvolt=%d, adcval=%d\n",
|
||||
__func__, key->name, key->code, key->vref,
|
||||
key->channel, microvolt, key->adcval);
|
||||
vref = dev_read_u32_default(dev_get_parent(dev),
|
||||
"keyup-threshold-microvolt", -ENODATA);
|
||||
if (vref < 0) {
|
||||
printf("%s: read 'keyup-threshold-microvolt' failed, ret=%d\n",
|
||||
uc_key->name, vref);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uc_key->code = dev_read_u32_default(dev, "linux,code", -ENODATA);
|
||||
if (uc_key->code < 0) {
|
||||
printf("%s: read 'linux,code' failed\n", uc_key->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mV = dev_read_u32_default(dev, "press-threshold-microvolt", -ENODATA);
|
||||
if (mV < 0) {
|
||||
printf("%s: read 'press-threshold-microvolt' failed\n",
|
||||
uc_key->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uc_key->channel = chn[1];
|
||||
uc_key->adcval = mV / (vref / 1024);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_key_ops key_ops = {
|
||||
.name = "adc-keys",
|
||||
U_BOOT_DRIVER(adc_key) = {
|
||||
.name = "adc_key",
|
||||
.id = UCLASS_KEY,
|
||||
.ofdata_to_platdata = adc_key_ofdata_to_platdata,
|
||||
};
|
||||
|
||||
static const struct udevice_id adc_keys_ids[] = {
|
||||
/* Key Bus */
|
||||
static int adc_key_bus_bind(struct udevice *dev)
|
||||
{
|
||||
return key_bind_children(dev, "adc_key");
|
||||
}
|
||||
|
||||
static const struct udevice_id adc_key_bus_match[] = {
|
||||
{ .compatible = "adc-keys" },
|
||||
{ },
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(adc_keys) = {
|
||||
.name = "adc-keys",
|
||||
.id = UCLASS_KEY,
|
||||
.ops = &key_ops,
|
||||
.of_match = adc_keys_ids,
|
||||
.ofdata_to_platdata = adc_keys_ofdata_to_platdata,
|
||||
U_BOOT_DRIVER(adc_key_bus) = {
|
||||
.name = "adc_key_bus",
|
||||
.id = UCLASS_SIMPLE_BUS,
|
||||
.of_match = adc_key_bus_match,
|
||||
.bind = adc_key_bus_bind,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,119 +4,55 @@
|
|||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include <adc.h>
|
||||
#include <common.h>
|
||||
#include <console.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <malloc.h>
|
||||
#include <key.h>
|
||||
#include <linux/input.h>
|
||||
#include <errno.h>
|
||||
#include <dm/read.h>
|
||||
#include <irq-generic.h>
|
||||
#include <irq-platform.h>
|
||||
|
||||
static void gpio_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct input_key *key = data;
|
||||
|
||||
if (key->irq != irq)
|
||||
return;
|
||||
|
||||
/* up event */
|
||||
if (irq_get_gpio_level(irq)) {
|
||||
key->up_t = key_timer(0);
|
||||
debug("%s: key down: %llu ms\n", key->name, key->down_t);
|
||||
/* down event */
|
||||
} else {
|
||||
key->down_t = key_timer(0);
|
||||
debug("%s: key up: %llu ms\n", key->name, key->up_t);
|
||||
}
|
||||
/* Must delay */
|
||||
mdelay(10);
|
||||
irq_revert_irq_type(irq);
|
||||
}
|
||||
|
||||
static int gpio_key_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct input_key *key;
|
||||
u32 gpios[2];
|
||||
ofnode node;
|
||||
int irq, ret;
|
||||
struct dm_key_uclass_platdata *uc_key;
|
||||
|
||||
dev_for_each_subnode(node, dev) {
|
||||
key = calloc(1, sizeof(struct input_key));
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
uc_key = dev_get_uclass_platdata(dev);
|
||||
if (!uc_key)
|
||||
return -ENXIO;
|
||||
|
||||
key->parent = dev;
|
||||
key->type = GPIO_KEY;
|
||||
key->name = ofnode_read_string(node, "label");
|
||||
ret = ofnode_read_u32(node, "linux,code", &key->code);
|
||||
if (ret) {
|
||||
printf("%s: failed read 'linux,code', ret=%d\n",
|
||||
key->name, ret);
|
||||
free(key);
|
||||
continue;
|
||||
}
|
||||
uc_key->type = GPIO_KEY;
|
||||
uc_key->name = dev_read_string(dev, "label");
|
||||
uc_key->code = dev_read_u32_default(dev, "linux,code", -ENODATA);
|
||||
if (uc_key->code < 0) {
|
||||
printf("%s: read 'linux,code' failed\n", uc_key->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Only register power key as interrupt */
|
||||
if (key->code == KEY_POWER) {
|
||||
ret = ofnode_read_u32_array(node, "gpios", gpios, 2);
|
||||
if (ret) {
|
||||
printf("%s: failed to read 'gpios', ret=%d\n",
|
||||
key->name, ret);
|
||||
free(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Must register as interrupt, be able to wakeup system */
|
||||
irq = phandle_gpio_to_irq(gpios[0], gpios[1]);
|
||||
if (irq < 0) {
|
||||
printf("%s: failed to request irq, ret=%d\n",
|
||||
key->name, irq);
|
||||
free(key);
|
||||
continue;
|
||||
}
|
||||
key->irq = irq;
|
||||
key_add(key);
|
||||
irq_install_handler(irq, gpio_irq_handler, key);
|
||||
irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
|
||||
irq_handler_enable(irq);
|
||||
} else {
|
||||
ret = gpio_request_by_name_nodev(node, "gpios", 0,
|
||||
&key->gpio,
|
||||
GPIOD_IS_IN);
|
||||
if (ret) {
|
||||
printf("%s: failed to request gpio, ret=%d\n",
|
||||
key->name, ret);
|
||||
}
|
||||
|
||||
key_add(key);
|
||||
}
|
||||
|
||||
debug("%s: name=%s: code=%d\n", __func__, key->name, key->code);
|
||||
if (dev_read_u32_array(dev, "gpios",
|
||||
uc_key->gpios, ARRAY_SIZE(uc_key->gpios))) {
|
||||
printf("%s: read 'gpios' failed\n", uc_key->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_key_ops key_ops = {
|
||||
.name = "gpio-keys",
|
||||
U_BOOT_DRIVER(gpio_key) = {
|
||||
.name = "gpio_key",
|
||||
.id = UCLASS_KEY,
|
||||
.ofdata_to_platdata = gpio_key_ofdata_to_platdata,
|
||||
};
|
||||
|
||||
static const struct udevice_id gpio_key_ids[] = {
|
||||
/* Key Bus */
|
||||
static int gpio_key_bus_bind(struct udevice *dev)
|
||||
{
|
||||
return key_bind_children(dev, "gpio_key");
|
||||
}
|
||||
|
||||
static const struct udevice_id gpio_key_bus_match[] = {
|
||||
{ .compatible = "gpio-keys" },
|
||||
{ },
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(gpio_keys) = {
|
||||
.name = "gpio-keys",
|
||||
.id = UCLASS_KEY,
|
||||
.of_match = gpio_key_ids,
|
||||
.ops = &key_ops,
|
||||
.ofdata_to_platdata = gpio_key_ofdata_to_platdata,
|
||||
U_BOOT_DRIVER(gpio_key_bus) = {
|
||||
.name = "gpio_key_bus",
|
||||
.id = UCLASS_SIMPLE_BUS,
|
||||
.of_match = gpio_key_bus_match,
|
||||
.bind = gpio_key_bus_bind,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,15 +8,12 @@
|
|||
#include <adc.h>
|
||||
#include <dm.h>
|
||||
#include <key.h>
|
||||
#include <dm/lists.h>
|
||||
#include <irq-generic.h>
|
||||
|
||||
static LIST_HEAD(key_list);
|
||||
|
||||
const char *evt_name[] = {
|
||||
"Not down",
|
||||
"Down",
|
||||
"Long down",
|
||||
"Not exist",
|
||||
};
|
||||
#define KEY_WARN(fmt, args...) printf("Key Warn: "fmt, ##args)
|
||||
#define KEY_ERR(fmt, args...) printf("Key Error: "fmt, ##args)
|
||||
#define KEY_DBG(fmt, args...) debug("Key Debug: "fmt, ##args)
|
||||
|
||||
static inline uint64_t arch_counter_get_cntpct(void)
|
||||
{
|
||||
|
|
@ -26,7 +23,7 @@ static inline uint64_t arch_counter_get_cntpct(void)
|
|||
#ifdef CONFIG_ARM64
|
||||
asm volatile("mrs %0, cntpct_el0" : "=r" (cval));
|
||||
#else
|
||||
asm volatile ("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval));
|
||||
asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval));
|
||||
#endif
|
||||
return cval;
|
||||
}
|
||||
|
|
@ -39,197 +36,263 @@ uint64_t key_timer(uint64_t base)
|
|||
return (cntpct > base) ? (cntpct - base) : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
static int key_adc_event(struct dm_key_uclass_platdata *uc_key, int adcval)
|
||||
{
|
||||
int max, min, margin = 30;
|
||||
int keyval;
|
||||
|
||||
/* Get min, max */
|
||||
max = key->adcval + margin;
|
||||
if (key->adcval > margin)
|
||||
min = key->adcval - margin;
|
||||
else
|
||||
min = 0;
|
||||
|
||||
debug("%s: '%s' configure adc=%d: range[%d~%d]; hw adcval=%d\n",
|
||||
__func__, key->name, key->adcval, min, max, adcval);
|
||||
|
||||
/* Check */
|
||||
if ((adcval <= max) && (adcval >= min)) {
|
||||
keyval = KEY_PRESS_DOWN;
|
||||
debug("%s key pressed..\n", key->name);
|
||||
} else {
|
||||
keyval = KEY_PRESS_NONE;
|
||||
}
|
||||
|
||||
return keyval;
|
||||
return (adcval <= uc_key->max && adcval >= uc_key->min) ?
|
||||
KEY_PRESS_DOWN : KEY_PRESS_NONE;
|
||||
}
|
||||
|
||||
static int key_read_gpio_simple_event(struct input_key *key)
|
||||
static int key_gpio_event(struct dm_key_uclass_platdata *uc_key)
|
||||
{
|
||||
if (!dm_gpio_is_valid(&key->gpio)) {
|
||||
printf("%s: invalid gpio\n", key->name);
|
||||
if (!dm_gpio_is_valid(&uc_key->gpio)) {
|
||||
KEY_ERR("'%s' Invalid gpio\n", uc_key->name);
|
||||
return KEY_PRESS_NONE;
|
||||
}
|
||||
|
||||
return dm_gpio_get_value(&key->gpio) ? KEY_PRESS_DOWN : KEY_PRESS_NONE;
|
||||
return dm_gpio_get_value(&uc_key->gpio) ?
|
||||
KEY_PRESS_DOWN : KEY_PRESS_NONE;
|
||||
}
|
||||
|
||||
static int key_read_gpio_complex_event(struct input_key *key)
|
||||
static int key_gpio_interrupt_event(struct dm_key_uclass_platdata *uc_key)
|
||||
{
|
||||
int keyval;
|
||||
int event;
|
||||
|
||||
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);
|
||||
__func__, uc_key->name, uc_key->rise_ms, uc_key->fall_ms,
|
||||
uc_key->rise_ms - uc_key->fall_ms);
|
||||
|
||||
/* Possible this is machine power-on long pressed, so ignore this */
|
||||
if (key->down_t == 0 && key->up_t != 0) {
|
||||
keyval = KEY_PRESS_NONE;
|
||||
if (uc_key->fall_ms == 0 && uc_key->rise_ms != 0) {
|
||||
event = KEY_PRESS_NONE;
|
||||
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;
|
||||
keyval = KEY_PRESS_LONG_DOWN;
|
||||
debug("%s key long pressed..\n", key->name);
|
||||
} else if (key->down_t &&
|
||||
key_timer(key->down_t) >= KEY_LONG_DOWN_MS) {
|
||||
key->up_t = 0;
|
||||
key->down_t = 0;
|
||||
keyval = KEY_PRESS_LONG_DOWN;
|
||||
debug("%s key long pressed(hold)..\n", key->name);
|
||||
} 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;
|
||||
keyval = KEY_PRESS_DOWN;
|
||||
debug("%s key short pressed..\n", key->name);
|
||||
if ((uc_key->rise_ms > uc_key->fall_ms) &&
|
||||
(uc_key->rise_ms - uc_key->fall_ms) >= KEY_LONG_DOWN_MS) {
|
||||
uc_key->rise_ms = 0;
|
||||
uc_key->fall_ms = 0;
|
||||
event = KEY_PRESS_LONG_DOWN;
|
||||
KEY_DBG("%s key long pressed..\n", uc_key->name);
|
||||
} else if (uc_key->fall_ms &&
|
||||
key_timer(uc_key->fall_ms) >= KEY_LONG_DOWN_MS) {
|
||||
uc_key->rise_ms = 0;
|
||||
uc_key->fall_ms = 0;
|
||||
event = KEY_PRESS_LONG_DOWN;
|
||||
KEY_DBG("%s key long pressed(hold)..\n", uc_key->name);
|
||||
} else if ((uc_key->rise_ms > uc_key->fall_ms) &&
|
||||
(uc_key->rise_ms - uc_key->fall_ms) < KEY_LONG_DOWN_MS) {
|
||||
uc_key->rise_ms = 0;
|
||||
uc_key->fall_ms = 0;
|
||||
event = KEY_PRESS_DOWN;
|
||||
KEY_DBG("%s key short pressed..\n", uc_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);
|
||||
} else if (uc_key->rise_ms && uc_key->fall_ms &&
|
||||
(uc_key->rise_ms == uc_key->fall_ms)) {
|
||||
uc_key->rise_ms = 0;
|
||||
uc_key->fall_ms = 0;
|
||||
event = KEY_PRESS_DOWN;
|
||||
KEY_DBG("%s key short pressed..\n", uc_key->name);
|
||||
} else {
|
||||
keyval = KEY_PRESS_NONE;
|
||||
event = KEY_PRESS_NONE;
|
||||
}
|
||||
|
||||
out:
|
||||
return keyval;
|
||||
return event;
|
||||
}
|
||||
|
||||
static int key_read_gpio_interrupt_event(struct input_key *key)
|
||||
int key_is_pressed(int event)
|
||||
{
|
||||
debug("%s: %s\n", __func__, key->name);
|
||||
|
||||
return key_read_gpio_complex_event(key);
|
||||
return (event == KEY_PRESS_DOWN || event == KEY_PRESS_LONG_DOWN);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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");
|
||||
list_add_tail(&key->link, &key_list);
|
||||
}
|
||||
|
||||
static int __key_read(struct input_key *key)
|
||||
static int key_core_read(struct dm_key_uclass_platdata *uc_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__);
|
||||
if (uc_key->type == ADC_KEY) {
|
||||
if (adc_channel_single_shot("saradc",
|
||||
uc_key->channel, &adcval)) {
|
||||
KEY_ERR("%s failed to read saradc\n", uc_key->name);
|
||||
return KEY_NOT_EXIST;
|
||||
}
|
||||
|
||||
return key_adc_event(uc_key, adcval);
|
||||
}
|
||||
|
||||
debug("%s: '%s'(code=%d) is %s\n",
|
||||
__func__, key->name, key->code, evt_name[keyval]);
|
||||
|
||||
return keyval;
|
||||
return (uc_key->code == KEY_POWER) ?
|
||||
key_gpio_interrupt_event(uc_key) :
|
||||
key_gpio_event(uc_key);
|
||||
}
|
||||
|
||||
int key_read(int code)
|
||||
{
|
||||
struct dm_key_uclass_platdata *uc_key;
|
||||
struct udevice *dev;
|
||||
struct input_key *key;
|
||||
static int initialized;
|
||||
int keyval = KEY_NOT_EXIST;
|
||||
struct uclass *uc;
|
||||
bool allow_pre_reloc = false;
|
||||
int ret, event = KEY_NOT_EXIST;
|
||||
|
||||
/* Initialize all key drivers */
|
||||
if (!initialized) {
|
||||
for (uclass_first_device(UCLASS_KEY, &dev);
|
||||
dev;
|
||||
uclass_next_device(&dev)) {
|
||||
debug("%s: have found key driver '%s'\n\n",
|
||||
__func__, dev->name);
|
||||
}
|
||||
}
|
||||
ret = uclass_get(UCLASS_KEY, &uc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* The key from kernel dtb has higher priority */
|
||||
debug("Reading key from kernel\n");
|
||||
list_for_each_entry(key, &key_list, link) {
|
||||
if (key->pre_reloc || (key->code != code))
|
||||
try_again:
|
||||
for (uclass_first_device(UCLASS_KEY, &dev);
|
||||
dev;
|
||||
uclass_next_device(&dev)) {
|
||||
uc_key = dev_get_uclass_platdata(dev);
|
||||
|
||||
if (!allow_pre_reloc && uc_key->pre_reloc)
|
||||
continue;
|
||||
|
||||
keyval = __key_read(key);
|
||||
if (key_is_pressed(keyval))
|
||||
return keyval;
|
||||
if (uc_key->code != code)
|
||||
continue;
|
||||
|
||||
event = key_core_read(uc_key);
|
||||
if (key_is_pressed(event))
|
||||
return event;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
/* If not find valid key node from kernel, try from u-boot */
|
||||
if (event == KEY_NOT_EXIST && !allow_pre_reloc) {
|
||||
allow_pre_reloc = true;
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
keyval = __key_read(key);
|
||||
if (key_is_pressed(keyval))
|
||||
return keyval;
|
||||
return event;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IRQ
|
||||
static void gpio_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct dm_key_uclass_platdata *uc_key = data;
|
||||
|
||||
if (uc_key->irq != irq)
|
||||
return;
|
||||
|
||||
if (irq_get_gpio_level(irq)) {
|
||||
uc_key->rise_ms = key_timer(0);
|
||||
KEY_DBG("%s: key dn: %llu ms\n", uc_key->name, uc_key->fall_ms);
|
||||
} else {
|
||||
uc_key->fall_ms = key_timer(0);
|
||||
KEY_DBG("%s: key up: %llu ms\n", uc_key->name, uc_key->rise_ms);
|
||||
}
|
||||
|
||||
/* Must delay */
|
||||
mdelay(10);
|
||||
irq_revert_irq_type(irq);
|
||||
}
|
||||
#endif
|
||||
|
||||
int key_bind_children(struct udevice *dev, const char *drv_name)
|
||||
{
|
||||
const char *name;
|
||||
ofnode node;
|
||||
int ret;
|
||||
|
||||
dev_for_each_subnode(node, dev) {
|
||||
/*
|
||||
* If this node has "compatible" property, this is not
|
||||
* a amp subnode, but a normal device. skip.
|
||||
*/
|
||||
ofnode_get_property(node, "compatible", &ret);
|
||||
if (ret >= 0)
|
||||
continue;
|
||||
|
||||
if (ret != -FDT_ERR_NOTFOUND)
|
||||
return ret;
|
||||
|
||||
name = ofnode_get_name(node);
|
||||
if (!name)
|
||||
return -EINVAL;
|
||||
ret = device_bind_driver_to_node(dev, drv_name, name,
|
||||
node, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int key_post_probe(struct udevice *dev)
|
||||
{
|
||||
struct dm_key_uclass_platdata *uc_key;
|
||||
int margin = 30;
|
||||
int ret;
|
||||
|
||||
uc_key = dev_get_uclass_platdata(dev);
|
||||
if (!uc_key)
|
||||
return -ENXIO;
|
||||
|
||||
/* True from U-Boot key node */
|
||||
uc_key->pre_reloc = dev_read_bool(dev, "u-boot,dm-pre-reloc");
|
||||
|
||||
if (uc_key->type == ADC_KEY) {
|
||||
uc_key->max = uc_key->adcval + margin;
|
||||
uc_key->min = uc_key->adcval > margin ?
|
||||
uc_key->adcval - margin : 0;
|
||||
} else {
|
||||
if (uc_key->code == KEY_POWER) {
|
||||
/* The gpio irq has been setup by key driver */
|
||||
if (uc_key->irq)
|
||||
goto finish;
|
||||
#ifdef CONFIG_IRQ
|
||||
int irq;
|
||||
|
||||
irq = phandle_gpio_to_irq(uc_key->gpios[0],
|
||||
uc_key->gpios[1]);
|
||||
if (irq < 0) {
|
||||
KEY_ERR("%s: failed to request irq, ret=%d\n",
|
||||
uc_key->name, irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
uc_key->irq = irq;
|
||||
irq_install_handler(irq, gpio_irq_handler, uc_key);
|
||||
irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
|
||||
irq_handler_enable(irq);
|
||||
#else
|
||||
KEY_WARN("%s: no IRQ framework available\n", uc_key->name);
|
||||
#endif
|
||||
} else {
|
||||
ret = gpio_request_by_name(dev, "gpios", 0,
|
||||
&uc_key->gpio, GPIOD_IS_IN);
|
||||
if (ret) {
|
||||
KEY_ERR("%s: failed to request gpio, ret=%d\n",
|
||||
uc_key->name, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return keyval;
|
||||
finish:
|
||||
#ifdef DEBUG
|
||||
printf("[%s] (%s, %s, %s):\n", uc_key->name,
|
||||
uc_key->type == ADC_KEY ? "ADC" : "GPIO",
|
||||
uc_key->pre_reloc ? "U-Boot" : "Kernel",
|
||||
dev->parent->name);
|
||||
|
||||
if (uc_key->type == ADC_KEY) {
|
||||
printf(" adcval: %d (%d, %d)\n", uc_key->adcval,
|
||||
uc_key->min, uc_key->max);
|
||||
printf(" channel: %d\n\n", uc_key->channel);
|
||||
} else {
|
||||
const char *gpio_name =
|
||||
ofnode_get_name(ofnode_get_by_phandle(uc_key->gpios[0]));
|
||||
|
||||
printf(" irq: %d\n", uc_key->irq);
|
||||
printf(" gpio[0]: %s\n", gpio_name);
|
||||
printf(" gpio[1]: %d\n\n", uc_key->gpios[1]);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(key) = {
|
||||
.id = UCLASS_KEY,
|
||||
.name = "key",
|
||||
.post_probe = key_post_probe,
|
||||
.per_device_platdata_auto_alloc_size =
|
||||
sizeof(struct dm_key_uclass_platdata),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ static void pwrkey_irq_handler(int irq, void *data)
|
|||
{
|
||||
struct udevice *dev = data;
|
||||
struct rk8xx_key_priv *priv = dev_get_priv(dev);
|
||||
struct input_key *key = dev_get_platdata(dev);
|
||||
struct dm_key_uclass_platdata *uc_key = dev_get_uclass_platdata(dev);
|
||||
int ret, val, i;
|
||||
|
||||
debug("%s: irq = %d\n", __func__, irq);
|
||||
|
|
@ -135,14 +135,14 @@ static void pwrkey_irq_handler(int irq, void *data)
|
|||
|
||||
/* fall event */
|
||||
if (val & priv->pwron_fall_int) {
|
||||
key->down_t = key_timer(0);
|
||||
debug("%s: key down: %llu ms\n", __func__, key->down_t);
|
||||
uc_key->fall_ms = key_timer(0);
|
||||
debug("%s: key down: %llu ms\n", __func__, uc_key->fall_ms);
|
||||
}
|
||||
|
||||
/* rise event */
|
||||
if (val & priv->pwron_rise_int) {
|
||||
key->up_t = key_timer(0);
|
||||
debug("%s: key up: %llu ms\n", __func__, key->up_t);
|
||||
uc_key->rise_ms = key_timer(0);
|
||||
debug("%s: key up: %llu ms\n", __func__, uc_key->rise_ms);
|
||||
}
|
||||
|
||||
/* clear intertup */
|
||||
|
|
@ -162,33 +162,31 @@ static void pwrkey_irq_handler(int irq, void *data)
|
|||
|
||||
static int pwrkey_interrupt_init(struct udevice *dev)
|
||||
{
|
||||
struct input_key *key = dev_get_platdata(dev);
|
||||
struct dm_key_uclass_platdata *uc_key = dev_get_uclass_platdata(dev);
|
||||
u32 interrupt[2], phandle;
|
||||
int irq, ret;
|
||||
|
||||
phandle = dev_read_u32_default(dev->parent, "interrupt-parent", -1);
|
||||
if (phandle < 0) {
|
||||
printf("failed get 'interrupt-parent', ret=%d\n", phandle);
|
||||
printf("read 'interrupt-parent' failed, ret=%d\n", phandle);
|
||||
return phandle;
|
||||
}
|
||||
|
||||
ret = dev_read_u32_array(dev->parent, "interrupts", interrupt, 2);
|
||||
if (ret) {
|
||||
printf("failed get 'interrupt', ret=%d\n", ret);
|
||||
printf("read 'interrupt' failed, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
key->parent = dev;
|
||||
key->name = "rk8xx_pwrkey";
|
||||
key->code = KEY_POWER;
|
||||
key->type = GPIO_KEY;
|
||||
uc_key->name = "rk8xx_pwr";
|
||||
uc_key->type = GPIO_KEY;
|
||||
uc_key->code = KEY_POWER;
|
||||
irq = phandle_gpio_to_irq(phandle, interrupt[0]);
|
||||
if (irq < 0) {
|
||||
printf("%s: failed to request irq, ret=%d\n", key->name, irq);
|
||||
printf("%s: request irq failed, ret=%d\n", uc_key->name, irq);
|
||||
return irq;
|
||||
}
|
||||
key->irq = irq;
|
||||
key_add(key);
|
||||
uc_key->irq = irq;
|
||||
irq_install_handler(irq, pwrkey_irq_handler, dev);
|
||||
irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
|
||||
irq_handler_enable(irq);
|
||||
|
|
@ -196,10 +194,6 @@ static int pwrkey_interrupt_init(struct udevice *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_key_ops key_ops = {
|
||||
.name = "rk8xx-pwrkey",
|
||||
};
|
||||
|
||||
static int rk8xx_pwrkey_probe(struct udevice *dev)
|
||||
{
|
||||
struct rk8xx_priv *rk8xx = dev_get_priv(dev->parent);
|
||||
|
|
@ -270,8 +264,6 @@ static int rk8xx_pwrkey_probe(struct udevice *dev)
|
|||
U_BOOT_DRIVER(rk8xx_pwrkey) = {
|
||||
.name = "rk8xx_pwrkey",
|
||||
.id = UCLASS_KEY,
|
||||
.ops = &key_ops,
|
||||
.probe = rk8xx_pwrkey_probe,
|
||||
.platdata_auto_alloc_size = sizeof(struct input_key),
|
||||
.priv_auto_alloc_size = sizeof(struct rk8xx_key_priv),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,145 +4,65 @@
|
|||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include <adc.h>
|
||||
#include <common.h>
|
||||
#include <console.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <malloc.h>
|
||||
#include <key.h>
|
||||
#include <linux/input.h>
|
||||
#include <errno.h>
|
||||
#include <dm/read.h>
|
||||
#include <irq-generic.h>
|
||||
#include <irq-platform.h>
|
||||
|
||||
static void gpio_irq_handler(int irq, void *data)
|
||||
static int rk_key_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct input_key *key = data;
|
||||
struct dm_key_uclass_platdata *uc_key;
|
||||
u32 chn[2];
|
||||
|
||||
if (key->irq != irq)
|
||||
return;
|
||||
uc_key = dev_get_uclass_platdata(dev);
|
||||
if (!uc_key)
|
||||
return -ENXIO;
|
||||
|
||||
/* up event */
|
||||
if (irq_get_gpio_level(irq)) {
|
||||
key->up_t = key_timer(0);
|
||||
debug("%s: key down: %llu ms\n", key->name, key->down_t);
|
||||
/* down event */
|
||||
} else {
|
||||
key->down_t = key_timer(0);
|
||||
debug("%s: key up: %llu ms\n", key->name, key->up_t);
|
||||
}
|
||||
/* Must delay */
|
||||
mdelay(10);
|
||||
irq_revert_irq_type(irq);
|
||||
}
|
||||
uc_key->name = dev_read_string(dev, "label");
|
||||
uc_key->code = dev_read_u32_default(dev, "linux,code", -ENODATA);
|
||||
|
||||
static int rk_keys_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct input_key *key;
|
||||
u32 adc_channels[2], gpios[2], adcval;
|
||||
int irq, ret;
|
||||
ofnode node;
|
||||
|
||||
/* Get IO channel */
|
||||
if (dev_read_u32_array(dev, "io-channels", adc_channels, 2)) {
|
||||
printf("%s: failed to read 'io-channels'\n", __func__);
|
||||
if (dev_read_u32_array(dev_get_parent(dev), "io-channels", chn, 2)) {
|
||||
printf("%s: read 'io-channels' failed\n", uc_key->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_for_each_subnode(node, dev) {
|
||||
key = calloc(1, sizeof(struct input_key));
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
|
||||
/* This is an ACD key */
|
||||
if (!ofnode_read_u32(node, "rockchip,adc_value", &adcval)) {
|
||||
key->parent = dev;
|
||||
key->name = ofnode_read_string(node, "label");
|
||||
key->type = ADC_KEY;
|
||||
key->adcval = adcval;
|
||||
key->channel = adc_channels[1];
|
||||
if (ofnode_read_u32(node, "linux,code", &key->code)) {
|
||||
printf("%s: failed to read 'linux,code'\n",
|
||||
key->name);
|
||||
free(key);
|
||||
continue;
|
||||
}
|
||||
key_add(key);
|
||||
/* This is a GPIO key */
|
||||
} else {
|
||||
key->parent = dev;
|
||||
key->type = GPIO_KEY;
|
||||
key->name = ofnode_read_string(node, "label");
|
||||
ret = ofnode_read_u32(node, "linux,code", &key->code);
|
||||
if (ret) {
|
||||
printf("%s: failed read 'linux,code', ret=%d\n",
|
||||
key->name, ret);
|
||||
free(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Only register power key as interrupt */
|
||||
if (key->code == KEY_POWER) {
|
||||
ret = ofnode_read_u32_array(node, "gpios",
|
||||
gpios, 2);
|
||||
if (ret) {
|
||||
printf("%s: failed to read 'gpios', ret=%d\n",
|
||||
key->name, ret);
|
||||
free(key);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Request irq */
|
||||
irq = phandle_gpio_to_irq(gpios[0], gpios[1]);
|
||||
if (irq < 0) {
|
||||
printf("%s: failed to request irq, ret=%d\n",
|
||||
__func__, irq);
|
||||
free(key);
|
||||
continue;
|
||||
}
|
||||
key->irq = irq;
|
||||
key_add(key);
|
||||
irq_install_handler(irq, gpio_irq_handler, key);
|
||||
irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING);
|
||||
irq_handler_enable(irq);
|
||||
} else {
|
||||
ret = gpio_request_by_name_nodev(node, "gpios",
|
||||
0, &key->gpio, GPIOD_IS_IN);
|
||||
if (ret) {
|
||||
printf("%s: failed to request gpio, ret=%d\n",
|
||||
key->name, ret);
|
||||
free(key);
|
||||
continue;
|
||||
}
|
||||
key_add(key);
|
||||
}
|
||||
if (dev_read_bool(dev, "rockchip,adc_value")) {
|
||||
uc_key->type = ADC_KEY;
|
||||
uc_key->channel = chn[1];
|
||||
uc_key->adcval =
|
||||
dev_read_u32_default(dev, "rockchip,adc_value", 0);
|
||||
} else {
|
||||
uc_key->type = GPIO_KEY;
|
||||
if (dev_read_u32_array(dev, "gpios",
|
||||
uc_key->gpios,
|
||||
ARRAY_SIZE(uc_key->gpios))) {
|
||||
printf("%s: read 'gpios' failed\n", uc_key->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
debug("%s: name=%s: code=%d, adcval=%d, channel=%d, type=%d\n",
|
||||
__func__, key->name, key->code, key->adcval,
|
||||
key->channel, key->type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_key_ops key_ops = {
|
||||
.name = "rk-keys",
|
||||
U_BOOT_DRIVER(rk_key) = {
|
||||
.name = "rk_key",
|
||||
.id = UCLASS_KEY,
|
||||
.ofdata_to_platdata = rk_key_ofdata_to_platdata,
|
||||
};
|
||||
|
||||
static const struct udevice_id rk_keys_ids[] = {
|
||||
/* Key Bus */
|
||||
static int rk_key_bus_bind(struct udevice *dev)
|
||||
{
|
||||
return key_bind_children(dev, "rk_key");
|
||||
}
|
||||
|
||||
static const struct udevice_id rk_key_bus_match[] = {
|
||||
{ .compatible = "rockchip,key" },
|
||||
{ },
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(rk_keys) = {
|
||||
.name = "rk-keys",
|
||||
.id = UCLASS_KEY,
|
||||
.ops = &key_ops,
|
||||
.of_match = rk_keys_ids,
|
||||
.ofdata_to_platdata = rk_keys_ofdata_to_platdata,
|
||||
U_BOOT_DRIVER(rk_key_bus) = {
|
||||
.name = "rk_key_bus",
|
||||
.id = UCLASS_SIMPLE_BUS,
|
||||
.of_match = rk_key_bus_match,
|
||||
.bind = rk_key_bus_bind,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,49 +18,43 @@ enum {
|
|||
GPIO_KEY = 0x2,
|
||||
};
|
||||
|
||||
enum key_state {
|
||||
enum key_event {
|
||||
KEY_PRESS_NONE, /* press without release */
|
||||
KEY_PRESS_DOWN, /* press -> release */
|
||||
KEY_PRESS_LONG_DOWN,
|
||||
KEY_NOT_EXIST,
|
||||
};
|
||||
|
||||
struct input_key {
|
||||
struct udevice *parent;
|
||||
struct list_head link;
|
||||
struct dm_key_uclass_platdata {
|
||||
const char *name;
|
||||
bool pre_reloc;
|
||||
u32 code;
|
||||
u8 type;
|
||||
|
||||
/* ADC key */
|
||||
u32 adcval;
|
||||
u32 vref;
|
||||
u8 channel;
|
||||
u32 adcval;
|
||||
u32 min;
|
||||
u32 max;
|
||||
|
||||
/* GPIO key */
|
||||
u32 irq;
|
||||
u32 gpios[2];
|
||||
struct gpio_desc gpio;
|
||||
|
||||
/* Event */
|
||||
u64 up_t;
|
||||
u64 down_t;
|
||||
};
|
||||
|
||||
struct dm_key_ops {
|
||||
const char *name;
|
||||
u64 rise_ms;
|
||||
u64 fall_ms;
|
||||
};
|
||||
|
||||
/* Use it instead of get_timer() in key interrupt handler */
|
||||
uint64_t key_timer(uint64_t base);
|
||||
|
||||
/* Reister you key to dm key framework */
|
||||
void key_add(struct input_key *key);
|
||||
|
||||
/* Confirm if your key value is a press event */
|
||||
int key_is_pressed(int keyval);
|
||||
|
||||
/* Read key */
|
||||
int key_read(int code);
|
||||
|
||||
int key_bind_children(struct udevice *dev, const char *drv_name);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue