--- /dev/null
+From a4a7cbe36623ffaabbae413c0eacf40d033db71f Mon Sep 17 00:00:00 2001
+Date: Fri, 6 Sep 2024 10:25:07 +0200
+Subject: dt-bindings: clocks: add binding for gated-fixed-clocks
+
+In contrast to fixed clocks that are described as ungateable, boards
+sometimes use additional oscillators for things like PCIe reference
+clocks, that need actual supplies to get enabled and enable-gpios to be
+toggled for them to work.
+
+This adds a binding for such oscillators that are not configurable
+themself, but need to handle supplies for them to work.
+
+In schematics they often can be seen as
+
+ ----------------
+Enable - | 100MHz,3.3V, | - VDD
+ | 3225 |
+ GND - | | - OUT
+ ----------------
+
+or similar. The enable pin might be separate but can also just be tied
+to the vdd supply, hence it is optional in the binding.
+
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/gated-fixed-clock.yaml
+@@ -0,0 +1,49 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/clock/gated-fixed-clock.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Gated Fixed clock
++
++maintainers:
++
++properties:
++ compatible:
++ const: gated-fixed-clock
++
++ "#clock-cells":
++ const: 0
++
++ clock-frequency: true
++
++ clock-output-names:
++ maxItems: 1
++
++ enable-gpios:
++ description:
++ Contains a single GPIO specifier for the GPIO that enables and disables
++ the oscillator.
++ maxItems: 1
++
++ vdd-supply:
++ description: handle of the regulator that provides the supply voltage
++
++required:
++ - compatible
++ - "#clock-cells"
++ - clock-frequency
++ - vdd-supply
++
++additionalProperties: false
++
++examples:
++ - |
++ clock-1000000000 {
++ compatible = "gated-fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <1000000000>;
++ vdd-supply = <®_vdd>;
++ };
++...
--- /dev/null
+From 4940071d962827467f7297be3b874c96186ca0b7 Mon Sep 17 00:00:00 2001
+Date: Fri, 6 Sep 2024 10:25:10 +0200
+Subject: clk: clk-gpio: add driver for gated-fixed-clocks
+
+In contrast to fixed clocks that are described as ungateable, boards
+sometimes use additional oscillators for things like PCIe reference
+clocks, that need actual supplies to get enabled and enable-gpios to be
+toggled for them to work.
+
+This adds a driver for those generic gated-fixed-clocks
+that can show up in schematics looking like
+
+ ----------------
+Enable - | 100MHz,3.3V, | - VDD
+ | 3225 |
+ GND - | | - OUT
+ ----------------
+
+The new driver gets grouped together with the existing gpio-gate and
+gpio-mux, as it for one re-uses a lot of the gpio-gate functions
+and also in its core it's just another gpio-controlled clock, just
+with a fixed rate and a regulator-supply added in.
+
+The regulator-API provides function stubs for the !CONFIG_REGULATOR case,
+so no special handling is necessary.
+
+
+--- a/drivers/clk/clk-gpio.c
++++ b/drivers/clk/clk-gpio.c
+@@ -17,6 +17,7 @@
+ #include <linux/device.h>
+ #include <linux/of.h>
+ #include <linux/platform_device.h>
++#include <linux/regulator/consumer.h>
+
+ /**
+ * DOC: basic gpio gated clock which can be enabled and disabled
+@@ -239,3 +240,187 @@ static struct platform_driver gpio_clk_d
+ },
+ };
+ builtin_platform_driver(gpio_clk_driver);
++
++/**
++ * DOC: gated fixed clock, controlled with a gpio output and a regulator
++ * Traits of this clock:
++ * prepare - clk_prepare and clk_unprepare are function & control regulator
++ * optionally a gpio that can sleep
++ * enable - clk_enable and clk_disable are functional & control gpio
++ * rate - rate is fixed and set on clock registration
++ * parent - fixed clock is a root clock and has no parent
++ */
++
++/**
++ * struct clk_gated_fixed - Gateable fixed rate clock
++ * @clk_gpio: instance of clk_gpio for gate-gpio
++ * @supply: supply regulator
++ * @rate: fixed rate
++ */
++struct clk_gated_fixed {
++ struct clk_gpio clk_gpio;
++ struct regulator *supply;
++ unsigned long rate;
++};
++
++#define to_clk_gated_fixed(_clk_gpio) container_of(_clk_gpio, struct clk_gated_fixed, clk_gpio)
++
++static unsigned long clk_gated_fixed_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ return to_clk_gated_fixed(to_clk_gpio(hw))->rate;
++}
++
++static int clk_gated_fixed_prepare(struct clk_hw *hw)
++{
++ struct clk_gated_fixed *clk = to_clk_gated_fixed(to_clk_gpio(hw));
++
++ if (!clk->supply)
++ return 0;
++
++ return regulator_enable(clk->supply);
++}
++
++static void clk_gated_fixed_unprepare(struct clk_hw *hw)
++{
++ struct clk_gated_fixed *clk = to_clk_gated_fixed(to_clk_gpio(hw));
++
++ if (!clk->supply)
++ return;
++
++ regulator_disable(clk->supply);
++}
++
++static int clk_gated_fixed_is_prepared(struct clk_hw *hw)
++{
++ struct clk_gated_fixed *clk = to_clk_gated_fixed(to_clk_gpio(hw));
++
++ if (!clk->supply)
++ return true;
++
++ return regulator_is_enabled(clk->supply);
++}
++
++/*
++ * Fixed gated clock with non-sleeping gpio.
++ *
++ * Prepare operation turns on the supply regulator
++ * and the enable operation switches the enable-gpio.
++ */
++static const struct clk_ops clk_gated_fixed_ops = {
++ .prepare = clk_gated_fixed_prepare,
++ .unprepare = clk_gated_fixed_unprepare,
++ .is_prepared = clk_gated_fixed_is_prepared,
++ .enable = clk_gpio_gate_enable,
++ .disable = clk_gpio_gate_disable,
++ .is_enabled = clk_gpio_gate_is_enabled,
++ .recalc_rate = clk_gated_fixed_recalc_rate,
++};
++
++static int clk_sleeping_gated_fixed_prepare(struct clk_hw *hw)
++{
++ int ret;
++
++ ret = clk_gated_fixed_prepare(hw);
++ if (ret)
++ return ret;
++
++ ret = clk_sleeping_gpio_gate_prepare(hw);
++ if (ret)
++ clk_gated_fixed_unprepare(hw);
++
++ return ret;
++}
++
++static void clk_sleeping_gated_fixed_unprepare(struct clk_hw *hw)
++{
++ clk_gated_fixed_unprepare(hw);
++ clk_sleeping_gpio_gate_unprepare(hw);
++}
++
++/*
++ * Fixed gated clock with non-sleeping gpio.
++ *
++ * Enabling the supply regulator and switching the enable-gpio happens
++ * both in the prepare step.
++ * is_prepared only needs to check the gpio state, as toggling the
++ * gpio is the last step when preparing.
++ */
++static const struct clk_ops clk_sleeping_gated_fixed_ops = {
++ .prepare = clk_sleeping_gated_fixed_prepare,
++ .unprepare = clk_sleeping_gated_fixed_unprepare,
++ .is_prepared = clk_sleeping_gpio_gate_is_prepared,
++ .recalc_rate = clk_gated_fixed_recalc_rate,
++};
++
++static int clk_gated_fixed_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct clk_gated_fixed *clk;
++ const struct clk_ops *ops;
++ const char *clk_name;
++ u32 rate;
++ int ret;
++
++ clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
++ if (!clk)
++ return -ENOMEM;
++
++ ret = device_property_read_u32(dev, "clock-frequency", &rate);
++ if (ret)
++ return dev_err_probe(dev, ret, "Failed to get clock-frequency\n");
++ clk->rate = rate;
++
++ ret = device_property_read_string(dev, "clock-output-names", &clk_name);
++ if (ret)
++ clk_name = fwnode_get_name(dev->fwnode);
++
++ clk->supply = devm_regulator_get_optional(dev, "vdd");
++ if (IS_ERR(clk->supply)) {
++ if (PTR_ERR(clk->supply) != -ENODEV)
++ return dev_err_probe(dev, PTR_ERR(clk->supply),
++ "Failed to get regulator\n");
++ clk->supply = NULL;
++ }
++
++ clk->clk_gpio.gpiod = devm_gpiod_get_optional(dev, "enable",
++ GPIOD_OUT_LOW);
++ if (IS_ERR(clk->clk_gpio.gpiod))
++ return dev_err_probe(dev, PTR_ERR(clk->clk_gpio.gpiod),
++ "Failed to get gpio\n");
++
++ if (gpiod_cansleep(clk->clk_gpio.gpiod))
++ ops = &clk_sleeping_gated_fixed_ops;
++ else
++ ops = &clk_gated_fixed_ops;
++
++ clk->clk_gpio.hw.init = CLK_HW_INIT_NO_PARENT(clk_name, ops, 0);
++
++ /* register the clock */
++ ret = devm_clk_hw_register(dev, &clk->clk_gpio.hw);
++ if (ret)
++ return dev_err_probe(dev, ret,
++ "Failed to register clock\n");
++
++ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
++ &clk->clk_gpio.hw);
++ if (ret)
++ return dev_err_probe(dev, ret,
++ "Failed to register clock provider\n");
++
++ return 0;
++}
++
++static const struct of_device_id gated_fixed_clk_match_table[] = {
++ { .compatible = "gated-fixed-clock" },
++ { /* sentinel */ }
++};
++
++static struct platform_driver gated_fixed_clk_driver = {
++ .probe = clk_gated_fixed_probe,
++ .driver = {
++ .name = "gated-fixed-clk",
++ .of_match_table = gated_fixed_clk_match_table,
++ },
++};
++builtin_platform_driver(gated_fixed_clk_driver);