Skip to content

GPIO Acquire/Release Semantics for 'AI Camera' #6884

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: rpi-6.12.y
Choose a base branch
from
Open
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
21 changes: 20 additions & 1 deletion Documentation/devicetree/bindings/clock/gpio-gate-clock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,15 @@ maintainers:

properties:
compatible:
const: gpio-gate-clock
enum:
- gpio-gate-clock
- gpio-gate-clock-releasing
description: |
Use "gpio-gate-clock" for normal operation where the GPIO is held for the
lifetime of the clock. Use "gpio-gate-clock-releasing" for power-sensitive
applications where the GPIO should be acquired only when the clock is
enabled and released when disabled, allowing shared regulators to be
powered down.

clocks:
maxItems: 1
Expand Down Expand Up @@ -40,3 +48,14 @@ examples:
#clock-cells = <0>;
enable-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
};

- |
#include <dt-bindings/gpio/gpio.h>

/* Power-sensitive clock that releases GPIO when disabled */
clock {
compatible = "gpio-gate-clock-releasing";
clocks = <&parentclk>;
#clock-cells = <0>;
enable-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
};
2 changes: 1 addition & 1 deletion arch/arm/boot/dts/overlays/imx500-overlay.dts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
};

clk_aicam_gated: clk-aicam-gated1 {
compatible = "gpio-gate-clock";
compatible = "gpio-gate-clock-releasing";
clocks = <&clk_aicam>;
#clock-cells = <0>;
enable-gpios = <&spi_bridge 21 GPIO_ACTIVE_HIGH>;
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/boot/dts/overlays/imx500-pi5-overlay.dts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
};

clk_aicam_gated: clk-aicam-gated1 {
compatible = "gpio-gate-clock";
compatible = "gpio-gate-clock-releasing";
clocks = <&clk_aicam>;
#clock-cells = <0>;
enable-gpios = <&spi_bridge 21 GPIO_ACTIVE_HIGH>;
Expand Down
129 changes: 123 additions & 6 deletions drivers/clk/clk-gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
*
* @hw: handle between common and hardware-specific interfaces
* @gpiod: gpio descriptor
* @dev: device pointer for acquire/release operations
*
* Clock with a gpio control for enabling and disabling the parent clock
* or switching between two parents by asserting or deasserting the gpio.
Expand All @@ -44,9 +45,37 @@
struct clk_gpio {
struct clk_hw hw;
struct gpio_desc *gpiod;
struct device *dev;
};

#define to_clk_gpio(_hw) container_of(_hw, struct clk_gpio, hw)
static int clk_gpio_gate_acquire(struct clk_hw *hw)
{
struct clk_gpio *clk = to_clk_gpio(hw);
struct device *dev = clk->dev;

clk->gpiod = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(clk->gpiod))
return PTR_ERR(clk->gpiod);

return 0;
}

static bool clk_gpio_gate_is_acquired(struct clk_hw *hw)
{
struct clk_gpio *clk = to_clk_gpio(hw);

return clk->gpiod;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you are declaring non-zero to indicate success (although I'd have preferred !!clk->gpiod to make the type conversion more obvious), but clk_gpio_get_acquire leaves it containing an error code on failure.

}

static void clk_gpio_gate_release(struct clk_hw *hw)
{
struct clk_gpio *clk = to_clk_gpio(hw);
struct device *dev = clk->dev;

devm_gpiod_put(dev, clk->gpiod);
clk->gpiod = NULL;
}

static int clk_gpio_gate_enable(struct clk_hw *hw)
{
Expand Down Expand Up @@ -77,6 +106,39 @@ static const struct clk_ops clk_gpio_gate_ops = {
.is_enabled = clk_gpio_gate_is_enabled,
};

static int clk_gpio_gate_releasing_enable(struct clk_hw *hw)
{
int ret;

if (!clk_gpio_gate_is_acquired(hw)) {
ret = clk_gpio_gate_acquire(hw);
if (ret)
return ret;
}

return clk_gpio_gate_enable(hw);
}

static void clk_gpio_gate_releasing_disable(struct clk_hw *hw)
{
clk_gpio_gate_disable(hw);
clk_gpio_gate_release(hw);
}

static int clk_gpio_gate_releasing_is_enabled(struct clk_hw *hw)
{
if (!clk_gpio_gate_is_acquired(hw))
return 0;

return clk_gpio_gate_is_enabled(hw);
}

static const struct clk_ops clk_gpio_gate_releasing_ops = {
.enable = clk_gpio_gate_releasing_enable,
.disable = clk_gpio_gate_releasing_disable,
.is_enabled = clk_gpio_gate_releasing_is_enabled,
};

static int clk_sleeping_gpio_gate_prepare(struct clk_hw *hw)
{
struct clk_gpio *clk = to_clk_gpio(hw);
Expand Down Expand Up @@ -106,6 +168,39 @@ static const struct clk_ops clk_sleeping_gpio_gate_ops = {
.is_prepared = clk_sleeping_gpio_gate_is_prepared,
};

static int clk_sleeping_gpio_gate_releasing_prepare(struct clk_hw *hw)
{
int ret;

if (!clk_gpio_gate_is_acquired(hw)) {
ret = clk_gpio_gate_acquire(hw);
if (ret)
return ret;
}

return clk_sleeping_gpio_gate_prepare(hw);
}

static void clk_sleeping_gpio_gate_releasing_unprepare(struct clk_hw *hw)
{
clk_sleeping_gpio_gate_unprepare(hw);
clk_gpio_gate_release(hw);
}

static int clk_sleeping_gpio_gate_releasing_is_prepared(struct clk_hw *hw)
{
if (!clk_gpio_gate_is_acquired(hw))
return 0;

return clk_sleeping_gpio_gate_is_prepared(hw);
}

static const struct clk_ops clk_sleeping_gpio_gate_releasing_ops = {
.prepare = clk_sleeping_gpio_gate_releasing_prepare,
.unprepare = clk_sleeping_gpio_gate_releasing_unprepare,
.is_prepared = clk_sleeping_gpio_gate_releasing_is_prepared,
};

/**
* DOC: basic clock multiplexer which can be controlled with a gpio output
* Traits of this clock:
Expand Down Expand Up @@ -160,6 +255,7 @@ static struct clk_hw *clk_register_gpio(struct device *dev, u8 num_parents,
init.flags = CLK_SET_RATE_PARENT;

clk_gpio->gpiod = gpiod;
clk_gpio->dev = dev;
clk_gpio->hw.init = &init;

hw = &clk_gpio->hw;
Expand All @@ -172,14 +268,29 @@ static struct clk_hw *clk_register_gpio(struct device *dev, u8 num_parents,

static struct clk_hw *clk_hw_register_gpio_gate(struct device *dev,
int num_parents,
struct gpio_desc *gpiod)
struct gpio_desc *gpiod,
bool releasing)
{
const struct clk_ops *ops;

if (gpiod_cansleep(gpiod))
ops = &clk_sleeping_gpio_gate_ops;
else
ops = &clk_gpio_gate_ops;
if (releasing) {
/* For releasing variant, confirm GPIO works then release it
* for acquire/release semantics
*/
if (gpiod_cansleep(gpiod))
ops = &clk_sleeping_gpio_gate_releasing_ops;
else
ops = &clk_gpio_gate_releasing_ops;

devm_gpiod_put(dev, gpiod);
gpiod = NULL;
} else {
/* Regular variant - keep GPIO and choose appropriate ops */
if (gpiod_cansleep(gpiod))
ops = &clk_sleeping_gpio_gate_ops;
else
ops = &clk_gpio_gate_ops;
}

return clk_register_gpio(dev, num_parents, gpiod, ops);
}
Expand All @@ -199,9 +310,12 @@ static int gpio_clk_driver_probe(struct platform_device *pdev)
struct gpio_desc *gpiod;
struct clk_hw *hw;
bool is_mux;
bool is_releasing;
int ret;

is_mux = of_device_is_compatible(node, "gpio-mux-clock");
is_releasing =
of_device_is_compatible(node, "gpio-gate-clock-releasing");

num_parents = of_clk_get_parent_count(node);
if (is_mux && num_parents != 2) {
Expand All @@ -226,7 +340,9 @@ static int gpio_clk_driver_probe(struct platform_device *pdev)
if (is_mux)
hw = clk_hw_register_gpio_mux(dev, gpiod);
else
hw = clk_hw_register_gpio_gate(dev, num_parents, gpiod);
hw = clk_hw_register_gpio_gate(dev, num_parents, gpiod,
is_releasing);

if (IS_ERR(hw))
return PTR_ERR(hw);

Expand All @@ -236,6 +352,7 @@ static int gpio_clk_driver_probe(struct platform_device *pdev)
static const struct of_device_id gpio_clk_match_table[] = {
{ .compatible = "gpio-mux-clock" },
{ .compatible = "gpio-gate-clock" },
{ .compatible = "gpio-gate-clock-releasing" },
{ }
};

Expand Down
Loading