/* * Copyright (c) 2017 Actions Semiconductor Co., Ltd * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief GPIO Keyboard driver for Actions SoC */ #include #include #include #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(gpiokey, CONFIG_LOG_DEFAULT_LEVEL); #if CONFIG_GPIOKEY_PRESSED_VOLTAGE_LEVEL #define GPIOKEY_PIN_MODE_DEFAULT (GPIO_INPUT) #else #define GPIOKEY_PIN_MODE_DEFAULT (GPIO_INPUT | GPIO_PULL_UP) #endif #define GPIOKEY_PIN_MODE_IRQ (GPIO_INPUT | GPIO_ACTIVE_LOW) struct acts_gpiokey_data { input_notify_t notify; #ifdef GPIO_KEY_POLL_TIMER struct k_timer timer; #else struct k_delayed_work timer; #endif const struct device *this_dev; uint32_t time_wait; uint8_t scan_count; bool irq_trigger; }; struct acts_gpiokey_config { uint8_t pinmux_size; const struct acts_pin_config *pinmux; uint16_t poll_interval_ms; uint32_t poll_total_ms; uint8_t sample_filter_cnt; }; typedef void (*gpio_config_irq_t)(struct device *dev); struct acts_gpio_config { uint32_t base; uint32_t irq_num; gpio_config_irq_t config_func; }; static struct acts_gpiokey_data gpiokey_acts_ddata; static const struct acts_pin_config pins_gpiokey[] = {CONFIG_GPIOKEY_MFP}; static const struct acts_gpiokey_config gpiokey_acts_cdata = { .pinmux = pins_gpiokey, .poll_interval_ms = CONFIG_GPIOKEY_POLL_INTERVAL_MS, .poll_total_ms = CONFIG_GPIOKEY_POLL_TOTAL_MS, .sample_filter_cnt = CONFIG_GPIOKEY_SAMPLE_FILTER_CNT, .pinmux_size = ARRAY_SIZE(pins_gpiokey), }; struct gpio_status { const struct device *gpio_dev; uint8_t past_tatus; uint8_t curent_tatus; }; static void gpiokey_acts_enable(const struct device *dev); static void gpiokey_acts_disable(const struct device *dev); struct gpio_status key_gpio_status[ARRAY_SIZE(pins_gpiokey)]; struct gpio_callback key_gpio_cb[ARRAY_SIZE(pins_gpiokey)]; uint8_t disable_cb; static void KEY_IRQ_callback(const struct device *port, struct gpio_callback *cb, uint32_t pins) { for( int i = 0; i < ARRAY_SIZE(pins_gpiokey); i++){ gpio_pin_configure(key_gpio_status[i].gpio_dev, pins_gpiokey[i].pin_num % 32, GPIOKEY_PIN_MODE_DEFAULT); } gpiokey_acts_ddata.time_wait = k_uptime_get_32(); #ifdef GPIO_KEY_POLL_TIMER k_timer_start(&gpiokey_acts_ddata.timer, K_MSEC(gpiokey_acts_cdata.poll_interval_ms), K_NO_WAIT); #else k_delayed_work_submit(&gpiokey_acts_ddata.timer, K_MSEC(gpiokey_acts_cdata.poll_interval_ms)); #endif gpiokey_acts_ddata.irq_trigger = true; } #ifdef GPIO_KEY_POLL_TIMER static void gpiokey_acts_poll(struct k_timer *timer) #else static void gpiokey_acts_poll(struct k_work *work) #endif { #ifdef GPIO_KEY_POLL_TIMER const struct device *dev = k_timer_user_data_get(timer); struct acts_gpiokey_data *gpiokey = dev->data; #else struct acts_gpiokey_data *gpiokey = CONTAINER_OF(work, struct acts_gpiokey_data, timer); const struct device *dev = gpiokey->this_dev; #endif const struct acts_gpiokey_config *cfg = dev->config; const struct acts_pin_config *pinconf = cfg->pinmux; int val; int i; for (i = 0; i < cfg->pinmux_size; i++) { val = gpio_pin_get(key_gpio_status[i].gpio_dev, (pinconf[i].pin_num % 32)); if (val < 0) { LOG_DBG("failed to get gpio:%d input state", pinconf[i].pin_num); continue; } LOG_DBG("gpiokey pin:%d state:%d stable_state:%d", pinconf[i].pin_num, val, key_gpio_status[i].past_tatus); if (val != key_gpio_status[i].past_tatus) {//a key is pressed or released if (++gpiokey->scan_count == cfg->sample_filter_cnt) { gpiokey->time_wait = k_uptime_get_32(); key_gpio_status[i].past_tatus = (uint8_t)val; if(gpiokey->notify) { struct input_value val_notify = {0}; val_notify.keypad.type = EV_KEY; #ifdef CONFIG_GPIOKEY_USER_KEYCODE val_notify.keypad.code = CONFIG_GPIOKEY_USER_KEYCODE; #else val_notify.keypad.code = KEY_NUM0 + i; #endif if (CONFIG_GPIOKEY_PRESSED_VOLTAGE_LEVEL == val) val_notify.keypad.value = 1; /* key pressed */ else val_notify.keypad.value = 0; /* key release */ gpiokey->notify(NULL, &val_notify); } gpiokey->scan_count = 0; gpiokey_acts_ddata.irq_trigger = false; } goto out; } if (key_gpio_status[i].past_tatus == CONFIG_GPIOKEY_PRESSED_VOLTAGE_LEVEL) { if(gpiokey->notify) { struct input_value val_notify = {0}; val_notify.keypad.type = EV_KEY; #ifdef CONFIG_GPIOKEY_USER_KEYCODE val_notify.keypad.code = CONFIG_GPIOKEY_USER_KEYCODE; #else val_notify.keypad.code = KEY_NUM0 + i; #endif val_notify.keypad.value = 1; gpiokey->notify(NULL, &val_notify); } } } out: if ((k_uptime_get_32() - gpiokey->time_wait) > cfg->poll_total_ms) { gpiokey_acts_disable(dev); gpiokey->scan_count = 0; gpiokey_acts_ddata.irq_trigger = false; for(i = 0; i < cfg->pinmux_size; i++) { gpio_pin_configure(key_gpio_status[i].gpio_dev, (pinconf[i].pin_num % 32), GPIOKEY_PIN_MODE_IRQ); gpio_pin_interrupt_configure(key_gpio_status[i].gpio_dev, (pinconf[i].pin_num % 32), GPIO_INT_EDGE_TO_INACTIVE); gpio_init_callback(&key_gpio_cb[i], KEY_IRQ_callback, BIT(pinconf[i].pin_num % 32)); gpio_add_callback(key_gpio_status[i].gpio_dev, &key_gpio_cb[i]); gpio_pin_interrupt_configure(key_gpio_status[i].gpio_dev, (pinconf[i].pin_num % 32), GPIO_INT_EDGE_BOTH); } } else { #ifdef GPIO_KEY_POLL_TIMER k_timer_start(&gpiokey->timer, K_MSEC(cfg->poll_interval_ms), K_NO_WAIT); #else k_delayed_work_submit(&gpiokey->timer, K_MSEC(cfg->poll_interval_ms)); #endif } } static void gpiokey_acts_enable(const struct device *dev) { struct acts_gpiokey_data *gpiokey = dev->data; gpiokey->time_wait = k_uptime_get_32(); #ifdef GPIO_KEY_POLL_TIMER const struct acts_gpiokey_config *cfg = dev->config; k_timer_start(&gpiokey->timer, K_MSEC(cfg->poll_interval_ms), K_NO_WAIT); #else k_delayed_work_submit(&gpiokey->timer, K_NO_WAIT); #endif LOG_DBG("enable gpiokey"); } static void gpiokey_acts_disable(const struct device *dev) { struct acts_gpiokey_data *gpiokey = dev->data; #ifdef GPIO_KEY_POLL_TIMER k_timer_stop(&gpiokey->timer); #else k_delayed_work_cancel(&gpiokey->timer); #endif LOG_DBG("disable gpiokey"); } static void gpiokey_acts_register_notify(const struct device *dev, input_notify_t notify) { struct acts_gpiokey_data *gpiokey = dev->data; LOG_DBG("register notify 0x%x", (uint32_t)notify); gpiokey->notify = notify; } static void gpiokey_acts_unregister_notify(const struct device *dev, input_notify_t notify) { struct acts_gpiokey_data *gpiokey = dev->data; LOG_DBG("unregister notify 0x%x", (uint32_t)notify); gpiokey->notify = NULL; } const struct input_dev_driver_api gpiokey_acts_driver_api = { .enable = gpiokey_acts_enable, .disable = gpiokey_acts_disable, .register_notify = gpiokey_acts_register_notify, .unregister_notify = gpiokey_acts_unregister_notify, }; #ifdef CONFIG_PM_DEVICE int gpiokey_pm_control(const struct device *dev, enum pm_device_action action) { struct acts_gpiokey_data *data = (struct acts_gpiokey_data *const)(dev->data); switch (action) { case PM_DEVICE_ACTION_RESUME: // data->suspended = 0; break; case PM_DEVICE_ACTION_SUSPEND: if((data->irq_trigger != false)) { LOG_ERR("gpiokey scan keypress, cannot suspend"); return -1; } // data->suspended = 1; break; default: return 0; } return 0; } #else #define gpiokey_pm_control NULL #endif int gpiokey_acts_init(const struct device *dev) { const struct acts_gpiokey_config *cfg = dev->config; const struct acts_pin_config *pinconf = cfg->pinmux; struct acts_gpiokey_data *gpiokey = dev->data; int val, i; for(i = 0; i < cfg->pinmux_size; i++) { const struct device *gpio_dev = device_get_binding(CONFIG_GPIO_PIN2NAME(pinconf[i].pin_num)); if (!gpio_dev) { LOG_ERR("failed to bind GPIO:%d", pinconf[i].pin_num); continue; } gpio_pin_configure(gpio_dev, (pinconf[i].pin_num % 32), GPIOKEY_PIN_MODE_DEFAULT); val = gpio_pin_get(gpio_dev, (pinconf[i].pin_num % 32)); if (val < 0) { LOG_ERR("failed to get gpio:%d input state", pinconf[i].pin_num); continue; } key_gpio_status[i].past_tatus = val; key_gpio_status[i].gpio_dev = gpio_dev; } gpiokey->this_dev = dev; gpiokey->scan_count = 0; gpiokey_acts_ddata.irq_trigger = false; #ifdef GPIO_KEY_POLL_TIMER k_timer_init(&gpiokey->timer, gpiokey_acts_poll, NULL); k_timer_user_data_set(&gpiokey->timer, (void *)dev); #else k_delayed_work_init(&gpiokey->timer, gpiokey_acts_poll); #endif return 0; } #if IS_ENABLED(CONFIG_GPIOKEY) DEVICE_DEFINE(gpiokey, CONFIG_INPUT_DEV_ACTS_GPIOKEY_NAME, gpiokey_acts_init, gpiokey_pm_control, &gpiokey_acts_ddata, &gpiokey_acts_cdata, POST_KERNEL, 60, &gpiokey_acts_driver_api); #endif