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> 10*18fdcbceSJoseph Chen #include <irq-generic.h> 11b398a9a7SJoseph Chen #include <key.h> 125a54baa7SJoseph Chen #include <dm/lists.h> 13*18fdcbceSJoseph Chen #include <dm/uclass-internal.h> 1464048c53SJoseph Chen 155a54baa7SJoseph Chen #define KEY_WARN(fmt, args...) printf("Key Warn: "fmt, ##args) 165a54baa7SJoseph Chen #define KEY_ERR(fmt, args...) printf("Key Error: "fmt, ##args) 175a54baa7SJoseph Chen #define KEY_DBG(fmt, args...) debug("Key Debug: "fmt, ##args) 18adba3792SJoseph Chen 19a2df9606SJoseph Chen static inline uint64_t arch_counter_get_cntpct(void) 20a2df9606SJoseph Chen { 21a2df9606SJoseph Chen uint64_t cval = 0; 22a2df9606SJoseph Chen 23a2df9606SJoseph Chen isb(); 24a2df9606SJoseph Chen #ifdef CONFIG_ARM64 25a2df9606SJoseph Chen asm volatile("mrs %0, cntpct_el0" : "=r" (cval)); 26a2df9606SJoseph Chen #else 27a2df9606SJoseph Chen asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval)); 28a2df9606SJoseph Chen #endif 29a2df9606SJoseph Chen return cval; 30a2df9606SJoseph Chen } 31a2df9606SJoseph Chen 3264048c53SJoseph Chen uint64_t key_timer(uint64_t base) 33a2df9606SJoseph Chen { 34a2df9606SJoseph Chen uint64_t cntpct; 35a2df9606SJoseph Chen 36a2df9606SJoseph Chen cntpct = arch_counter_get_cntpct() / 24000UL; 37a2df9606SJoseph Chen return (cntpct > base) ? (cntpct - base) : 0; 38a2df9606SJoseph Chen } 39a2df9606SJoseph Chen 405a54baa7SJoseph Chen static int key_adc_event(struct dm_key_uclass_platdata *uc_key, int adcval) 41a2df9606SJoseph Chen { 425a54baa7SJoseph Chen return (adcval <= uc_key->max && adcval >= uc_key->min) ? 435a54baa7SJoseph Chen KEY_PRESS_DOWN : KEY_PRESS_NONE; 443fb84000SJoseph Chen } 453fb84000SJoseph Chen 465a54baa7SJoseph Chen static int key_gpio_event(struct dm_key_uclass_platdata *uc_key) 473fb84000SJoseph Chen { 485a54baa7SJoseph Chen if (!dm_gpio_is_valid(&uc_key->gpio)) { 495a54baa7SJoseph Chen KEY_ERR("'%s' Invalid gpio\n", uc_key->name); 5064048c53SJoseph Chen return KEY_PRESS_NONE; 5164048c53SJoseph Chen } 5264048c53SJoseph Chen 535a54baa7SJoseph Chen return dm_gpio_get_value(&uc_key->gpio) ? 545a54baa7SJoseph Chen KEY_PRESS_DOWN : KEY_PRESS_NONE; 5564048c53SJoseph Chen } 5664048c53SJoseph Chen 575a54baa7SJoseph Chen static int key_gpio_interrupt_event(struct dm_key_uclass_platdata *uc_key) 5864048c53SJoseph Chen { 595a54baa7SJoseph Chen int event; 603fb84000SJoseph Chen 613fb84000SJoseph Chen debug("%s: %s: up=%llu, down=%llu, delta=%llu\n", 625a54baa7SJoseph Chen __func__, uc_key->name, uc_key->rise_ms, uc_key->fall_ms, 635a54baa7SJoseph Chen uc_key->rise_ms - uc_key->fall_ms); 643fb84000SJoseph Chen 653fb84000SJoseph Chen /* Possible this is machine power-on long pressed, so ignore this */ 665a54baa7SJoseph Chen if (uc_key->fall_ms == 0 && uc_key->rise_ms != 0) { 675a54baa7SJoseph Chen event = KEY_PRESS_NONE; 683fb84000SJoseph Chen goto out; 693fb84000SJoseph Chen } 703fb84000SJoseph Chen 715a54baa7SJoseph Chen if ((uc_key->rise_ms > uc_key->fall_ms) && 725a54baa7SJoseph Chen (uc_key->rise_ms - uc_key->fall_ms) >= KEY_LONG_DOWN_MS) { 735a54baa7SJoseph Chen uc_key->rise_ms = 0; 745a54baa7SJoseph Chen uc_key->fall_ms = 0; 755a54baa7SJoseph Chen event = KEY_PRESS_LONG_DOWN; 765a54baa7SJoseph Chen KEY_DBG("%s key long pressed..\n", uc_key->name); 775a54baa7SJoseph Chen } else if (uc_key->fall_ms && 785a54baa7SJoseph Chen key_timer(uc_key->fall_ms) >= KEY_LONG_DOWN_MS) { 795a54baa7SJoseph Chen uc_key->rise_ms = 0; 805a54baa7SJoseph Chen uc_key->fall_ms = 0; 815a54baa7SJoseph Chen event = KEY_PRESS_LONG_DOWN; 825a54baa7SJoseph Chen KEY_DBG("%s key long pressed(hold)..\n", uc_key->name); 835a54baa7SJoseph Chen } else if ((uc_key->rise_ms > uc_key->fall_ms) && 845a54baa7SJoseph Chen (uc_key->rise_ms - uc_key->fall_ms) < KEY_LONG_DOWN_MS) { 855a54baa7SJoseph Chen uc_key->rise_ms = 0; 865a54baa7SJoseph Chen uc_key->fall_ms = 0; 875a54baa7SJoseph Chen event = KEY_PRESS_DOWN; 885a54baa7SJoseph Chen KEY_DBG("%s key short pressed..\n", uc_key->name); 8964048c53SJoseph Chen /* Possible in charge animation, we enable irq after fuel gauge updated */ 905a54baa7SJoseph Chen } else if (uc_key->rise_ms && uc_key->fall_ms && 915a54baa7SJoseph Chen (uc_key->rise_ms == uc_key->fall_ms)) { 925a54baa7SJoseph Chen uc_key->rise_ms = 0; 935a54baa7SJoseph Chen uc_key->fall_ms = 0; 945a54baa7SJoseph Chen event = KEY_PRESS_DOWN; 955a54baa7SJoseph Chen KEY_DBG("%s key short pressed..\n", uc_key->name); 963fb84000SJoseph Chen } else { 975a54baa7SJoseph Chen event = KEY_PRESS_NONE; 983fb84000SJoseph Chen } 993fb84000SJoseph Chen 1003fb84000SJoseph Chen out: 1015a54baa7SJoseph Chen return event; 10264048c53SJoseph Chen } 10364048c53SJoseph Chen 1045a54baa7SJoseph Chen int key_is_pressed(int event) 10564048c53SJoseph Chen { 1065a54baa7SJoseph Chen return (event == KEY_PRESS_DOWN || event == KEY_PRESS_LONG_DOWN); 10764048c53SJoseph Chen } 10864048c53SJoseph Chen 1095a54baa7SJoseph Chen static int key_core_read(struct dm_key_uclass_platdata *uc_key) 11064048c53SJoseph Chen { 11164048c53SJoseph Chen unsigned int adcval; 11264048c53SJoseph Chen 1135a54baa7SJoseph Chen if (uc_key->type == ADC_KEY) { 1145a54baa7SJoseph Chen if (adc_channel_single_shot("saradc", 1155a54baa7SJoseph Chen uc_key->channel, &adcval)) { 1165a54baa7SJoseph Chen KEY_ERR("%s failed to read saradc\n", uc_key->name); 1175a54baa7SJoseph Chen return KEY_NOT_EXIST; 11864048c53SJoseph Chen } 11964048c53SJoseph Chen 1205a54baa7SJoseph Chen return key_adc_event(uc_key, adcval); 1215a54baa7SJoseph Chen } 12264048c53SJoseph Chen 1235a54baa7SJoseph Chen return (uc_key->code == KEY_POWER) ? 1245a54baa7SJoseph Chen key_gpio_interrupt_event(uc_key) : 1255a54baa7SJoseph Chen key_gpio_event(uc_key); 1269f1dd9dfSJoseph Chen } 1279f1dd9dfSJoseph Chen 1289f1dd9dfSJoseph Chen int key_read(int code) 1299f1dd9dfSJoseph Chen { 1305a54baa7SJoseph Chen struct dm_key_uclass_platdata *uc_key; 1319f1dd9dfSJoseph Chen struct udevice *dev; 1325a54baa7SJoseph Chen struct uclass *uc; 1335a54baa7SJoseph Chen bool allow_pre_reloc = false; 1345a54baa7SJoseph Chen int ret, event = KEY_NOT_EXIST; 1359f1dd9dfSJoseph Chen 1365a54baa7SJoseph Chen ret = uclass_get(UCLASS_KEY, &uc); 1375a54baa7SJoseph Chen if (ret) 1385a54baa7SJoseph Chen return ret; 1395a54baa7SJoseph Chen 1405a54baa7SJoseph Chen try_again: 1419f1dd9dfSJoseph Chen for (uclass_first_device(UCLASS_KEY, &dev); 1429f1dd9dfSJoseph Chen dev; 1439f1dd9dfSJoseph Chen uclass_next_device(&dev)) { 1445a54baa7SJoseph Chen uc_key = dev_get_uclass_platdata(dev); 1459f1dd9dfSJoseph Chen 1465a54baa7SJoseph Chen if (!allow_pre_reloc && uc_key->pre_reloc) 1479f1dd9dfSJoseph Chen continue; 1489f1dd9dfSJoseph Chen 1495a54baa7SJoseph Chen if (uc_key->code != code) 1509f1dd9dfSJoseph Chen continue; 1519f1dd9dfSJoseph Chen 1525a54baa7SJoseph Chen event = key_core_read(uc_key); 1535a54baa7SJoseph Chen if (key_is_pressed(event)) 1545a54baa7SJoseph Chen return event; 1555a54baa7SJoseph Chen } 1565a54baa7SJoseph Chen 1575a54baa7SJoseph Chen /* If not find valid key node from kernel, try from u-boot */ 1585a54baa7SJoseph Chen if (event == KEY_NOT_EXIST && !allow_pre_reloc) { 1595a54baa7SJoseph Chen allow_pre_reloc = true; 1605a54baa7SJoseph Chen goto try_again; 1615a54baa7SJoseph Chen } 1625a54baa7SJoseph Chen 1635a54baa7SJoseph Chen return event; 1645a54baa7SJoseph Chen } 1655a54baa7SJoseph Chen 166*18fdcbceSJoseph Chen int key_exist(int code) 167*18fdcbceSJoseph Chen { 168*18fdcbceSJoseph Chen struct dm_key_uclass_platdata *uc_key; 169*18fdcbceSJoseph Chen struct udevice *dev; 170*18fdcbceSJoseph Chen 171*18fdcbceSJoseph Chen for (uclass_find_first_device(UCLASS_KEY, &dev); 172*18fdcbceSJoseph Chen dev; 173*18fdcbceSJoseph Chen uclass_find_next_device(&dev)) { 174*18fdcbceSJoseph Chen uc_key = dev_get_uclass_platdata(dev); 175*18fdcbceSJoseph Chen 176*18fdcbceSJoseph Chen if (uc_key->code == code) 177*18fdcbceSJoseph Chen return 1; 178*18fdcbceSJoseph Chen } 179*18fdcbceSJoseph Chen 180*18fdcbceSJoseph Chen return 0; 181*18fdcbceSJoseph Chen } 182*18fdcbceSJoseph Chen 1837cef7918SJoseph Chen #if CONFIG_IS_ENABLED(IRQ) 1841a9c8b1bSJoseph Chen #if defined(CONFIG_PWRKEY_DNL_TRIGGER_NUM) && \ 1851a9c8b1bSJoseph Chen (CONFIG_PWRKEY_DNL_TRIGGER_NUM > 0) 1861a9c8b1bSJoseph Chen static void power_key_download(struct dm_key_uclass_platdata *uc_key) 1871a9c8b1bSJoseph Chen { 1881a9c8b1bSJoseph Chen int trig_cnt = CONFIG_PWRKEY_DNL_TRIGGER_NUM; 1891a9c8b1bSJoseph Chen static u64 old_rise_ms; 1901a9c8b1bSJoseph Chen 1911a9c8b1bSJoseph Chen if (uc_key->code == KEY_POWER && old_rise_ms != uc_key->rise_ms) { 1921a9c8b1bSJoseph Chen old_rise_ms = uc_key->rise_ms; 1931a9c8b1bSJoseph Chen uc_key->trig_cnt++; 1941a9c8b1bSJoseph Chen if (uc_key->trig_cnt >= trig_cnt) { 1951a9c8b1bSJoseph Chen printf("\nEnter download mode by pwrkey\n"); 1961a9c8b1bSJoseph Chen irq_handler_disable(uc_key->irq); 1971a9c8b1bSJoseph Chen run_command("rockusb 0 $devtype $devnum", 0); 1981a9c8b1bSJoseph Chen run_command("rbrom", 0); 1991a9c8b1bSJoseph Chen } 2001a9c8b1bSJoseph Chen } 2011a9c8b1bSJoseph Chen } 2021a9c8b1bSJoseph Chen 2031a9c8b1bSJoseph Chen int pwrkey_download_init(void) 2041a9c8b1bSJoseph Chen { 2051a9c8b1bSJoseph Chen return (KEY_NOT_EXIST == key_read(KEY_POWER)); 2061a9c8b1bSJoseph Chen } 2071a9c8b1bSJoseph Chen #endif 2081a9c8b1bSJoseph Chen 2095a54baa7SJoseph Chen static void gpio_irq_handler(int irq, void *data) 2105a54baa7SJoseph Chen { 211c2e7a0d4SJoseph Chen struct udevice *dev = data; 212c2e7a0d4SJoseph Chen struct dm_key_uclass_platdata *uc_key = dev_get_uclass_platdata(dev); 2135a54baa7SJoseph Chen 2145a54baa7SJoseph Chen if (uc_key->irq != irq) 2155a54baa7SJoseph Chen return; 2165a54baa7SJoseph Chen 217c2e7a0d4SJoseph Chen if (uc_key->irq_thread) { 218c2e7a0d4SJoseph Chen uc_key->irq_thread(irq, data); 219c2e7a0d4SJoseph Chen } else { 2205a54baa7SJoseph Chen if (irq_get_gpio_level(irq)) { 2215a54baa7SJoseph Chen uc_key->rise_ms = key_timer(0); 222c2e7a0d4SJoseph Chen KEY_DBG("%s: key dn: %llu ms\n", 223c2e7a0d4SJoseph Chen uc_key->name, uc_key->fall_ms); 2245a54baa7SJoseph Chen } else { 2255a54baa7SJoseph Chen uc_key->fall_ms = key_timer(0); 226c2e7a0d4SJoseph Chen KEY_DBG("%s: key up: %llu ms\n", 227c2e7a0d4SJoseph Chen uc_key->name, uc_key->rise_ms); 2285a54baa7SJoseph Chen } 2295a54baa7SJoseph Chen 2305a54baa7SJoseph Chen /* Must delay */ 2315a54baa7SJoseph Chen mdelay(10); 2325a54baa7SJoseph Chen irq_revert_irq_type(irq); 2335a54baa7SJoseph Chen } 2341a9c8b1bSJoseph Chen 2351a9c8b1bSJoseph Chen /* Hook event: enter download mode by pwrkey */ 2361a9c8b1bSJoseph Chen #if defined(CONFIG_PWRKEY_DNL_TRIGGER_NUM) && \ 2371a9c8b1bSJoseph Chen (CONFIG_PWRKEY_DNL_TRIGGER_NUM > 0) 2381a9c8b1bSJoseph Chen power_key_download(uc_key); 2391a9c8b1bSJoseph Chen #endif 240c2e7a0d4SJoseph Chen } 2415a54baa7SJoseph Chen #endif 2425a54baa7SJoseph Chen 2435a54baa7SJoseph Chen int key_bind_children(struct udevice *dev, const char *drv_name) 2445a54baa7SJoseph Chen { 2455a54baa7SJoseph Chen const char *name; 2465a54baa7SJoseph Chen ofnode node; 2475a54baa7SJoseph Chen int ret; 2485a54baa7SJoseph Chen 2495a54baa7SJoseph Chen dev_for_each_subnode(node, dev) { 2505a54baa7SJoseph Chen /* 2515a54baa7SJoseph Chen * If this node has "compatible" property, this is not 2525a54baa7SJoseph Chen * a amp subnode, but a normal device. skip. 2535a54baa7SJoseph Chen */ 2545a54baa7SJoseph Chen ofnode_get_property(node, "compatible", &ret); 2555a54baa7SJoseph Chen if (ret >= 0) 2565a54baa7SJoseph Chen continue; 2575a54baa7SJoseph Chen 2585a54baa7SJoseph Chen if (ret != -FDT_ERR_NOTFOUND) 2595a54baa7SJoseph Chen return ret; 2605a54baa7SJoseph Chen 2615a54baa7SJoseph Chen name = ofnode_get_name(node); 2625a54baa7SJoseph Chen if (!name) 2635a54baa7SJoseph Chen return -EINVAL; 2645a54baa7SJoseph Chen ret = device_bind_driver_to_node(dev, drv_name, name, 2655a54baa7SJoseph Chen node, NULL); 2665a54baa7SJoseph Chen if (ret) 2675a54baa7SJoseph Chen return ret; 2685a54baa7SJoseph Chen } 2695a54baa7SJoseph Chen 2705a54baa7SJoseph Chen return 0; 2715a54baa7SJoseph Chen } 2725a54baa7SJoseph Chen 2735a54baa7SJoseph Chen static int key_post_probe(struct udevice *dev) 2745a54baa7SJoseph Chen { 2755a54baa7SJoseph Chen struct dm_key_uclass_platdata *uc_key; 2765a54baa7SJoseph Chen int ret; 277b08714cbSJoseph Chen #ifdef CONFIG_SARADC_ROCKCHIP_V2 278b08714cbSJoseph Chen int margin = 120; 279b08714cbSJoseph Chen #else 280b08714cbSJoseph Chen int margin = 30; 281b08714cbSJoseph Chen #endif 2825a54baa7SJoseph Chen uc_key = dev_get_uclass_platdata(dev); 2835a54baa7SJoseph Chen if (!uc_key) 2845a54baa7SJoseph Chen return -ENXIO; 2855a54baa7SJoseph Chen 2865a54baa7SJoseph Chen /* True from U-Boot key node */ 287930ceb12SJoseph Chen uc_key->pre_reloc = dev_read_bool(dev, "u-boot,dm-pre-reloc") || 288930ceb12SJoseph Chen dev_read_bool(dev, "u-boot,dm-spl"); 2895a54baa7SJoseph Chen 2905a54baa7SJoseph Chen if (uc_key->type == ADC_KEY) { 2915a54baa7SJoseph Chen uc_key->max = uc_key->adcval + margin; 2925a54baa7SJoseph Chen uc_key->min = uc_key->adcval > margin ? 2935a54baa7SJoseph Chen uc_key->adcval - margin : 0; 2945a54baa7SJoseph Chen } else { 2955a54baa7SJoseph Chen if (uc_key->code == KEY_POWER) { 2967cef7918SJoseph Chen #if CONFIG_IS_ENABLED(IRQ) 2975a54baa7SJoseph Chen int irq; 2985a54baa7SJoseph Chen 2990f9d23eaSJoseph Chen if (uc_key->skip_irq_init) 3000f9d23eaSJoseph Chen return 0; 3010f9d23eaSJoseph Chen 3025a54baa7SJoseph Chen irq = phandle_gpio_to_irq(uc_key->gpios[0], 3035a54baa7SJoseph Chen uc_key->gpios[1]); 3045a54baa7SJoseph Chen if (irq < 0) { 3055a54baa7SJoseph Chen KEY_ERR("%s: failed to request irq, ret=%d\n", 3065a54baa7SJoseph Chen uc_key->name, irq); 3075a54baa7SJoseph Chen return irq; 3085a54baa7SJoseph Chen } 3095a54baa7SJoseph Chen 310c2e7a0d4SJoseph Chen if (uc_key->code != KEY_POWER && uc_key->irq_thread) { 311c2e7a0d4SJoseph Chen KEY_WARN("%s: only power key can request irq thread\n", 312c2e7a0d4SJoseph Chen uc_key->name); 313c2e7a0d4SJoseph Chen return -EINVAL; 314c2e7a0d4SJoseph Chen } 315c2e7a0d4SJoseph Chen 3165a54baa7SJoseph Chen uc_key->irq = irq; 317c2e7a0d4SJoseph Chen irq_install_handler(irq, gpio_irq_handler, dev); 3185a54baa7SJoseph Chen irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING); 3195a54baa7SJoseph Chen irq_handler_enable(irq); 3205a54baa7SJoseph Chen #else 3215a54baa7SJoseph Chen KEY_WARN("%s: no IRQ framework available\n", uc_key->name); 3225a54baa7SJoseph Chen #endif 3235a54baa7SJoseph Chen } else { 3245a54baa7SJoseph Chen ret = gpio_request_by_name(dev, "gpios", 0, 3255a54baa7SJoseph Chen &uc_key->gpio, GPIOD_IS_IN); 3265a54baa7SJoseph Chen if (ret) { 3275a54baa7SJoseph Chen KEY_ERR("%s: failed to request gpio, ret=%d\n", 3285a54baa7SJoseph Chen uc_key->name, ret); 3295a54baa7SJoseph Chen return ret; 3305a54baa7SJoseph Chen } 3319f1dd9dfSJoseph Chen } 332adba3792SJoseph Chen } 333adba3792SJoseph Chen 3345a54baa7SJoseph Chen #ifdef DEBUG 3355a54baa7SJoseph Chen printf("[%s] (%s, %s, %s):\n", uc_key->name, 3365a54baa7SJoseph Chen uc_key->type == ADC_KEY ? "ADC" : "GPIO", 3375a54baa7SJoseph Chen uc_key->pre_reloc ? "U-Boot" : "Kernel", 3385a54baa7SJoseph Chen dev->parent->name); 3395a54baa7SJoseph Chen 3405a54baa7SJoseph Chen if (uc_key->type == ADC_KEY) { 3415a54baa7SJoseph Chen printf(" adcval: %d (%d, %d)\n", uc_key->adcval, 3425a54baa7SJoseph Chen uc_key->min, uc_key->max); 3435a54baa7SJoseph Chen printf(" channel: %d\n\n", uc_key->channel); 3445a54baa7SJoseph Chen } else { 3455a54baa7SJoseph Chen const char *gpio_name = 3465a54baa7SJoseph Chen ofnode_get_name(ofnode_get_by_phandle(uc_key->gpios[0])); 3475a54baa7SJoseph Chen 3485a54baa7SJoseph Chen printf(" irq: %d\n", uc_key->irq); 3495a54baa7SJoseph Chen printf(" gpio[0]: %s\n", gpio_name); 3505a54baa7SJoseph Chen printf(" gpio[1]: %d\n\n", uc_key->gpios[1]); 3515a54baa7SJoseph Chen } 3525a54baa7SJoseph Chen #endif 3535a54baa7SJoseph Chen 3545a54baa7SJoseph Chen return 0; 3553fb84000SJoseph Chen } 3563fb84000SJoseph Chen 357b398a9a7SJoseph Chen UCLASS_DRIVER(key) = { 358b398a9a7SJoseph Chen .id = UCLASS_KEY, 359b398a9a7SJoseph Chen .name = "key", 3605a54baa7SJoseph Chen .post_probe = key_post_probe, 3615a54baa7SJoseph Chen .per_device_platdata_auto_alloc_size = 3625a54baa7SJoseph Chen sizeof(struct dm_key_uclass_platdata), 363b398a9a7SJoseph Chen }; 364