Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 05226112 authored by Shefali Jain's avatar Shefali Jain Committed by Veera Vegivada
Browse files

clk: qcom: cmn_blk: Add support for Common block PLL



On target like qcs404, Common block PLL is required
for USB and PCIE subsystems. Add support for common
block pll so that client driver can vote and unvote
it as per requirement.

Change-Id: Idf6db3f4585fe73f4f9087ec0db7551c0fb6f862
Signed-off-by: default avatarShefali Jain <shefjain@codeaurora.org>
Signed-off-by: default avatarVeera Vegivada <vvegivad@codeaurora.org>
parent 1e019c66
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -72,6 +72,15 @@ config QCOM_CLK_RPMH
	 Say Y if you want to support the clocks exposed by RPMh on
	 platforms such as SDM845.

config QCS_CMN_BLK_PLL
	tristate "QCS404 CMN BLK PLL"
	select QCS_GCC_404
	help
	 Support for the CMN BLK PLL on Qualcomm Technologies, Inc QCS404
	 devices.
	 Common block PLL is required for USB and PCIE subsystems.
	 Say Y if you want to support the Common Block PLL.

config APQ_GCC_8084
	tristate "APQ8084 Global Clock Controller"
	select QCOM_GDSC
+1 −0
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
obj-$(CONFIG_QCOM_CLK_RPMH) += clk-rpmh.o
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
obj-$(CONFIG_QCS_CMN_BLK_PLL) += cmn-blk-pll.o
obj-$(CONFIG_QCS_GCC_404) += gcc-qcs404.o
obj-$(CONFIG_QCS_TURING_404) += turingcc-qcs404.o
obj-$(CONFIG_SA_DEBUGCC_DIREWOLF) += debugcc-direwolf.o
+236 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2019, 2021, The Linux Foundation. All rights reserved.
 */

#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/reset-controller.h>

#include <dt-bindings/clock/qcom,cmn-blk-pll.h>

#include "common.h"

#define SW_RESET_LOGIC_MASK	BIT(6)

struct cmn_blk_pll {
	struct regmap *regmap;
	struct clk *misc_reset;
	struct clk *ahb_clk;
	struct clk *aon_clk;
	struct reset_control *reset;
};
static struct cmn_blk_pll pll;

static unsigned long clk_cmn_blk_recalc_rate(struct clk_hw *hw,
					unsigned long parent_rate)
{
	return to_clk_fixed_rate(hw)->fixed_rate;
}

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

	ret = clk_prepare(pll.misc_reset);
	if (ret)
		return ret;

	ret = clk_prepare(pll.aon_clk);
	if (ret)
		return ret;

	return clk_prepare(pll.ahb_clk);
}

static int clk_cmn_blk_pll_enable(struct clk_hw *hw)
{
	u32 val, ret;

	ret = reset_control_reset(pll.reset);
	if (ret)
		return ret;

	ret = clk_enable(pll.misc_reset);
	if (ret)
		return ret;

	ret = clk_enable(pll.aon_clk);
	if (ret)
		return ret;

	ret = clk_enable(pll.ahb_clk);
	if (ret)
		return ret;

	ret = regmap_read(pll.regmap, 0, &val);
	if (ret)
		return ret;

	if (val & SW_RESET_LOGIC_MASK) {
		val &= ~BIT(6);
		regmap_write(pll.regmap, 0, val);
	}

	val |= BIT(6);
	regmap_write(pll.regmap, 0, val);

	return 0;
}

static void clk_cmn_blk_pll_unprepare(struct clk_hw *hw)
{
	clk_unprepare(pll.misc_reset);
	clk_unprepare(pll.aon_clk);
	clk_unprepare(pll.ahb_clk);
}

static void clk_cmn_blk_pll_disable(struct clk_hw *hw)
{
	u32 val;

	regmap_read(pll.regmap, 0, &val);
	val &= ~BIT(6);
	regmap_write(pll.regmap, 0, val);

	clk_disable(pll.misc_reset);
	clk_disable(pll.aon_clk);
	clk_disable(pll.ahb_clk);
}

const struct clk_ops clk_cmn_blk_ops = {
	.prepare = clk_cmn_blk_pll_prepare,
	.enable = clk_cmn_blk_pll_enable,
	.disable = clk_cmn_blk_pll_disable,
	.unprepare = clk_cmn_blk_pll_unprepare,
	.recalc_rate = clk_cmn_blk_recalc_rate,
};

static struct clk_fixed_rate cmn_blk_pll = {
	.fixed_rate = 100000000,
	.hw.init = &(struct clk_init_data){
		.name = "cmn_blk_pll",
		.parent_names = (const char *[]){ "bi_tcxo" },
		.num_parents = 1,
		.ops = &clk_cmn_blk_ops,
	},
};

struct clk_hw *cmn_blk_pll_hws[] = {
	[CMN_BLK_PLL] = &cmn_blk_pll.hw,
};

static const struct regmap_config cmn_blk_pll_regmap_config = {
	.reg_bits	= 32,
	.reg_stride	= 4,
	.val_bits	= 32,
	.max_register	= 0x8,
	.fast_io	= true,
};

static const struct of_device_id cmn_blk_pll_match_table[] = {
	{ .compatible = "qcom,cmn_blk_pll" },
	{ }
};
MODULE_DEVICE_TABLE(of, cmn_blk_pll_match_table);

static int cmn_blk_pll_probe(struct platform_device *pdev)
{
	struct resource *res;
	struct clk_hw_onecell_data *clk_data;
	struct device *dev = &pdev->dev;
	void __iomem *base;
	int i, ret;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	base = devm_ioremap_resource(dev, res);
	if (IS_ERR(base))
		return PTR_ERR(base);

	pll.regmap = devm_regmap_init_mmio(&pdev->dev, base,
						&cmn_blk_pll_regmap_config);
	if (IS_ERR(pll.regmap))
		return PTR_ERR(pll.regmap);

	pll.misc_reset = devm_clk_get(&pdev->dev, "misc_reset_clk");
	if (IS_ERR(pll.misc_reset)) {
		if (PTR_ERR(pll.misc_reset) != -EPROBE_DEFER)
			dev_err(&pdev->dev, "Unable to get misc_reset clock\n");
		return PTR_ERR(pll.misc_reset);
	}

	pll.ahb_clk = devm_clk_get(&pdev->dev, "ahb_clk");
	if (IS_ERR(pll.ahb_clk)) {
		if (PTR_ERR(pll.ahb_clk) != -EPROBE_DEFER)
			dev_err(&pdev->dev, "Unable to get ahb_clk clock\n");
		return PTR_ERR(pll.ahb_clk);
	}

	pll.aon_clk = devm_clk_get(&pdev->dev, "aon_clk");
	if (IS_ERR(pll.aon_clk)) {
		if (PTR_ERR(pll.aon_clk) != -EPROBE_DEFER)
			dev_err(&pdev->dev, "Unable to get aon_clk clock\n");
		return PTR_ERR(pll.aon_clk);
	}

	pll.reset = devm_reset_control_get(&pdev->dev, "cmn_blk_pll_reset");
	if (IS_ERR(pll.reset)) {
		if (PTR_ERR(pll.reset) != -EPROBE_DEFER)
			dev_err(&pdev->dev, "Unable to get cmn_blk\n");
		return PTR_ERR(pll.reset);
	}

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

	clk_data->num = ARRAY_SIZE(cmn_blk_pll_hws);

	for (i = 0; i < clk_data->num; i++) {
		ret = devm_clk_hw_register(dev, cmn_blk_pll_hws[i]);
		if (ret) {
			dev_err(&pdev->dev, "Failed to register cmn_blk_pll\n");
			return ret;
		}
		clk_data->hws[i] = cmn_blk_pll_hws[i];
	}

	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
	if (ret) {
		dev_err(dev, "Unable to register cmn_blk_pll provider ret = %d\n"
									, ret);
		return ret;
	}

	dev_info(&pdev->dev, "Registered CMN BLK PLL\n");
	return 0;
}

static struct platform_driver cmn_blk_pll_driver = {
	.probe  = cmn_blk_pll_probe,
	.driver = {
		.name = "cmn_blk_pll",
		.of_match_table = cmn_blk_pll_match_table,
	},
};

static int __init cmn_blk_pll_init(void)
{
	return platform_driver_register(&cmn_blk_pll_driver);
}
subsys_initcall(cmn_blk_pll_init);

static void __exit cmn_blk_pll_exit(void)
{
	platform_driver_unregister(&cmn_blk_pll_driver);
}
module_exit(cmn_blk_pll_exit);