Skip to content
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
146 changes: 146 additions & 0 deletions Documentation/devicetree/bindings/clock/qcom,glymur-tcsr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/qcom,glymur-tcsr.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Qualcomm TCSR Clock Controller on Glymur

maintainers:
- Bjorn Andersson <andersson@kernel.org>
- Taniya Das <taniya.das@oss.qualcomm.com>

description: |
Qualcomm TCSR clock control module provides the clocks, resets and
power domains on Glymur

See also:
- include/dt-bindings/clock/qcom,glymur-tcsr.h

properties:
compatible:
items:
- enum:
- qcom,glymur-tcsr
- qcom,mahua-tcsr
- const: syscon

clocks:
items:
- description: TCXO pad clock

reg:
maxItems: 1

'#clock-cells':
const: 1

'#reset-cells':
const: 1

vdda-qrefrpt0-0p9-supply: true
vdda-qrefrpt1-0p9-supply: true
vdda-qrefrpt2-0p9-supply: true
vdda-qrefrpt3-0p9-supply: true
vdda-qrefrpt4-0p9-supply: true
vdda-qrefrpt5-0p9-supply: true
vdda-qrefrx0-0p9-supply: true
vdda-qrefrx1-0p9-supply: true
vdda-qrefrx2-0p9-supply: true
vdda-qrefrx3-0p9-supply: true
vdda-qrefrx4-0p9-supply: true
vdda-qrefrx5-0p9-supply: true
vdda-qreftx0-0p9-supply: true
vdda-qreftx0-1p2-supply: true
vdda-qreftx1-0p9-supply: true
vdda-refgen3-0p9-supply: true
vdda-refgen3-1p2-supply: true
vdda-refgen4-0p9-supply: true
vdda-refgen4-1p2-supply: true

allOf:
- if:
properties:
compatible:
contains:
const: qcom,glymur-tcsr
then:
required:
- vdda-qrefrpt0-0p9-supply
- vdda-qrefrpt1-0p9-supply
- vdda-qrefrpt2-0p9-supply
- vdda-qrefrpt3-0p9-supply
- vdda-qrefrpt4-0p9-supply
- vdda-qrefrx0-0p9-supply
- vdda-qrefrx1-0p9-supply
- vdda-qrefrx2-0p9-supply
- vdda-qrefrx4-0p9-supply
- vdda-qrefrx5-0p9-supply
- vdda-qreftx0-0p9-supply
- vdda-qreftx0-1p2-supply
- vdda-qreftx1-0p9-supply
- vdda-refgen3-0p9-supply
- vdda-refgen3-1p2-supply
- vdda-refgen4-0p9-supply
- vdda-refgen4-1p2-supply
- if:
properties:
compatible:
contains:
const: qcom,mahua-tcsr
then:
required:
- vdda-qrefrpt0-0p9-supply
- vdda-qrefrpt1-0p9-supply
- vdda-qrefrpt2-0p9-supply
- vdda-qrefrpt3-0p9-supply
- vdda-qrefrpt4-0p9-supply
- vdda-qrefrpt5-0p9-supply
- vdda-qrefrx1-0p9-supply
- vdda-qrefrx2-0p9-supply
- vdda-qrefrx3-0p9-supply
- vdda-qreftx1-0p9-supply
- vdda-refgen3-0p9-supply
- vdda-refgen3-1p2-supply

required:
- compatible
- clocks

additionalProperties: false

examples:
- |
#include <dt-bindings/clock/qcom,rpmh.h>

soc {
#address-cells = <2>;
#size-cells = <2>;

clock-controller@1fd5000 {
compatible = "qcom,glymur-tcsr", "syscon";
reg = <0x0 0x1fd5000 0x0 0x21000>;
clocks = <&rpmhcc RPMH_CXO_CLK>;
#clock-cells = <1>;
#reset-cells = <1>;
vdda-qrefrpt0-0p9-supply = <&vreg_l1a>;
vdda-qrefrpt1-0p9-supply = <&vreg_l1a>;
vdda-qrefrpt2-0p9-supply = <&vreg_l1a>;
vdda-qrefrpt3-0p9-supply = <&vreg_l1a>;
vdda-qrefrpt4-0p9-supply = <&vreg_l1a>;
vdda-qrefrx0-0p9-supply = <&vreg_l1a>;
vdda-qrefrx1-0p9-supply = <&vreg_l1a>;
vdda-qrefrx2-0p9-supply = <&vreg_l1a>;
vdda-qrefrx4-0p9-supply = <&vreg_l1a>;
vdda-qrefrx5-0p9-supply = <&vreg_l1a>;
vdda-qreftx0-0p9-supply = <&vreg_l1a>;
vdda-qreftx0-1p2-supply = <&vreg_l2a>;
vdda-qreftx1-0p9-supply = <&vreg_l1a>;
vdda-refgen3-0p9-supply = <&vreg_l1a>;
vdda-refgen3-1p2-supply = <&vreg_l2a>;
vdda-refgen4-0p9-supply = <&vreg_l1a>;
vdda-refgen4-1p2-supply = <&vreg_l2a>;
};
};

...
2 changes: 0 additions & 2 deletions Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ description: |

See also:
- include/dt-bindings/clock/qcom,eliza-tcsr.h
- include/dt-bindings/clock/qcom,glymur-tcsr.h
- include/dt-bindings/clock/qcom,hawi-tcsrcc.h
- include/dt-bindings/clock/qcom,nord-tcsrcc.h
- include/dt-bindings/clock/qcom,sm8550-tcsr.h
Expand All @@ -28,7 +27,6 @@ properties:
items:
- enum:
- qcom,eliza-tcsr
- qcom,glymur-tcsr
- qcom,hawi-tcsrcc
- qcom,kaanapali-tcsr
- qcom,milos-tcsr
Expand Down
1 change: 1 addition & 0 deletions drivers/clk/qcom/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ clk-qcom-y += clk-pll.o
clk-qcom-y += clk-rcg.o
clk-qcom-y += clk-rcg2.o
clk-qcom-y += clk-branch.o
clk-qcom-y += clk-ref.o
clk-qcom-y += clk-regmap-divider.o
clk-qcom-y += clk-regmap-mux.o
clk-qcom-y += clk-regmap-mux-div.o
Expand Down
209 changes: 209 additions & 0 deletions drivers/clk/qcom/clk-ref.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries.
*/

#include <linux/clk-provider.h>
#include <linux/clk/qcom.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>

#define QCOM_CLK_REF_EN_MASK BIT(0)

struct qcom_clk_ref_provider {
struct qcom_clk_ref *refs;
size_t num_refs;
};

static inline struct qcom_clk_ref *to_qcom_clk_ref(struct clk_hw *hw)
{
return container_of(hw, struct qcom_clk_ref, hw);
}

static const struct clk_parent_data qcom_clk_ref_parent_data = {
.index = 0,
};

static int qcom_clk_ref_prepare(struct clk_hw *hw)
{
struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
int ret;

if (!rclk->desc.num_regulators)
return 0;

ret = regulator_bulk_enable(rclk->desc.num_regulators, rclk->regulators);
if (ret)
pr_err("Failed to enable regulators for %s: %d\n",
clk_hw_get_name(hw), ret);

return ret;
}

static void qcom_clk_ref_unprepare(struct clk_hw *hw)
{
struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);

if (rclk->desc.num_regulators)
regulator_bulk_disable(rclk->desc.num_regulators, rclk->regulators);
}

static int qcom_clk_ref_enable(struct clk_hw *hw)
{
struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
int ret;

ret = regmap_set_bits(rclk->regmap, rclk->desc.offset, QCOM_CLK_REF_EN_MASK);
if (ret)
return ret;

udelay(10);

return 0;
}

static void qcom_clk_ref_disable(struct clk_hw *hw)
{
struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);

regmap_clear_bits(rclk->regmap, rclk->desc.offset, QCOM_CLK_REF_EN_MASK);
udelay(10);
}

static int qcom_clk_ref_is_enabled(struct clk_hw *hw)
{
struct qcom_clk_ref *rclk = to_qcom_clk_ref(hw);
u32 val;
int ret;

ret = regmap_read(rclk->regmap, rclk->desc.offset, &val);
if (ret)
return 0;

return !!(val & QCOM_CLK_REF_EN_MASK);
}

static const struct clk_ops qcom_clk_ref_ops = {
.prepare = qcom_clk_ref_prepare,
.unprepare = qcom_clk_ref_unprepare,
.enable = qcom_clk_ref_enable,
.disable = qcom_clk_ref_disable,
.is_enabled = qcom_clk_ref_is_enabled,
};

static int qcom_clk_ref_register(struct device *dev, struct regmap *regmap,
struct qcom_clk_ref *clk_refs,
const struct qcom_clk_ref_desc * const *descs,
size_t num_clk_refs)
{
const struct qcom_clk_ref_desc *desc;
struct clk_init_data init_data = {};
struct qcom_clk_ref *clk_ref;
size_t clk_idx;
unsigned int i;
int ret;

for (clk_idx = 0; clk_idx < num_clk_refs; clk_idx++) {
clk_ref = &clk_refs[clk_idx];
desc = descs[clk_idx];

/* Skip unpopulated indices; the array is indexed by clock ID. */
if (!desc)
continue;

if (WARN_ON(!desc->name))
continue;

clk_ref->regmap = regmap;
clk_ref->desc = *desc;

if (clk_ref->desc.num_regulators) {
clk_ref->regulators = devm_kcalloc(dev, clk_ref->desc.num_regulators,
sizeof(*clk_ref->regulators),
GFP_KERNEL);
if (!clk_ref->regulators)
return -ENOMEM;

for (i = 0; i < clk_ref->desc.num_regulators; i++)
clk_ref->regulators[i].supply =
clk_ref->desc.regulator_names[i];

ret = devm_regulator_bulk_get(dev, clk_ref->desc.num_regulators,
clk_ref->regulators);
if (ret)
return dev_err_probe(dev, ret,
"Failed to get regulators for %s\n",
clk_ref->desc.name);
}

init_data.name = clk_ref->desc.name;
init_data.parent_data = &qcom_clk_ref_parent_data;
init_data.num_parents = 1;
init_data.ops = &qcom_clk_ref_ops;
clk_ref->hw.init = &init_data;

ret = devm_clk_hw_register(dev, &clk_ref->hw);
if (ret)
return ret;
}

return 0;
}

static struct clk_hw *qcom_clk_ref_provider_get(struct of_phandle_args *clkspec, void *data)
{
struct qcom_clk_ref_provider *provider = data;
unsigned int idx = clkspec->args[0];

if (idx >= provider->num_refs)
return ERR_PTR(-EINVAL);

if (!provider->refs[idx].regmap)
return ERR_PTR(-ENOENT);

return &provider->refs[idx].hw;
}

int qcom_clk_ref_probe(struct platform_device *pdev,
const struct regmap_config *config,
const struct qcom_clk_ref_desc * const *descs,
size_t num_clk_refs)
{
struct qcom_clk_ref_provider *provider;
struct device *dev = &pdev->dev;
struct regmap *regmap;
void __iomem *base;
int ret;

base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);

regmap = devm_regmap_init_mmio(dev, base, config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);

provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL);
if (!provider)
return -ENOMEM;

provider->refs = devm_kcalloc(dev, num_clk_refs, sizeof(*provider->refs),
GFP_KERNEL);
if (!provider->refs)
return -ENOMEM;

provider->num_refs = num_clk_refs;

ret = qcom_clk_ref_register(dev, regmap, provider->refs, descs,
provider->num_refs);
if (ret)
return ret;

return devm_of_clk_add_hw_provider(dev, qcom_clk_ref_provider_get, provider);
}
EXPORT_SYMBOL_GPL(qcom_clk_ref_probe);
Loading