Dynamically control nPM1300 Load Switch 1 from firmware

I’m working on a project using the nRF54L15 SoC together with the nPM1300 Power Management IC.
In my hardware setup:

  • BUCK2 output from the nPM1300 is connected to the LSIN1 pin.

  • The LSOUT1 pin powers an I²C sensor.

The goal is to dynamically turn the sensor power on/off from firmware by controlling the Load Switch 1 (LDSW1) via I²C, not via the GPIO control pins. I’m using the load switch mode instead of the LDO mode, because the LDO mode supports up to 50 mA, while the load switch mode supports up to 100 mA — which better suits the sensor’s power requirements.

I’ve configured the nPM1300 in the Devicetree as an I²C peripheral and defined the npm1300_ldsw1 regulator node:

#include <dt-bindings/regulator/npm1300.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>

&pinctrl {
    i2c22_default: i2c22_default {
        group1 {
            psels = <NRF_PSEL(TWIM_SDA, 1, 12)>,
                    <NRF_PSEL(TWIM_SCL, 1, 11)>;
        };
    };
    i2c22_sleep: i2c22_sleep {
        group1 {
            psels = <NRF_PSEL(TWIM_SDA, 1, 12)>,
                    <NRF_PSEL(TWIM_SCL, 1, 11)>;
            low-power-enable;
        };
    };
};

&i2c22 {
    status = "okay";
    pinctrl-0 = <&i2c22_default>;
    pinctrl-1 = <&i2c22_sleep>;
    pinctrl-names = "default", "sleep";

    
    sht4x: sht4x@44 {
        status = "okay";
        compatible = "sensirion,sht4x";
        reg = <0x44>;
        repeatability = <0>;
    };

    scd41@62 {
        compatible = "sensirion,scd41";
        reg = <0x62>;
        status = "okay";
    };


    npm1300: pmic@6b {
        compatible = "nordic,npm1300";
        reg = <0x6b>;

        npm1300_gpios: gpio-controller {
            compatible = "nordic,npm1300-gpio";
            gpio-controller;
            #gpio-cells = <2>;
            ngpios = <5>;
            status = "disabled";
        };

        npm1300_regulators: npm1300_regulators {
            compatible = "nordic,npm1300-regulator";
            status = "okay";

            npm1300_buck1: BUCK1 {
                status = "okay";
                regulator-min-microvolt = <1000000>;
                regulator-max-microvolt = <3300000>;
                regulator-init-microvolt =  <1800000>;
                retention-microvolt = <1200000>;
            };

            npm1300_buck2: BUCK2 {
                status = "okay";
                regulator-min-microvolt = <1000000>;
                regulator-max-microvolt = <3300000>;
                regulator-init-microvolt =  <3300000>;
                retention-microvolt = <1800000>;
            };

            /* Load switches */
            npm1300_ldsw1: LDO1 {
                status = "okay";
                regulator-min-microvolt = <1000000>;
                regulator-max-microvolt = <3300000>;
                regulator-initial-mode = <NPM1300_LDSW_MODE_LDSW>;
            };

            npm1300_ldsw2: LDO2 {
                status = "disabled";
                regulator-min-microvolt = <3300000>;
                regulator-max-microvolt = <3300000>;
                regulator-initial-mode = <NPM1300_LDSW_MODE_LDSW>;
            };
        };

        /*
        npm1300_charger: charger {
            compatible = "nordic,npm1300-charger";
            term-microvolt = <4150000>;
            current-microamp = <150000>;
            dischg-limit-microamp = <1000000>;
            vbus-limit-microamp = <500000>;
            thermistor-ohms = <10000>;
            thermistor-beta = <3380>;
            charging-enable;
            status = "okay";
        };
        */

        npm1300_leds: leds {
            compatible = "nordic,npm1300-led";
            nordic,led0-mode = "error";
            nordic,led1-mode = "charging";
            nordic,led2-mode = "host";
        };
    };
};

And current my main.c:

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/regulator.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(main_app, LOG_LEVEL_INF);

void main(void)
{
    const struct device *sht_dev = DEVICE_DT_GET(DT_NODELABEL(sht4x));
    const struct device *ldsw1 = DEVICE_DT_GET(DT_NODELABEL(npm1300_ldsw1));
    struct sensor_value temp, humidity;
   
    int ret;

    if (!device_is_ready(ldsw1)) {
        LOG_ERR("LOADSW1 device not ready");
        return;
    }

    if (!device_is_ready(sht_dev)) {
        LOG_ERR("SHT4x device not ready");
        return;
    }

    LOG_INF("Starting sensor power control via LOADSW1");

    while (1) {
        /* Turn on sensor power */
        if (!regulator_is_enabled(ldsw1))
            ret = regulator_enable(ldsw1);

        if (ret) {
            LOG_ERR("Failed to enable LOADSW1: %d", ret);
            k_sleep(K_SECONDS(10));
            continue;
        }

        LOG_INF("Sensor power ON (via LOADSW1)");

        k_sleep(K_MSEC(5)); // settle time

        ret = sensor_sample_fetch(sht_dev);
        if (!ret) {
            sensor_channel_get(sht_dev, SENSOR_CHAN_AMBIENT_TEMP, &temp);
            sensor_channel_get(sht_dev, SENSOR_CHAN_HUMIDITY, &humidity);
            LOG_INF("Temp: %d.%06d C, RH: %d.%06d%%", 
                    temp.val1, temp.val2, humidity.val1, humidity.val2);
        } else {
            LOG_ERR("Sensor read failed: %d", ret);
        }

        k_sleep(K_SECONDS(10));

        /* Power off sensor */
    
        if (regulator_is_enabled(ldsw1))
            ret = regulator_disable(ldsw1);

        if (ret) {
            LOG_ERR("Failed to disable LOADSW1: %d", ret);
            k_sleep(K_SECONDS(10));
            continue;
        }

        LOG_INF("Sensor power OFF");
        
        k_sleep(K_SECONDS(10));
    }
}

However, the LSOUT1 pin always remains at 3.3 V. I’d like to confirm whether the current nPM1300 Zephyr driver supports controlling the load switch (LDSW) via I²C instead of GPIO, or if I’m missing something in my configuration.

Parents Reply
  • Just tried your code without any modifications (only added `bias-pull-up` to the i2c pins configuration and commented out the sensor code since I don't have it) on 2.8, and I get the expected behavior again...

    If you have a logic analyzer, do you think you could examine the I2C lines during your code execution? You should see the writes as on the screenshot above. You will of course have more due to the sensor communication, but these are responsible for enabling and disabling the LDSW1 respectively. 

Children
  • I connected an oscilloscope to the LDSW1 output and found the issue. I had the sensor board already connected to the LDSW1 power output, and the board included a large decoupling capacitor. Because of that, when the sensor board was attached, I didn’t realize how slowly the LDSW1 was turning on and off—it gave the impression of a stable power output, since I was enabling the LDSW1 again quickly enough that the voltage level never had time to drop all the way to 0 V. That’s when I realized I needed to enable the active-discharge property on my LD1 regulator:

    /* Load switches */
    npm1300_ek_ldo1: LDO1 {
        status = "okay";
        regulator-min-microvolt = <1000000>;
        regulator-max-microvolt = <3300000>;
        regulator-initial-mode = <NPM13XX_LDSW_MODE_LDSW>;
        active-discharge;
    };

    Once I did that, I started seeing the power turn on/off much faster. Thanks for your help!

    Now I’m facing another issue: I have an SHT4x sensor powered by the LDSW1. At boot time, the SHT4x isn’t powered yet because it’s behind the LDSW1 load switch, which is controlled by the PMIC. During system boot, Zephyr automatically tries to probe the SHT4x as part of its device initialization sequence—this happens before main(). Since the sensor isn’t powered yet, the I²C transactions fail (no ACK from the SHT4x), causing the init function to return a negative value. As a result, Zephyr marks the device as unready:

    device->state->initialized = false;

    Later in the application, when calling:

    device_is_ready(sht_dev)
    

    It returns false because the initialization previously failed.

    What’s the recommended way to handle this kind of situation, where a sensor is powered on by the PMIC only after the boot sequence?

    I wasn’t sure whether it was best to continue discussing this issue in the same ticket, since the problem—although related to the nPM1300 and Load Switch 1—is quite different. Therefore, I opened a new ticket for it. 

Related