diff --git a/boards/atguigu/mini_keyboard/mini_keyboard.dts b/boards/atguigu/mini_keyboard/mini_keyboard.dts index 3de68c7..bc2cb9e 100644 --- a/boards/atguigu/mini_keyboard/mini_keyboard.dts +++ b/boards/atguigu/mini_keyboard/mini_keyboard.dts @@ -95,6 +95,7 @@ wakeup-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; keepalive-interval-ms = <8000>; keepalive-pulse-width-ms = <500>; + keepalive-hardware; status = "okay"; }; }; diff --git a/drivers/pmic/ip5306/ip5306.c b/drivers/pmic/ip5306/ip5306.c index 248b7aa..2d7be4d 100644 --- a/drivers/pmic/ip5306/ip5306.c +++ b/drivers/pmic/ip5306/ip5306.c @@ -7,9 +7,14 @@ #include #include #include +#include #include +#include #include +#include +#include +#include #define DT_DRV_COMPAT injoinic_ip5306 @@ -19,21 +24,34 @@ LOG_MODULE_REGISTER(ip5306, LOG_LEVEL_INF); #define IP5306_REG_READ1 0x71 #define IP5306_CHARGING_BIT BIT(3) #define IP5306_FULL_BIT BIT(3) +#define IP5306_KEEPALIVE_RTC_FREQ_HZ 32768U +#define IP5306_KEEPALIVE_RTC_CHANNEL_END 0U +#define IP5306_KEEPALIVE_RTC_CHANNEL_START 1U struct ip5306_config { struct i2c_dt_spec bus; + nrfx_gpiote_t *gpiote; struct gpio_dt_spec wakeup_gpio; + uint8_t wakeup_pin; uint32_t keepalive_interval_ms; uint32_t keepalive_pulse_width_ms; + bool keepalive_hardware; }; struct ip5306_data { const struct device *dev; struct k_work_delayable keepalive_work; + uint8_t gpiote_channel; + nrfx_gppi_handle_t ppi_start; + nrfx_gppi_handle_t ppi_end; + nrfx_gppi_handle_t ppi_clear; bool started; bool pulse_active; + bool hardware_ready; }; +static const nrfx_rtc_t keepalive_rtc = NRFX_RTC_INSTANCE(2); + static uint32_t keepalive_low_delay_ms(const struct ip5306_config *config) { if (config->keepalive_interval_ms > config->keepalive_pulse_width_ms) { @@ -44,6 +62,11 @@ static uint32_t keepalive_low_delay_ms(const struct ip5306_config *config) return 0; } +static uint32_t keepalive_ms_to_rtc_ticks(uint32_t time_ms) +{ + return (uint32_t)(((uint64_t)time_ms * IP5306_KEEPALIVE_RTC_FREQ_HZ) / 1000U); +} + static void keepalive_work_handler(struct k_work *work) { struct k_work_delayable *dwork = k_work_delayable_from_work(work); @@ -79,6 +102,130 @@ static void keepalive_work_handler(struct k_work *work) K_MSEC(keepalive_low_delay_ms(config))); } +static void ip5306_keepalive_rtc_handler(nrfx_rtc_int_type_t int_type) +{ + ARG_UNUSED(int_type); +} + +static int hardware_keepalive_init(const struct device *dev) +{ + const struct ip5306_config *config = dev->config; + struct ip5306_data *data = dev->data; + nrfx_gpiote_output_config_t output_config = NRFX_GPIOTE_DEFAULT_OUTPUT_CONFIG; + nrfx_gpiote_task_config_t task_config = { + .task_ch = 0, + .polarity = NRF_GPIOTE_POLARITY_TOGGLE, + .init_val = (config->wakeup_gpio.dt_flags & GPIO_ACTIVE_LOW) ? + NRF_GPIOTE_INITIAL_VALUE_HIGH : + NRF_GPIOTE_INITIAL_VALUE_LOW, + }; + nrfx_rtc_config_t rtc_cfg = NRFX_RTC_DEFAULT_CONFIG; + uint32_t period_ticks = keepalive_ms_to_rtc_ticks(config->keepalive_interval_ms); + uint32_t pulse_ticks = keepalive_ms_to_rtc_ticks(config->keepalive_pulse_width_ms); + uint32_t start_ticks = period_ticks - pulse_ticks; + uint32_t eep_start; + uint32_t eep_end; + uint32_t tep_toggle; + uint32_t tep_clear; + int err; + + if (data->hardware_ready) { + return 0; + } + + if (!nrfx_gpiote_init_check(config->gpiote)) { + LOG_ERR("GPIOTE shared instance is not initialized"); + return -ENODEV; + } + + err = nrfx_gpiote_channel_alloc(config->gpiote, &data->gpiote_channel); + if (err) { + LOG_ERR("GPIOTE channel alloc failed (%d)", err); + return err; + } + + task_config.task_ch = data->gpiote_channel; + + err = nrfx_gpiote_output_configure(config->gpiote, + config->wakeup_pin, + &output_config, + &task_config); + if (err) { + LOG_ERR("GPIOTE output configure failed (%d)", err); + return err; + } + + nrfx_gpiote_out_task_enable(config->gpiote, config->wakeup_pin); + + err = nrfx_rtc_init(&keepalive_rtc, &rtc_cfg, ip5306_keepalive_rtc_handler); + if ((err != 0) && (err != -EALREADY)) { + LOG_ERR("RTC2 init failed (%d)", err); + return err; + } + + err = nrfx_rtc_cc_set(&keepalive_rtc, + IP5306_KEEPALIVE_RTC_CHANNEL_END, + period_ticks, + false); + if (err) { + LOG_ERR("RTC2 CC end set failed (%d)", err); + return err; + } + + err = nrfx_rtc_cc_set(&keepalive_rtc, + IP5306_KEEPALIVE_RTC_CHANNEL_START, + start_ticks, + false); + if (err) { + LOG_ERR("RTC2 CC start set failed (%d)", err); + return err; + } + + eep_start = nrfx_rtc_event_address_get(&keepalive_rtc, + NRF_RTC_EVENT_COMPARE_1); + eep_end = nrfx_rtc_event_address_get(&keepalive_rtc, + NRF_RTC_EVENT_COMPARE_0); + tep_toggle = nrfx_gpiote_out_task_address_get(config->gpiote, + config->wakeup_pin); + tep_clear = nrfx_rtc_task_address_get(&keepalive_rtc, NRF_RTC_TASK_CLEAR); + + err = nrfx_gppi_conn_alloc(eep_start, tep_toggle, &data->ppi_start); + if (err) { + LOG_ERR("GPPI start alloc failed (%d)", err); + return err; + } + + err = nrfx_gppi_conn_alloc(eep_end, tep_toggle, &data->ppi_end); + if (err) { + LOG_ERR("GPPI end alloc failed (%d)", err); + return err; + } + + err = nrfx_gppi_conn_alloc(eep_end, tep_clear, &data->ppi_clear); + if (err) { + LOG_ERR("GPPI clear alloc failed (%d)", err); + return err; + } + + nrfx_gppi_conn_enable(data->ppi_start); + nrfx_gppi_conn_enable(data->ppi_end); + nrfx_gppi_conn_enable(data->ppi_clear); + + nrfx_rtc_counter_clear(&keepalive_rtc); + nrfx_rtc_enable(&keepalive_rtc); + + data->hardware_ready = true; + return 0; +} + +static int software_keepalive_init(const struct device *dev) +{ + struct ip5306_data *data = dev->data; + + k_work_init_delayable(&data->keepalive_work, keepalive_work_handler); + return 0; +} + static int ip5306_init_api(const struct device *dev) { const struct ip5306_config *config = dev->config; @@ -95,9 +242,15 @@ static int ip5306_init_api(const struct device *dev) LOG_ERR("IP5306 probe failed (%d)", err); return err; } + ARG_UNUSED(reg_val); data->started = true; data->pulse_active = false; + + if (config->keepalive_hardware) { + return 0; + } + k_work_reschedule(&data->keepalive_work, K_MSEC(keepalive_low_delay_ms(config))); @@ -165,10 +318,13 @@ static int ip5306_dev_init(const struct device *dev) data->dev = dev; data->started = false; data->pulse_active = false; + data->hardware_ready = false; - k_work_init_delayable(&data->keepalive_work, keepalive_work_handler); + if (config->keepalive_hardware) { + return hardware_keepalive_init(dev); + } - return gpio_pin_configure_dt(&config->wakeup_gpio, GPIO_OUTPUT_INACTIVE); + return software_keepalive_init(dev); } static const struct ip5306_driver_api ip5306_driver_api = { @@ -181,11 +337,16 @@ static const struct ip5306_driver_api ip5306_driver_api = { \ static const struct ip5306_config ip5306_config_##inst = { \ .bus = I2C_DT_SPEC_INST_GET(inst), \ + .gpiote = &GPIOTE_NRFX_INST_BY_NODE( \ + NRF_DT_GPIOTE_NODE(DT_DRV_INST(inst), wakeup_gpios)), \ .wakeup_gpio = GPIO_DT_SPEC_INST_GET(inst, wakeup_gpios), \ + .wakeup_pin = NRF_DT_GPIOS_TO_PSEL(DT_DRV_INST(inst), wakeup_gpios), \ .keepalive_interval_ms = \ DT_INST_PROP(inst, keepalive_interval_ms), \ .keepalive_pulse_width_ms = \ DT_INST_PROP(inst, keepalive_pulse_width_ms), \ + .keepalive_hardware = \ + DT_INST_NODE_HAS_PROP(inst, keepalive_hardware), \ }; \ \ DEVICE_DT_INST_DEFINE(inst, ip5306_dev_init, NULL, \ diff --git a/dts/bindings/pmic/injoinic,ip5306.yaml b/dts/bindings/pmic/injoinic,ip5306.yaml index 47aa8d6..3236c90 100644 --- a/dts/bindings/pmic/injoinic,ip5306.yaml +++ b/dts/bindings/pmic/injoinic,ip5306.yaml @@ -1,4 +1,4 @@ -description: Injoinic IP5306 PMIC with software wakeup keepalive support +description: Injoinic IP5306 PMIC with selectable software or hardware wakeup keepalive support compatible: "injoinic,ip5306" @@ -19,3 +19,7 @@ properties: type: int required: true description: Active-high pulse width for the keepalive wakeup GPIO. + + keepalive-hardware: + type: boolean + description: Enable RTC2 plus GPPI plus GPIOTE based hardware keepalive pulses. diff --git a/prj.conf b/prj.conf index 28711f7..7462c2d 100644 --- a/prj.conf +++ b/prj.conf @@ -3,6 +3,8 @@ CONFIG_CAF_BUTTONS=y CONFIG_CAF_BUTTONS_DEF_PATH="buttons_def.h" CONFIG_GPIO=y CONFIG_I2C=y +CONFIG_NRFX_RTC2=y +CONFIG_NRFX_GPPI=y CONFIG_REBOOT=y CONFIG_SENSOR=y CONFIG_ADC=y