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

Commit 4589aff0 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

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

parents 54cddbe5 f02ed8a1
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
Qualcomm Technologies, Inc. Common Block PLL Controller Binding
---------------------------------------------------------------

Required properties :
- compatible : shall contain only the following:
			"qcom,cmn_blk_pll"

- reg : shall contain base register location and size.
- reg-names : "cmn_blk".
- clock-names : Shall contain "misc_reset", "ahb_clk", "aon_clk".
- clocks : phandle + clock reference to misc_reset, ahb and aon clock.
- #clock-cells : shall contain 1.

Example :
	clock_cmn_blk_pll@2f780 {
		compatible = "qcom,cmn_blk_pll";
		reg = <0x2f780 0x4>;
		reg-names = "cmn_blk";
		clocks = <&clock_gcc GCC_BIAS_PLL_MISC_RESET_CLK>,
			<&clock_gcc GCC_BIAS_PLL_AHB_CLK>,
			<&clock_gcc GCC_BIAS_PLL_AON_CLK>;
		clock-names = "misc_reset_clk", "ahb_clk", "aon_clk";
		resets = <&clock_gcc GCC_BIAS_PLL_BCR>;
		reset-names = "cmn_blk_pll_reset";
		#clock-cells = <1>;
	};
+8 −0
Original line number Diff line number Diff line
@@ -546,3 +546,11 @@ config SM_DEBUGCC_TRINKET
	  Support for the debug clock controller on Qualcomm Technologies, Inc
	  TRINKET devices.
	  Say Y if you want to support the clock measurement functionality.

config QCS_CMN_BLK_PLL
	tristate "QCS405 CMN BLK PLL"
	select MDM_GCC_QCS405
	help
	 Support for the CMN BLK PLL on Qualcomm Technologies, Inc QCS405
	 devices.
	 Say Y if you want to support the Common Block PLL.
+1 −0
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ obj-$(CONFIG_MSM_VIDEOCC_SM6150) += videocc-sm6150.o
obj-$(CONFIG_MSM_VIDEOCC_SM8150) += videocc-sm8150.o
obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o
obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o
obj-$(CONFIG_QCS_CMN_BLK_PLL) += cmn_blk_pll.o
obj-$(CONFIG_SM_DEBUGCC_TRINKET) += debugcc-trinket.o
obj-$(CONFIG_SM_DISPCC_TRINKET) += dispcc-trinket.o
obj-$(CONFIG_SM_GCC_TRINKET) += gcc-trinket.o
+226 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#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_enable(struct clk_hw *hw)
{
	u32 val, ret;

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

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

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

	ret = clk_prepare_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_disable(struct clk_hw *hw)
{
	u32 val;

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

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

const struct clk_ops clk_cmn_blk_ops = {
	.enable = clk_cmn_blk_pll_enable,
	.disable = clk_cmn_blk_pll_disable,
	.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 *[]){ "cxo" },
		.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);
	}

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cmn_blk");
	if (!res) {
		dev_err(&pdev->dev, "Register base not defined\n");
		return -ENOMEM;
	}

	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);
+45 −1
Original line number Diff line number Diff line
/*
 * Copyright (c) 2018, The Linux Foundation. All rights reserved.
 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -1401,6 +1401,46 @@ static struct clk_rcg2 vsync_clk_src = {
	},
};

static struct clk_branch gcc_bias_pll_misc_reset_clk  = {
	.halt_reg = 0x3c004,
	.halt_check = BRANCH_HALT_SKIP,
	.clkr = {
		.enable_reg = 0x3c004,
		.enable_is_inverted = true,
		.enable_mask = BIT(0),
		.hw.init = &(struct clk_init_data){
			.name = "gcc_bias_pll_misc_reset_clk",
			.ops = &clk_branch2_ops,
		},
	},
};

static struct clk_branch gcc_bias_pll_ahb_clk = {
	.halt_reg = 0x3c008,
	.halt_check = BRANCH_HALT,
	.clkr = {
		.enable_reg = 0x3c008,
		.enable_mask = BIT(0),
		.hw.init = &(struct clk_init_data){
			.name = "gcc_bias_pll_ahb_clk",
			.ops = &clk_branch2_ops,
		},
	},
};

static struct clk_branch gcc_bias_pll_aon_clk = {
	.halt_reg = 0x3c00c,
	.halt_check = BRANCH_HALT_DELAY,
	.clkr = {
		.enable_reg = 0x3c00c,
		.enable_mask = BIT(0),
		.hw.init = &(struct clk_init_data){
			.name = "gcc_bias_pll_aon_clk",
			.ops = &clk_branch2_ops,
		},
	},
};

static struct clk_branch gcc_apss_ahb_clk = {
	.halt_reg = 0x4601c,
	.halt_check = BRANCH_HALT_VOTED,
@@ -2847,6 +2887,9 @@ static struct clk_regmap *gcc_qcs405_clocks[] = {
	[GCC_CRYPTO_CLK] = &gcc_crypto_clk.clkr,
	[GCC_MDP_TBU_CLK] = &gcc_mdp_tbu_clk.clkr,
	[GCC_QDSS_DAP_CLK] = &gcc_qdss_dap_clk.clkr,
	[GCC_BIAS_PLL_MISC_RESET_CLK] = &gcc_bias_pll_misc_reset_clk.clkr,
	[GCC_BIAS_PLL_AHB_CLK] = &gcc_bias_pll_ahb_clk.clkr,
	[GCC_BIAS_PLL_AON_CLK] = &gcc_bias_pll_aon_clk.clkr,
};

static const struct qcom_reset_map gcc_qcs405_resets[] = {
@@ -2864,6 +2907,7 @@ static const struct qcom_reset_map gcc_qcs405_resets[] = {
	[GCC_PCIE_0_LINK_DOWN_BCR] = {0x3E038},
	[GCC_PCIEPHY_0_PHY_BCR] = {0x3E03C},
	[GCC_EMAC_BCR] = {0x4E000},
	[GCC_BIAS_PLL_BCR] = {0x3C000},
};

static const struct regmap_config gcc_qcs405_regmap_config = {
Loading