Regulator fixed is broken in nrf sdk 3.2.1

After upgrading nRF Connect SDK from 3.1.1 to 3.2.1, our fixed regulator stopped working. We are on nRF54L15. Tracking it down further, this seems to be some sort of GPIO value config/retention bug. The reason I'm saying "bug" is that this was working as expected on 3.1.1. Our regulator is defined in the dts to go high on boot.

load_3v3: load-3v3 {
  compatible = "regulator-fixed";
  status = "okay";
  regulator-name = "3V3_LOAD_EN";
  enable-gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;
  regulator-boot-on;
};

If you look at regulator_fixed_init() in the latest SDK, it configures the output pin, then reads the value. That value is passed regulator_common_init(), which only toggles the line high if it's not already `is_enabled`. However, for some reason, the gpio read of the pin reads HIGH immediately after config. This causes regulator_common_init() to think it's already on, when it's not actually on. If I add a 10ms delay and read the same value again, it correctly reads 0. In the logs below, the reading of value after sleep doesn't change is_enabled because that was added by me, is_enabled is still based on the value after the first read like in the unmodified code.

[00:00:00.323,296] <wrn> regulator_fixed: Regulator before config: 0
[00:00:00.323,296] <wrn> regulator_fixed: Regulator after config 1
[00:00:00.323,296] <wrn> regulator_fixed: Sleeping for 10ms
[00:00:00.333,408] <wrn> regulator_fixed: Regulator after 10ms sleep: 0
[00:00:00.333,408] <wrn> regulator_fixed: Calling regulator_common_init with is_enabled=1

Why does gpio_pin_get_dt() return 1 temporarily? This breaks the regulator setup.

Instrumented regulator_fixed_init with logs, but otherwise unchanged:

static int regulator_fixed_init(const struct device *dev)
{
  const struct regulator_fixed_config *cfg = dev->config;
  bool is_enabled = false;

  regulator_common_data_init(dev);

  if (cfg->enable.port != NULL) {
    if (!gpio_is_ready_dt(&cfg->enable)) {
     LOG_ERR("GPIO port: %s not ready", cfg->enable.port->name);
     return -ENODEV;
    }

    // DEBUG CODE
    LOG_WRN("Regulator before config: %d", gpio_pin_get_dt(&cfg->enable));
    // END DEBUG CODE

    int ret = gpio_pin_configure_dt(&cfg->enable, GPIO_OUTPUT);

    if (ret < 0) {
       return ret;
    }

    ret = gpio_pin_get_dt(&cfg->enable);

    if (ret < 0) {
      return ret;
    }

    is_enabled = ret;

    // DEBUG CODE
    LOG_WRN("Regulator after config %d", is_enabled);
    LOG_WRN("Sleeping for 10ms");

    k_sleep(K_MSEC(10));

    LOG_WRN("Regulator after 10ms sleep: %d", gpio_pin_get_dt(&cfg->enable));
    // END DEBUG CODE
  }

  LOG_WRN("Calling regulator_common_init with is_enabled=%d", is_enabled);
  return regulator_common_init(dev, is_enabled);
}

This issue has surfaced because of this commit in zephyr: https://github.com/nrfconnect/sdk-zephyr/commit/7fa815c92967f26a7c9af33776677015013e18d0, which checks the value of the pin before deciding if it should be toggled.

  • Hello,

    I did not have any luck replicating the issue. In my case it is always being read as '0' as you can see from the screen dump below. This was tested on a nRF54L15 DK with SDK v3.2.1.  

    I also find it strange that we are trying to read the GPIO input right after we have configured it as an output. The gpio_pin_get_dt() will read the GPIO.IN register which is only valid when the pin is configured as an input. Perhaps it works differently on other platforms. 

    Attached is the sample I used for test, perhaps you could have a look at it to see if we are doing anything differently?

    Best regards,

    Vidar

    regulator_fixed_test.zip

  • Thanks, I see the same thing. Both your example and our project behave fine on the nrf54l15 dk. This may be some sort of hardware issue on our end. But to your point, what's in the GPIO.IN if the pin is set to output? How/why could it read 1 after configuring it as an output? Is it possible there's some pull up or capacitance? Anything happens on the GPIO during config?

    If you agree that zephyr's driver is incorrect, even if it seems to result in correct behavior on the dk, what/how should that be addressed?

    Update: after speaking with our HW eng, we identified that the pull-up and decoupling cap on that regulator line were causing this issue. It seems that configuring the pin as the output briefly pulls up the line and GPIO.IN reads 1 for a few milliseconds. When we removed the pull-up and the capacitor, that issue went away. It seems that even if the pin is not set to input, the GPIO.IN register does do something.

    So the takeway is that if any customers are using fixed regulator with a pull-up/decoupling cap config, they can not use current zephyr regulator driver. Is that accurate?

  • Actually, after supplying VDD to the GPIO pad, I’m also reading ‘1’ in the regulator init function. I was certain that it was not possible to read the logic level from an output, but since it appears to be possible, could there be that there is voltage on this line that needs some time to discharge before it goes low? Do you see the same if you do a full power cycle of the board? Either way, I will discuss the case with my coworkers as well to see if I may be missing something. 

  • Yes. See my update to the post above. We have a pull-up/decoupling cap on the line. Removing those fixed the issue. 

  • I see now that a similar issue has already been reported in upstream Zephyr: https://github.com/zephyrproject-rtos/zephyr/issues/93012 but it has still not been addressed. I don't see the reasoning for reading the output to decide if the regulator is to be enabled in the first place. I think maybe it would make more sense to read the actual pin configuration if anything. Either way, I have reported this internally to see if someone from R&D can follow up and revive this PR. I assume you are able to work around this for now by reverting the commit? 

    Side note: I tested the same on a nRF52840 board, and unlike the nrf54L, it always returns '0' when the input buffer is disconnected.

Related