/* * Copyright (c) 2017 Actions Semiconductor Co., Ltd * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief ADC Keyboard driver for Actions SoC */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_CFG_DRV #include #include #endif LOG_MODULE_REGISTER(keyadc, CONFIG_SYS_LOG_INPUT_DEV_LEVEL); #ifdef CONFIG_CFG_DRV struct adckey_map { uint16_t key_code; uint16_t adc_min; uint16_t adc_max; }; #else struct adckey_map { uint16_t key_code; uint16_t adc_val; }; #endif struct acts_adckey_data { const struct device *adc_dev; const struct device *this_dev; #ifdef CONFIG_ADCKEY_POLL_TIMER struct k_timer timer; #else struct k_delayed_work timer; #endif struct adc_sequence sequence; uint8_t adc_buf[4]; int scan_count; uint16_t prev_keycode; uint16_t prev_stable_keycode; input_notify_t notify; uint32_t timestamp; uint8_t state; /* if 1 indicates the polling delay worker is runing */ #ifdef CONFIG_CFG_DRV struct adckey_map adckey_keymaps[CFG_MAX_LRADC_KEYS]; #endif }; struct acts_adckey_config { char *adc_name; uint16_t adc_chan; uint16_t poll_interval_ms; uint16_t sample_filter_dep; uint32_t poll_total_ms; #ifndef CONFIG_CFG_DRV uint16_t key_cnt; const struct adckey_map *key_maps; #endif }; static void adckey_pmu_notify(void *cb_data, int state); static void __adckey_acts_disable(const struct device *dev, int is_stop); static void adckey_acts_disable(const struct device *dev); /* adc_val -> key_code */ static uint16_t adckey_acts_get_keycode(const struct acts_adckey_config *cfg, struct acts_adckey_data *adckey, int adc_val) { #ifdef CONFIG_CFG_DRV struct adckey_map *map = adckey->adckey_keymaps; int i; if (!map) goto out; for (i = 0; i < CFG_MAX_LRADC_KEYS; i++) { if ((adc_val < map->adc_max) && (adc_val > map->adc_min)) return map->key_code; map++; } out: return KEY_RESERVED; #else const struct adckey_map *map = cfg->key_maps; int i; if (!map) goto out; for (i = 0; i < cfg->key_cnt; i++) { if (adc_val < map->adc_val) return map->key_code; map++; } out: return KEY_RESERVED; #endif } static void adckey_acts_report_key(struct acts_adckey_data *adckey, int key_code, int value) { struct input_value val; if (adckey->notify) { val.keypad.type = EV_KEY; val.keypad.code = key_code; val.keypad.value = value; LOG_DBG("report key_code %d value %d", key_code, value); adckey->notify(NULL, &val); } } void adckey_acts_inquiry(const struct device *dev, struct input_value *val) { struct acts_adckey_data *adckey = dev->data; const struct device *adckeydev = adckey->this_dev; const struct acts_adckey_config *cfg = adckeydev->config; uint16_t keycode, adcval; int ret; /* get adc value */ ret = adc_read(adckey->adc_dev, &adckey->sequence); if (ret) { LOG_ERR("ADC read error %d", ret); return ; } adcval = sys_get_le16(adckey->sequence.buffer); LOG_DBG("adc value: %d\n", adcval); /* get keycode */ keycode = adckey_acts_get_keycode(cfg, adckey, adcval); LOG_DBG("adcval %d, keycode %d\n", adcval, keycode); if (keycode != KEY_RESERVED && val->keypad.code == keycode) { val->keypad.type = EV_KEY; val->keypad.value = 1; } } #ifdef CONFIG_ADCKEY_POLL_TIMER static void adckey_acts_poll(struct k_timer *timer) #else static void adckey_acts_poll(struct k_work *work) #endif { #ifdef CONFIG_ADCKEY_POLL_TIMER struct device *dev = k_timer_user_data_get(timer); struct acts_adckey_data *adckey = dev->data; #else struct acts_adckey_data *adckey = CONTAINER_OF(work, struct acts_adckey_data, timer); const struct device *dev = adckey->this_dev; #endif const struct acts_adckey_config *cfg = dev->config; uint16_t keycode, adcval; int ret; bool key_release = false; struct detect_param_t detect_param = {PMU_DETECT_DEV_REMOTE, adckey_pmu_notify, (void *)dev}; /* get adc value */ ret = adc_read(adckey->adc_dev, &adckey->sequence); if (ret) { LOG_ERR("ADC read error %d", ret); return ; } adcval = sys_get_le16(adckey->sequence.buffer); /* get keycode */ keycode = adckey_acts_get_keycode(cfg, adckey, adcval); LOG_DBG("adcval 0x%x, keycode %d", adcval, keycode); /* no key is pressed or releasing */ if (keycode == KEY_RESERVED && adckey->prev_stable_keycode == KEY_RESERVED){ key_release = true; goto out; } key_release = false; if (keycode == adckey->prev_keycode) { adckey->scan_count++; if (adckey->scan_count == cfg->sample_filter_dep) { /* previous key is released? */ if (adckey->prev_stable_keycode != KEY_RESERVED && keycode != adckey->prev_stable_keycode) adckey_acts_report_key(adckey, adckey->prev_stable_keycode, 0); /* a new key press? */ if (keycode != KEY_RESERVED) adckey_acts_report_key(adckey, keycode, 1); /* clear counter for new checking */ adckey->prev_stable_keycode = keycode; adckey->scan_count = 0; } } else { /* new key pressed? */ adckey->prev_keycode = keycode; adckey->scan_count = 0; } out: /* Only LRADC1 can trigger PMU interrupt */ if (cfg->adc_chan != PMUADC_ID_LRADC1) { k_delayed_work_submit(&adckey->timer, K_MSEC(cfg->poll_interval_ms)); } else { if (((k_uptime_get_32() - adckey->timestamp) > cfg->poll_total_ms) && key_release) { __adckey_acts_disable(dev, 0); if (soc_pmu_register_notify(&detect_param)) LOG_ERR("failed to register pmu notify"); } else { #ifdef CONFIG_ADCKEY_POLL_TIMER k_timer_start(&adckey->timer, K_MSEC(cfg->poll_interval_ms), K_NO_WAIT); #else k_delayed_work_submit(&adckey->timer, K_MSEC(cfg->poll_interval_ms)); #endif } } } static void adckey_acts_enable(const struct device *dev) { struct acts_adckey_data *adckey = dev->data; uint32_t key; LOG_DBG("enable adckey"); key = irq_lock(); if (adckey->state) { irq_unlock(key); return ; } adckey->state = 1; irq_unlock(key); adckey->timestamp = k_uptime_get_32(); #ifdef CONFIG_ADCKEY_POLL_TIMER const struct acts_adckey_config *cfg = dev->config; k_timer_start(&adckey->timer, K_MSEC(cfg->poll_interval_ms), K_NO_WAIT); #else k_delayed_work_submit(&adckey->timer, K_NO_WAIT); #endif } static void __adckey_acts_disable(const struct device *dev, int is_stop) { struct acts_adckey_data *adckey = dev->data; uint32_t key; LOG_DBG("disable adckey"); key = irq_lock(); if (!adckey->state) { irq_unlock(key); return ; } adckey->state = 0; irq_unlock(key); adckey->timestamp = 0; if(is_stop){ #ifdef CONFIG_ADCKEY_POLL_TIMER k_timer_stop(&adckey->timer); #else k_delayed_work_cancel(&adckey->timer); #endif } } static void adckey_acts_disable(const struct device *dev) { __adckey_acts_disable(dev, 1); } static void adckey_acts_register_notify(const struct device *dev, input_notify_t notify) { struct acts_adckey_data *adckey = dev->data; LOG_DBG("register notify 0x%x", (uint32_t)notify); adckey->notify = notify; } static void adckey_acts_unregister_notify(const struct device *dev, input_notify_t notify) { struct acts_adckey_data *adckey = dev->data; LOG_DBG("unregister notify 0x%x", (uint32_t)notify); adckey->notify = NULL; } const struct input_dev_driver_api adckey_acts_driver_api = { .enable = adckey_acts_enable, .disable = adckey_acts_disable, .inquiry = adckey_acts_inquiry, .register_notify = adckey_acts_register_notify, .unregister_notify = adckey_acts_unregister_notify, }; static void adckey_pmu_notify(void *cb_data, int state) { struct device *dev = (struct device *)cb_data; LOG_INF("PMU notify state:%d", state); adckey_acts_enable(dev); } #ifdef CONFIG_CFG_DRV static int adckey_acts_config_init(const struct device *dev, uint8_t *channel_id) { struct acts_adckey_data *adckey = dev->data; int ret; CFG_Type_LRADC_Key Key[CFG_MAX_LRADC_KEYS]; uint32_t LRADC_Ctrl; uint8_t i; ret = cfg_get_by_key(ITEM_LRADC_KEY, Key, sizeof(Key)); if (!ret) { LOG_ERR("failed to get ITEM_LRADC_KEY"); return -ESRCH; } for (i = 0; i < CFG_MAX_LRADC_KEYS; i++) { adckey->adckey_keymaps[i].key_code = Key[i].Key_Value; adckey->adckey_keymaps[i].adc_max = Key[i].ADC_Max; adckey->adckey_keymaps[i].adc_min = Key[i].ADC_Min; } ret = cfg_get_by_key(ITEM_LRADC_CTRL, &LRADC_Ctrl, sizeof(LRADC_Ctrl)); if (!ret) { LOG_ERR("failed to get ITEM_LRADC_CTRL"); return -ESRCH; } if (ret > 0) { uint8_t channel_no = LRADC_Ctrl & 0xff; uint8_t gpio_no = (LRADC_Ctrl & 0xff00) >> 8; uint32_t mfp_sel = (LRADC_Ctrl & 0xff0000) >> 16; /* remap channel number to LRADC index */ channel_no += 5; if (channel_no < GPIO_MAX_PIN_NUM) sys_write32(mfp_sel, GPIO_REG_BASE + (1 + gpio_no)*4); else if (gpio_no < (GPIO_MAX_PIN_NUM + 4)) sys_write32(mfp_sel, WIO0_CTL + (gpio_no - GPIO_MAX_PIN_NUM) * 4); *channel_id = channel_no; } return 0; } #endif int adckey_acts_init(const struct device *dev) { const struct acts_adckey_config *cfg = dev->config; struct acts_adckey_data *adckey = dev->data; struct adc_channel_cfg channel_cfg = {0}; struct detect_param_t detect_param = {PMU_DETECT_DEV_REMOTE, adckey_pmu_notify, (void *)dev}; adckey->adc_dev = device_get_binding(cfg->adc_name); if (!adckey->adc_dev) { LOG_ERR("cannot found adc dev %s\n", cfg->adc_name); return -ENODEV; } channel_cfg.channel_id = cfg->adc_chan; #ifdef CONFIG_CFG_DRV uint8_t channel_cfg_id; if (adckey_acts_config_init(dev, &channel_cfg_id)) return -ENXIO; channel_cfg.channel_id = channel_cfg_id; #endif if (adc_channel_setup(adckey->adc_dev, &channel_cfg)) { LOG_ERR("setup channel_id %d error", channel_cfg.channel_id); return -EFAULT; } adckey->sequence.channels = BIT(cfg->adc_chan); adckey->sequence.buffer = &adckey->adc_buf[0]; adckey->sequence.buffer_size = sizeof(adckey->adc_buf); adckey->this_dev = dev; if (soc_pmu_register_notify(&detect_param)) { LOG_ERR("failed to register pmu notify"); return -ENXIO; } #ifdef CONFIG_ADCKEY_POLL_TIMER k_timer_init(&adckey->timer, adckey_acts_poll, NULL); k_timer_user_data_set(&adckey->timer, (void *)dev); #else k_delayed_work_init(&adckey->timer, adckey_acts_poll); #endif return 0; } static struct acts_adckey_data adckey_acts_ddata; #ifndef CONFIG_CFG_DRV static const struct adckey_map adckey_acts_keymaps[] = { #ifdef BOARD_ADCKEY_KEY_MAPPINGS BOARD_ADCKEY_KEY_MAPPINGS #endif }; #endif static const struct acts_adckey_config adckey_acts_cdata = { .adc_name = CONFIG_PMUADC_NAME, .adc_chan = CONFIG_ADCKEY_LRADC_CHAN, .poll_interval_ms = CONFIG_ADCKEY_POLL_INTERVAL_MS, .poll_total_ms = CONFIG_ADCKEY_POLL_TOTAL_MS, .sample_filter_dep = CONFIG_ADCKEY_SAMPLE_FILTER_CNT, #ifndef CONFIG_CFG_DRV .key_cnt = ARRAY_SIZE(adckey_acts_keymaps), .key_maps = &adckey_acts_keymaps[0], #endif }; #ifdef CONFIG_PM_DEVICE int adckey_pm_control(const struct device *dev, enum pm_device_action action) { struct acts_adckey_data *adckey = dev->data; switch (action) { case PM_DEVICE_ACTION_RESUME: break; case PM_DEVICE_ACTION_SUSPEND: if(adckey->state) { LOG_ERR("adckey busy, cannot suspend"); return -1; } break; default: return 0; } return 0; } #else #define adckey_pm_control NULL #endif #if IS_ENABLED(CONFIG_ADCKEY) DEVICE_DEFINE(adckey_acts, CONFIG_INPUT_DEV_ACTS_ADCKEY_NAME, adckey_acts_init, adckey_pm_control, &adckey_acts_ddata, &adckey_acts_cdata, PRE_KERNEL_2, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &adckey_acts_driver_api); #endif