clk: clk-gpio: Support acquire/release semantics

Add support for the 'gpio-gate-clock-releasing' compatible string. The
behaviour is identical to that of 'gpio-gate-clock' but the gpio is
acquired on 'enable' and released on 'disable'.

Signed-off-by: Richard Oliver <richard.oliver@raspberrypi.com>
This commit is contained in:
Richard Oliver
2025-06-05 09:53:54 +01:00
parent b5bed2815f
commit 186491696f

View File

@@ -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.
@@ -44,9 +45,41 @@
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)) {
int ret = PTR_ERR(clk->gpiod);
clk->gpiod = NULL;
return ret;
}
return 0;
}
static bool clk_gpio_gate_is_acquired(struct clk_hw *hw)
{
struct clk_gpio *clk = to_clk_gpio(hw);
return !!clk->gpiod;
}
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)
{
@@ -77,6 +110,37 @@ 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;
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);
@@ -106,6 +170,37 @@ 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;
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:
@@ -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;
@@ -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 (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);
}
@@ -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) {
@@ -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);
@@ -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" },
{ }
};