IO Expander : unable to request reset_gpio (-16)

Hi,

I got following error while attempting to use an output from the IO Expander as the reset-gpios for the camera sensor in Jetpack-6.2 :

nvidia@ORNN:~$ sudo dmesg | grep toto
[   12.596474] toto 7-0010: probing toto v4l2 sensor at addr 0x10
[   12.596720] toto 7-0010: supply vana not found, using dummy regulator
[   12.603744] toto 7-0010: camera_common_regulator_get: vana
[   12.603766] toto 7-0010: supply vdig not found, using dummy regulator
[   12.618172] toto 7-0010: camera_common_regulator_get: vdig
[   12.618200] toto 7-0010: supply vana not found, using dummy regulator
[   12.618990] toto 7-0010: camera_common_regulator_get: vana
[   12.618999] toto 7-0010: toto_power_get: unable to request reset_gpio (-16)
[   12.619004] toto 7-0010: unable to power get
[   12.619007] toto 7-0010: tegra camera driver registration failed
[   12.620628] toto: probe of 7-0010 failed with error -14

The IO Expander is well registered and enumerates the IOs under its control :

nvidia@ORNN:~$ sudo cat /sys/kernel/debug/gpio
gpiochip2: GPIOs 308-315, parent: i2c/7-0027, mcp23008, can sleep:
 gpio-309 (                    |mcp23008_27_output_h) in  hi 
 gpio-310 (                    |mcp23008_27_output_h) in  hi 
 gpio-311 (                    |mcp23008_27_output_h) in  hi 

Relevant device-tree node :

       i2c@c250000 {
            status = "okay";
            #address-cells = <1>;
            #size-cells = <0>;

            mcp23008_27: gpio@27 {
                compatible = "microchip,mcp23008";
                gpio-controller;
                #gpio-cells = <2>;
                reg = <0x27>;
                status = "okay";

//                interrupt-parent = <&gpio_aon>; // gpiochip1: registered GPIOs 316 to 347 on tegra234-gpio-aon
//                interrupts = <TEGRA234_AON_GPIO(CC, 3) IRQ_TYPE_EDGE_FALLING>; // GPIO6/GPIO06/PCC.03/331
//                irq-gpios = <&gpio_aon TEGRA234_AON_GPIO(CC, 3) GPIO_ACTIVE_HIGH>; // GPIO6/GPIO06/PCC.03/331
                reset-gpios = <&gpio TEGRA234_MAIN_GPIO(P, 6) GPIO_ACTIVE_LOW>; // GPIO2/GPIO02/PP.06/446
                interrupt-controller;
                #interrupt-cells=<2>;
                microchip,irq-mirror;
                microchip,irq-active-high;

                mcp23008_27_output_low {
                    status = "disabled";
                };

                mcp23008_27_output_high {
                    gpio-hog;
                    output-high;
                    gpios = <1 0 2 0 3 0>;
                };
            };

            toto_a@10 {
                status = "okay";
                compatible = "sony,toto";

                /* I2C device address */
                reg = <0x10>;

                /* V4L2 device node location */
                devnode = "video0";

                /* Physical dimensions of sensor */
                physical_w = "1.944";
                physical_h = "1.097";

                sensor_model = "toto";
                use_sensor_mode_id = "false";

                /* Define any required hw resources needed by driver */
                /* ie. clocks, io pins, power sources */
                /* mclk-index indicates the index of the */
                /* mclk-name with in the clock-names array */
                avdd-reg = "vana";
                iovdd-reg = "vif";
                dvdd-reg = "vdig";

                //vana-supply = <&p3509_avdd_cam_2v8>;
                //vif-supply = <&p3509_vdd_1v8_cvb>;
                //vana-supply = <&p3509_vdd_3v3_cvb>;
                //vdig-supply = <&p3509_vdd_sys_en>;
                clocks = <&bpmp TEGRA234_CLK_EXTPERIPH1>,
                         <&bpmp TEGRA234_CLK_EXTPERIPH1>;
                clock-names = "extperiph1", "pllp_grtba";
                mclk = "extperiph1";
                clock-frequency = <24000000>;

                reset-gpios = <&mcp23008_27 3 GPIO_ACTIVE_LOW>;

                mode0 { //1920x1080_60Fps

Relevant source code :

static int toto_power_get(struct tegracam_device *tc_dev)
{
	struct device *dev = tc_dev->dev;
	struct camera_common_data *s_data = tc_dev->s_data;
	struct camera_common_power_rail *pw = s_data->power;
	struct camera_common_pdata *pdata = s_data->pdata;
	struct clk *parent;
	int err = 0;

	if (!pdata) {
		dev_err(dev, "pdata missing\n");
		return -EFAULT;
	}

	/* Sensor MCLK (aka. INCK) */
	if (pdata->mclk_name) {
		pw->mclk = devm_clk_get(dev, pdata->mclk_name);
		if (IS_ERR(pw->mclk)) {
			dev_err(dev, "unable to get clock %s\n",
				pdata->mclk_name);
			return PTR_ERR(pw->mclk);
		}

		if (pdata->parentclk_name) {
			parent = devm_clk_get(dev, pdata->parentclk_name);
			if (IS_ERR(parent)) {
				dev_err(dev, "unable to get parent clock %s",
					pdata->parentclk_name);
			} else
				clk_set_parent(pw->mclk, parent);
		}
	}

	/* analog 3.3v */
	if (pdata->regulators.avdd)
		err |= camera_common_regulator_get(dev,
				&pw->avdd, pdata->regulators.avdd);
	if (err) {
		dev_err(dev, "%s: unable to get regulator(s)\n", __func__);
		goto done;
	}
	/* dig 1.2v */
	if (pdata->regulators.dvdd)
		err |= camera_common_regulator_get(dev,
				&pw->dvdd, pdata->regulators.dvdd);
	if (err) {
		dev_err(dev, "%s: unable to get regulator(s)\n", __func__);
		goto done;
	}
	/* vdd 1.8v */
	if (pdata->regulators.iovdd)
		err |= camera_common_regulator_get(dev,
				&pw->avdd, pdata->regulators.avdd);
	if (err) {
		dev_err(dev, "%s: unable to get regulator(s)\n", __func__);
		goto done;
	}
	/* Reset or ENABLE GPIO */
	pw->reset_gpio = pdata->reset_gpio;
	err = gpio_request(pw->reset_gpio, "cam_reset_gpio");
	if (err < 0) {
		dev_err(dev, "%s: unable to request reset_gpio (%d)\n",
			__func__, err);
		goto done;
	}

done:
	pw->state = SWITCH_OFF;

	return err;
}

Could you give a hint to debug this as this was working well with Jetpack-5, please ?

Thanks in advance and best regards,
Khang

Hi khang.l4es,

Are you using the devkit or custom board for Orin Nano?

It seems your camera can not request the gpio successfully.

Please request your vendor to know how to port/configure this mcp23008 I2C I/O expander module.
We are not clear about your camera and I/O expander driver since they are not developed from us.

I would suggest checking the full dmesg from the error logs first.

Hi @KevinFFF,

This is our own design / carrier board and it was working fine in older Jetpacks. However, there’s a large jump in terms of kernel version this time, so not really sure that that it is still compatible.

First, please check if CONFIG_PINCTRL_MCP23S08 is enabled in current JP6.2 release.

And you can compare the device tree and dmesg of these 2 release.
Or you can even port the driver from JP5 to JP6 to clarify if the issue is caused from something updated in driver.

Hi @KevinFFF,

Or you can even port the driver from JP5 to JP6 to clarify if the issue is caused from something updated in driver.

I have been doing this whenever there’s a new major Jetpack version released as our hardware design is rarely changed. Same device-tree configuration for the IO Expander works fine with Jetpack-5 (as in previous comment) however.

First, please check if CONFIG_PINCTRL_MCP23S08 is enabled in current JP6.2 release.

This is not enabled by default but I did it by myself. The evidence :

nvidia@ORNN:~$ sudo dmesg | grep mcp 
[    2.266832] gpio-309 (mcp23008_27_output_high): hogged as output/high
[    2.267065] gpio-310 (mcp23008_27_output_high): hogged as output/high
[    2.267293] gpio-311 (mcp23008_27_output_high): hogged as output/high
[    2.267359] mcp230xx 7-0027: try to register 8 pins ...
[    2.267368] pinctrl core: registered pin 0 (gpio0) on mcp23xxx-pinctrl
[    2.267374] pinctrl core: registered pin 1 (gpio1) on mcp23xxx-pinctrl
[    2.267375] pinctrl core: registered pin 2 (gpio2) on mcp23xxx-pinctrl
[    2.267377] pinctrl core: registered pin 3 (gpio3) on mcp23xxx-pinctrl
[    2.267379] pinctrl core: registered pin 4 (gpio4) on mcp23xxx-pinctrl
[    2.267380] pinctrl core: registered pin 5 (gpio5) on mcp23xxx-pinctrl
[    2.267382] pinctrl core: registered pin 6 (gpio6) on mcp23xxx-pinctrl
[    2.267384] pinctrl core: registered pin 7 (gpio7) on mcp23xxx-pinctrl
[    2.267389] mcp230xx 7-0027: no hogs found

and :

nvidia@ORNN:~$ sudo cat /sys/kernel/debug/gpio
gpiochip2: GPIOs 308-315, parent: i2c/7-0027, mcp23008, can sleep:
 gpio-309 (                    |mcp23008_27_output_h) in  hi 
 gpio-310 (                    |mcp23008_27_output_h) in  hi 
 gpio-311 (                    |mcp23008_27_output_h) in  hi 

And due to the error toto_power_get: unable to request reset_gpio (-16) caused by the following code :

/* Reset or ENABLE GPIO */
	pw->reset_gpio = pdata->reset_gpio;
	err = gpio_request(pw->reset_gpio, "cam_reset_gpio");
	if (err < 0) {
		dev_err(dev, "%s: unable to request reset_gpio (%d)\n",
			__func__, err);
		goto done;
	}

I added some debug messages in the relevant source file (gpiolib.c) :

--- /devel/Khang/SW/jetson-orin-bsp-jp6x/nvidia-kernel-jammy-src-test/drivers/gpio/gpiolib.c	2025-03-19 19:44:30.490483894 +0700
+++ /devel/Khang/SW/jetson-orin-bsp-jp6x-build/build/kernel/kernel-jammy-src/drivers/gpio/gpiolib.c	2025-03-26 06:37:35.255062337 +0700
@@ -1907,6 +1907,8 @@
 	unsigned long		flags;
 	unsigned		offset;
 
+	gpiod_warn(desc, "%s: [L4ES] label %s\n", __func__, label);
+
 	if (label) {
 		label = kstrdup_const(label, GFP_KERNEL);
 		if (!label)
@@ -1923,6 +1925,8 @@
 		desc_set_label(desc, label ? : "?");
 	} else {
 		ret = -EBUSY;
+		gpiod_err(desc, "%s: [L4ES] error %d\n", __func__, ret);
+
 		goto out_free_unlock;
 	}
 
@@ -1939,6 +1943,9 @@
 		if (ret) {
 			desc_set_label(desc, NULL);
 			clear_bit(FLAG_REQUESTED, &desc->flags);
+
+			gpiod_err(desc, "%s: [L4ES] error %d\n", __func__, ret);
+
 			goto out_free_unlock;
 		}
 	}
@@ -2012,7 +2019,7 @@
 	}
 
 	if (ret)
-		gpiod_dbg(desc, "%s: status %d\n", __func__, ret);
+		gpiod_err(desc, "%s: [L4ES] status %d\n", __func__, ret);
 
 	return ret;
 }

And in runtime, it gave :

nvidia@ORNN:~$ sudo dmesg | grep L4ES
[    2.266302] gpio-446 (?): gpiod_request_commit: [L4ES] label reset
[    2.266596] gpio-309 (?): gpiod_request_commit: [L4ES] label mcp23008_27_output_high
[    2.266840] gpio-310 (?): gpiod_request_commit: [L4ES] label mcp23008_27_output_high
[    2.267067] gpio-311 (?): gpiod_request_commit: [L4ES] label mcp23008_27_output_high
[    2.288860] gpio-348 (?): gpiod_request_commit: [L4ES] label regulator-vdd-3v3-sd
[    2.289026] gpio-321 (?): gpiod_request_commit: [L4ES] label regulator-vdd-3v3-pcie
[    2.290141] gpio-341 (?): gpiod_request_commit: [L4ES] label id
[    2.290164] gpio-479 (?): gpiod_request_commit: [L4ES] label vbus
[    2.309187] gpio-383 (?): gpiod_request_commit: [L4ES] label Force Recovery
[    2.309281] gpio-343 (?): gpiod_request_commit: [L4ES] label Power
[    2.309336] gpio-385 (?): gpiod_request_commit: [L4ES] label Suspend
[    2.352021] gpio-390 (?): gpiod_request_commit: [L4ES] label cd
[   10.982125] gpio-311 (mcp23008_27_output_high): gpiod_request_commit: [L4ES] label cam_reset_gpio
[   10.982134] gpio-311 (mcp23008_27_output_high): gpiod_request_commit: [L4ES] error -16
[   10.982138] gpio-311 (mcp23008_27_output_high): gpiod_request: [L4ES] status -16

The gpio number seems ok but I am not sure why there was always -16 (-EBUSY) error.

Hi @KevinFFF,

For updating, I found the reason why the -16 (-EBUSY) error is returned : it is due to the fact that the relevant IO was defined/hogged as output-high in the device-tree

I removed the gpio #3 (which is later defined as the reset-gpios of the camera node) from the gpios list of the IO expander node then there’s no more error :

                mcp23008_27_output_high {
                    gpio-hog;
                    output-high;
                    gpios = <1 0 2 0>;
                };

Do you mean that you’ve managed to get it work after removing mcp23008_27_output_high?
Please note that GPIO pin can only be used by one driver at the same time.

Hi @KevinFFF,

I meant after removing the GPIO in question (which is/are used as reset-gpios of the camera node) from the gpios list of the IO expander node, I had no more -16 (-EBUSY) error and the tegra camera driver registration was therefore successful.

However, the reset-gpios defined under the IO expander node

                xreset-gpios = <&mcp23008_27 1 GPIO_ACTIVE_LOW>;
                reset-gpios = <&mcp23008_27 2 GPIO_ACTIVE_LOW>;
nvidia@ORNN:~$ sudo cat /sys/kernel/debug/gpio
gpiochip2: GPIOs 308-315, parent: i2c/7-0027, mcp23008, can sleep:
 gpio-308 (                    |mcp23008_27_output_h) in  hi 
 gpio-309 (                    |ext_reset_gpio      ) in  lo 
 gpio-310 (                    |cam_reset_gpio      ) in  lo 

seem not to be controlled (via gpio_set_value_cansleep() ) at all (measured by the scope) :

[  504.633075] toto 7-0010: v4l2sd_stream++ enable 1
[  504.633435] toto 7-0010: toto_start_streaming:gpio_set_value_cansleep(pw->xreset_gpio, 1)
[  504.633568] toto 7-0010: toto_start_streaming:gpio_set_value_cansleep(pw->reset_gpio, 1)
[  512.362468] toto 7-0010: v4l2sd_stream++ enable 0
[  512.363055] toto 7-0010: toto_stop_streaming:gpio_set_value_cansleep(pw->xreset_gpio, 0)
[  512.363175] toto 7-0010: toto_stop_streaming:gpio_set_value_cansleep(pw->reset_gpio, 0)
...
[  548.202453] toto 7-0010: v4l2sd_stream++ enable 1
[  548.202626] toto 7-0010: toto_start_streaming:gpio_set_value_cansleep(pw->xreset_gpio, 1)
[  548.202749] toto 7-0010: toto_start_streaming:gpio_set_value_cansleep(pw->reset_gpio, 1)
[  550.710519] toto 7-0010: v4l2sd_stream++ enable 0
[  550.711018] toto 7-0010: toto_stop_streaming:gpio_set_value_cansleep(pw->xreset_gpio, 0)
[  550.711168] toto 7-0010: toto_stop_streaming:gpio_set_value_cansleep(pw->reset_gpio, 0)

Best Regards,
Khang

Why they are configured as Input pins but it seems you want to control them?

Could you try using gpiod_set_value_cansleep() instead?

Hi @KevinFFF,

Why they are configured as Input pins but it seems you want to control them?

In the initial comment, I defined them as gpio-hog & output-high but they were still seen as input. And gave -16 (-EBUSY) error when being requested as reset-gpios of the camera node.

I then removed them from the gpio-hog & output-high definition list and there was no more -16 (-EBUSY) error.

Could you try using gpiod_set_value_cansleep() instead?

I tried it :

[   11.125047] toto 7-0010: toto_power_on:gpiod_set_value_cansleep(gpio_to_desc(pw->reset_gpio), 0)
[   35.159692] toto 7-0010: toto_power_on:gpiod_set_value_cansleep(gpio_to_desc(pw->reset_gpio), 0)
[   35.183690] toto 7-0010: toto_start_streaming:gpiod_set_value_cansleep(gpio_to_desc(pw->xreset_gpio), 1)
[   35.183814] toto 7-0010: toto_start_streaming:gpiod_set_value_cansleep(gpio_to_desc(pw->reset_gpio), 1)
[   89.022537] toto 7-0010: toto_stop_streaming:gpiod_set_value_cansleep(gpio_to_desc(pw->xreset_gpio), 0)
[   89.022676] toto 7-0010: toto_stop_streaming:gpiod_set_value_cansleep(gpio_to_desc(pw->reset_gpio), 0)
[   89.027932] toto 7-0010: toto_power_off:gpiod_set_value_cansleep(gpio_to_desc(pw->xreset_gpio), 0)
[   90.112885] toto 7-0010: toto_power_on:gpiod_set_value_cansleep(gpio_to_desc(pw->reset_gpio), 0)
[   90.126117] toto 7-0010: toto_start_streaming:gpiod_set_value_cansleep(gpio_to_desc(pw->xreset_gpio), 1)
[   90.126239] toto 7-0010: toto_start_streaming:gpiod_set_value_cansleep(gpio_to_desc(pw->reset_gpio), 1)
[   95.198421] toto 7-0010: toto_stop_streaming:gpiod_set_value_cansleep(gpio_to_desc(pw->xreset_gpio), 0)
[   95.198766] toto 7-0010: toto_stop_streaming:gpiod_set_value_cansleep(gpio_to_desc(pw->reset_gpio), 0)
[   95.203560] toto 7-0010: toto_power_off:gpiod_set_value_cansleep(gpio_to_desc(pw->xreset_gpio), 0)
[  111.762198] toto 7-0010: toto_power_on:gpiod_set_value_cansleep(gpio_to_desc(pw->reset_gpio), 0)

but this time they are :

nvidia@ORNN:~$  sudo cat /sys/kernel/debug/gpio
gpiochip2: GPIOs 308-315, parent: i2c/7-0027, mcp23008, can sleep:
 gpio-308 (                    |mcp23008_27_output_h) in  hi 
 gpio-309 (                    |ext_reset_gpio      ) in  hi 
 gpio-310 (                    |cam_reset_gpio      ) in  hi 

compared to gpio_set_value_cansleep() :

nvidia@ORNN:~$ sudo cat /sys/kernel/debug/gpio
gpiochip2: GPIOs 308-315, parent: i2c/7-0027, mcp23008, can sleep:
 gpio-308 (                    |mcp23008_27_output_h) in  hi 
 gpio-309 (                    |ext_reset_gpio      ) in  lo 
 gpio-310 (                    |cam_reset_gpio      ) in  lo 

and still not controllable.

It seems the pinmux for these 3 pins are not correct for output usage so that it shows in here.
I’m not sure if you can request these pin through gpiod directly or you have to call any API from I2C I/O expander driver.

Could you compare the result with Jetpack 5 or request your vendor for them since we don’t have this I/O expander to verify locally?

Hi @KevinFFF,

Thanks for your suggestion. By the way do you have any example of GIO expander’s device-tree configuration that work with the Nvidia Devkits to share as reference, please ?

Best Regards,
K.

We currently don’t have an I²C I/O expander module like the MCP23008 available for local testing.

However, I came across the documentation for the MCP23008 in the upstream resources, which you can find at the following path:
<Linux_for_Tegra>/source/kernel/kernel-jammy-src/Documentation/devicetree/bindings/pinctrl/pinctrl-mcp23s08.txt