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