Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@
pinctrl-names = "default", "sleep";
};

/ {
slm_gpio_pins: slm_gpio_pins {
compatible = "slm-gpio-pins";
power-gpios = <&gpio0 8 GPIO_ACTIVE_LOW>;
power-gpios-active-time-ms = <100>;
power-gpios-debounce-time-ms = <50>;
indicate-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
indicate-gpios-active-time-ms = <100>;
//power-gpios = <&gpio0 8 (GPIO_INPUT | GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
};
};

&i2c2 {
status = "disabled";
};
Expand Down
11 changes: 11 additions & 0 deletions applications/serial_lte_modem/overlay-external-mcu.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,14 @@
&uart0 {
status = "disabled";
};

/ {
slm_gpio_pins: slm_gpio_pins {
compatible = "slm-gpio-pins";
power-gpios = <&gpio0 31 GPIO_ACTIVE_LOW>;
power-gpios-active-time-ms = <100>;
power-gpios-debounce-time-ms = <50>;
indicate-gpios = <&gpio0 30 GPIO_ACTIVE_LOW>;
indicate-gpios-active-time-ms = <100>;
};
};
2 changes: 2 additions & 0 deletions applications/serial_lte_modem/src/slm_at_host.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ static int slm_at_send_indicate(const uint8_t *data, size_t len,
return -EFAULT;
}

#if (INDICATE_PIN_IS_ENABLED)
if (indicate) {
enum pm_device_state state = PM_DEVICE_STATE_OFF;

Expand All @@ -513,6 +514,7 @@ static int slm_at_send_indicate(const uint8_t *data, size_t len,
slm_ctrl_pin_indicate();
}
}
#endif

ret = at_backend.send(data, len);
if (!ret) {
Expand Down
95 changes: 47 additions & 48 deletions applications/serial_lte_modem/src/slm_ctrl_pin.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,28 @@

LOG_MODULE_REGISTER(slm_ctrl_pin, CONFIG_SLM_LOG_LEVEL);

#define POWER_PIN_DEBOUNCE_MS 50
#if POWER_PIN_IS_ENABLED

static const struct gpio_dt_spec power_pin_member =
GPIO_DT_SPEC_GET_OR(DT_NODELABEL(slm_gpio_pins), power_gpios, {0});
static const struct gpio_dt_spec indicate_pin_member =
GPIO_DT_SPEC_GET_OR(DT_NODELABEL(slm_gpio_pins), indicate_gpios, {0});
static const int power_pin_debounce_ms =
DT_PROP(DT_NODELABEL(slm_gpio_pins), power_gpios_debounce_time_ms);
static const int indicate_pin_time_ms =
DT_PROP(DT_NODELABEL(slm_gpio_pins), indicate_gpios_active_time_ms);

static const struct device *gpio_dev = DEVICE_DT_GET(DT_NODELABEL(gpio0));
#endif

#if POWER_PIN_IS_ENABLED
static struct gpio_callback gpio_cb;
static struct k_work_delayable indicate_work;
static atomic_t callback_wakeup_running;
#else
BUILD_ASSERT(!IS_ENABLED(CONFIG_SLM_START_SLEEP),
"CONFIG_SLM_START_SLEEP requires CONFIG_SLM_POWER_PIN to be defined.");
"CONFIG_SLM_START_SLEEP requires slm_gpio_pins to be defined in devicetree.");
#endif

static struct k_work_delayable indicate_work;
static atomic_t callback_wakeup_running;

static int ext_xtal_control(bool xtal_on)
{
int err = 0;
Expand Down Expand Up @@ -65,57 +73,41 @@ static int ext_xtal_control(bool xtal_on)
return err;
}

#if POWER_PIN_IS_ENABLED || INDICATE_PIN_IS_ENABLED

static int configure_gpio(gpio_pin_t pin, gpio_flags_t flags)
{
const int err = gpio_pin_configure(gpio_dev, pin, flags);

if (err) {
LOG_ERR("Failed to configure GPIO pin P0.%d. (%d)", pin, err);
return err;
}

return 0;
}
#endif

#if POWER_PIN_IS_ENABLED

static int configure_power_pin_interrupt(gpio_callback_handler_t handler, gpio_flags_t flags)
{
int err;
const gpio_pin_t pin = CONFIG_SLM_POWER_PIN;

/* First disable the previously configured interrupt. Somehow when in idle mode if
* the wake-up interrupt is configured to be on an edge the power consumption
* drastically increases (3x), which is why it is configured to be level-triggered.
* When entering idle for some reason first disabling the previously edge-level
* configured interrupt is also needed to keep the power consumption down.
*/
err = gpio_pin_interrupt_configure(gpio_dev, pin, GPIO_INT_DISABLE);
err = gpio_pin_interrupt_configure_dt(&power_pin_member, GPIO_INT_DISABLE);
if (err) {
LOG_ERR("Failed to configure %s (0x%x) on power pin. (%d)",
"interrupt", GPIO_INT_DISABLE, err);
}

err = gpio_pin_interrupt_configure(gpio_dev, pin, flags);
err = gpio_pin_interrupt_configure_dt(&power_pin_member, flags);
if (err) {
LOG_ERR("Failed to configure %s (0x%x) on power pin. (%d)",
"interrupt", flags, err);
return err;
}

gpio_init_callback(&gpio_cb, handler, BIT(pin));
gpio_init_callback(&gpio_cb, handler, BIT(power_pin_member.pin));

err = gpio_add_callback(gpio_dev, &gpio_cb);
err = gpio_add_callback_dt(&power_pin_member, &gpio_cb);
if (err) {
LOG_ERR("Failed to configure %s (0x%x) on power pin. (%d)", "callback", flags, err);
return err;
}

LOG_DBG("Configured interrupt (0x%x) on power pin (%u) with handler (%p).",
flags, pin, (void *)handler);
flags, power_pin_member.pin, (void *)handler);
return 0;
}

Expand All @@ -130,16 +122,18 @@ static void power_pin_callback_poweroff(const struct device *dev,
static K_WORK_DEFINE(work, slm_ctrl_pin_enter_sleep_work_fn);

LOG_INF("Power off triggered.");
gpio_remove_callback(dev, gpio_callback);
gpio_remove_callback_dt(&power_pin_member, gpio_callback);
k_work_submit(&work);
}

#endif /* POWER_PIN_IS_ENABLED */

#if (INDICATE_PIN_IS_ENABLED)

static void indicate_stop(void)
{
#if (INDICATE_PIN_IS_ENABLED)
if (gpio_pin_set(gpio_dev, CONFIG_SLM_INDICATE_PIN, 0) != 0) {
if (gpio_pin_set_dt(&indicate_pin_member, 0) != 0) {
LOG_WRN("GPIO_0 set error");
}
LOG_DBG("Stop indicating");
Expand All @@ -153,6 +147,8 @@ static void indicate_wk(struct k_work *work)
indicate_stop();
}

#endif

#if POWER_PIN_IS_ENABLED

static void power_pin_callback_enable_poweroff_fn(struct k_work *)
Expand All @@ -166,10 +162,10 @@ static K_WORK_DELAYABLE_DEFINE(work_poweroff, power_pin_callback_enable_poweroff
static void power_pin_callback_enable_poweroff(const struct device *dev,
struct gpio_callback *gpio_callback, uint32_t)
{
LOG_INF("Enabling the poweroff interrupt shortly...");
gpio_remove_callback(dev, gpio_callback);
LOG_INF("Enabling the poweroff interrupt after debounce time %d...", power_pin_debounce_ms);
gpio_remove_callback_dt(&power_pin_member, gpio_callback);

k_work_reschedule(&work_poweroff, K_MSEC(POWER_PIN_DEBOUNCE_MS));
k_work_reschedule(&work_poweroff, K_MSEC(power_pin_debounce_ms));
}

static void power_pin_callback_wakeup_work_fn(struct k_work *)
Expand All @@ -189,7 +185,7 @@ static void power_pin_callback_wakeup_work_fn(struct k_work *)
err = slm_at_host_power_on();
if (err) {
LOG_ERR("Failed to power on uart: %d. Resetting SLM.", err);
gpio_remove_callback(gpio_dev, &gpio_cb);
gpio_remove_callback_dt(&power_pin_member, &gpio_cb);
slm_reset();
return;
}
Expand All @@ -208,7 +204,7 @@ static void power_pin_callback_wakeup(const struct device *dev,
}

LOG_INF("Resuming from idle shortly...");
gpio_remove_callback(dev, gpio_callback);
gpio_remove_callback_dt(&power_pin_member, gpio_callback);

/* Enable the poweroff interrupt only when the pin will be back to a nonactive state. */
configure_power_pin_interrupt(power_pin_callback_enable_poweroff, GPIO_INT_EDGE_FALLING);
Expand All @@ -224,12 +220,12 @@ int slm_ctrl_pin_indicate(void)
if (k_work_delayable_is_pending(&indicate_work)) {
return 0;
}
LOG_DBG("Start indicating");
err = gpio_pin_set(gpio_dev, CONFIG_SLM_INDICATE_PIN, 1);
LOG_DBG("Start indicating for %d ms", indicate_pin_time_ms);
err = gpio_pin_set_dt(&indicate_pin_member, 1);
if (err) {
LOG_ERR("GPIO_0 set error: %d", err);
} else {
k_work_reschedule(&indicate_work, K_MSEC(CONFIG_SLM_INDICATE_TIME));
k_work_reschedule(&indicate_work, K_MSEC(indicate_pin_time_ms));
}
#endif
return err;
Expand All @@ -240,7 +236,7 @@ void slm_ctrl_pin_enter_idle(void)
LOG_INF("Entering idle.");
int err;

gpio_remove_callback(gpio_dev, &gpio_cb);
gpio_remove_callback_dt(&power_pin_member, &gpio_cb);

err = configure_power_pin_interrupt(power_pin_callback_wakeup, GPIO_INT_LEVEL_LOW);
if (err) {
Expand Down Expand Up @@ -270,7 +266,7 @@ void slm_ctrl_pin_enter_sleep_no_uninit(void)
{
LOG_INF("Entering sleep.");
LOG_PANIC();
nrf_gpio_cfg_sense_set(CONFIG_SLM_POWER_PIN, NRF_GPIO_PIN_SENSE_LOW);
nrf_gpio_cfg_sense_set(power_pin_member.pin, NRF_GPIO_PIN_SENSE_LOW);

k_sleep(K_MSEC(100));

Expand All @@ -286,11 +282,11 @@ void slm_ctrl_pin_enter_shutdown(void)

/* De-configure GPIOs */
#if POWER_PIN_IS_ENABLED
gpio_pin_interrupt_configure(gpio_dev, CONFIG_SLM_POWER_PIN, GPIO_INT_DISABLE);
gpio_pin_configure(gpio_dev, CONFIG_SLM_POWER_PIN, GPIO_DISCONNECTED);
gpio_pin_interrupt_configure_dt(&power_pin_member, GPIO_INT_DISABLE);
gpio_pin_configure_dt(&power_pin_member, GPIO_DISCONNECTED);
#endif
#if INDICATE_PIN_IS_ENABLED
gpio_pin_configure(gpio_dev, CONFIG_SLM_INDICATE_PIN, GPIO_DISCONNECTED);
gpio_pin_configure_dt(&indicate_pin_member, GPIO_DISCONNECTED);
#endif

k_sleep(K_MSEC(100));
Expand All @@ -301,32 +297,35 @@ void slm_ctrl_pin_enter_shutdown(void)

void slm_ctrl_pin_init_gpios(void)
{
if (!device_is_ready(gpio_dev)) {
#if POWER_PIN_IS_ENABLED
if (!gpio_is_ready_dt(&power_pin_member)) {
LOG_ERR("GPIO controller not ready");
return;
}

#if POWER_PIN_IS_ENABLED
(void)configure_gpio(CONFIG_SLM_POWER_PIN, GPIO_INPUT | GPIO_PULL_UP | GPIO_ACTIVE_LOW);
LOG_WRN("power_pin_member %d", power_pin_member.pin);
/* TODO: Flags into DTS */
gpio_pin_configure_dt(&power_pin_member, GPIO_INPUT | GPIO_PULL_UP);
#endif

#if INDICATE_PIN_IS_ENABLED
(void)configure_gpio(CONFIG_SLM_INDICATE_PIN, GPIO_OUTPUT_INACTIVE | GPIO_ACTIVE_LOW);
LOG_WRN("indicate_pin_member %d", indicate_pin_member.pin);
gpio_pin_configure_dt(&indicate_pin_member, GPIO_OUTPUT_INACTIVE);
#endif
}

int slm_ctrl_pin_init(void)
{
int err;

k_work_init_delayable(&indicate_work, indicate_wk);

err = ext_xtal_control(true);
if (err < 0) {
LOG_ERR("Failed to enable ext XTAL: %d", err);
return err;
}
#if POWER_PIN_IS_ENABLED
k_work_init_delayable(&indicate_work, indicate_wk);

/* Do not directly enable the poweroff interrupt so that only a full toggle triggers
* power off. This is because power on is triggered on low level, so if the pin is held
* down until SLM is fully initialized releasing it would directly trigger the power off.
Expand Down
4 changes: 2 additions & 2 deletions applications/serial_lte_modem/src/slm_defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ enum {
#define SLM_NRF52_BLK_SIZE 4096 /** nRF52 flash block size for write operation */
#define SLM_NRF52_BLK_TIME 2000 /** nRF52 flash block write time in millisecond (1.x second) */

#define POWER_PIN_IS_ENABLED (CONFIG_SLM_POWER_PIN != -1)
#define INDICATE_PIN_IS_ENABLED (CONFIG_SLM_INDICATE_PIN != -1)
#define POWER_PIN_IS_ENABLED DT_NODE_HAS_PROP(DT_NODELABEL(slm_gpio_pins), power_gpios)
#define INDICATE_PIN_IS_ENABLED DT_NODE_HAS_PROP(DT_NODELABEL(slm_gpio_pins), indicate_gpios)

#endif
33 changes: 33 additions & 0 deletions dts/bindings/gpio/slm-gpio-pins.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
description: GPIO pins to control Serial LTE Modem

compatible: "slm-gpio-pins"

properties:
power-gpios:
type: phandle-array
required: true
description: |
Power pin.

power-gpios-active-time-ms:
type: int
default: 100
description: |
Power pin active time in milliseconds.

power-gpios-debounce-time-ms:
type: int
default: 50
description: |
Power pin debounce time in milliseconds.

indicate-gpios:
type: phandle-array
description: |
Indicate pin.

indicate-gpios-active-time-ms:
type: int
default: 100
description: |
Indicate pin active time in milliseconds.
Loading
Loading