Received: by 2002:ac0:a594:0:0:0:0:0 with SMTP id m20-v6csp1817090imm; Thu, 24 May 2018 01:06:26 -0700 (PDT) X-Google-Smtp-Source: AB8JxZoM1ddNhf41lxd6N+Yu+QPr4lF8r3BlGBjp0wJcZZc1HCUTr9MbYzXGvZlTkWy5OCgNyOmM X-Received: by 2002:a17:902:9689:: with SMTP id n9-v6mr6132345plp.363.1527149186099; Thu, 24 May 2018 01:06:26 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1527149186; cv=none; d=google.com; s=arc-20160816; b=gaYL9G8HycEWFeVmDJwbafHVtvuqbT7U0dujBu3aL+Dmt4fmq0ixIjRPUAulSEhjpC CWjFDd6La5Gp6+9cKC4Cp0hKfDyuFrtvLSUywtD663XOgyBWbT5IFufr06ns933QyYiw oK+at7TzXkwPi+V6sFVDWukbTUu3OUPy9OFcJMq2fECwuYbBOUgmMGaY4964Pv4e8vuX j6vxwC9f9ljJIMnrRePuP9zgHfHHtBf/f1NbAUgbOAv8cwcsSr6bHwTFhimsEOyPUBgo /G1uzXGMxBEBIL6PwIZ+oGgvZPC++F/i2nrTp134bT4HGEvdQJVGHLCuyix4Xd9N4wcD D1og== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dmarc-filter:dkim-signature:dkim-signature :arc-authentication-results; bh=5GxV54UDK3+pffKn5Aq9ERACLft2rb1TuJdNkPCNT3g=; b=DbXKLxUwDorRCx4lmLOzrjcmhi63ETk/qT4c9tkvIY8ypDhI6+5/rhDNELALMUardn 5P/0yOKP03WsnncOpYpEX+0ZRIijMH4Brh7/Sbx0ZbTlY+vcqw4xIwL1UjEHgYLSvVAq 5Asfe1Ud1vBMkfrzW1cgV7+68AORuYsW91FgYRVxFcDKvv66REyZg2NJrSKegte9MKX8 6TBHTcFRsw+Ql81PBIwut97wmCUIuFLmv6I/D+cEGUEc2UWf2Uq/PJxwVC942s1atKS8 eXd4N2nYkhX7FgPoPZ+j/QtI7bbjKJk2wSj+/QSfsTnGyDYh6MP+gzVoW2FiuUvARlte goPw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@codeaurora.org header.s=default header.b=N09KdFqC; dkim=pass header.i=@codeaurora.org header.s=default header.b=lXRBTwXA; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s189-v6si6048125pgb.486.2018.05.24.01.05.30; Thu, 24 May 2018 01:06:26 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@codeaurora.org header.s=default header.b=N09KdFqC; dkim=pass header.i=@codeaurora.org header.s=default header.b=lXRBTwXA; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965349AbeEXHwF (ORCPT + 99 others); Thu, 24 May 2018 03:52:05 -0400 Received: from smtp.codeaurora.org ([198.145.29.96]:52992 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965241AbeEXHuk (ORCPT ); Thu, 24 May 2018 03:50:40 -0400 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 35A3160C67; Thu, 24 May 2018 07:50:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1527148240; bh=ttDQUQ/t6lSwk0qohgvP76wi5rmpRbMp5nrSr4itlNk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=N09KdFqCe0IG/w8Njs+QN+8bH/hS23ZETW1SWtpQqugOffwrzzxHLJHP+EGPO33uN JWM1t797fprJO98CaCMM+jG2iPThAEin4+GH6ZhPGPwHkD27mVlKS+anB+UFyFKlzQ /ZavAK0Funap57w3nwx2lDFwb3TBHZZSrpdaZngE= X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on pdx-caf-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00, DKIM_SIGNED,T_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0 Received: from lx-ilial.mea.qualcomm.com (unknown [185.23.60.4]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: ilialin@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 05DF060A06; Thu, 24 May 2018 07:50:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1527148239; bh=ttDQUQ/t6lSwk0qohgvP76wi5rmpRbMp5nrSr4itlNk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lXRBTwXAOyP7CvHSAX1cSngTOlNYGmriAzXoknWm+K7VNCG6gdGSfJL/35y70LfGF q2XzcsbLkEdXj2xysdegSPKRkcw/WFGZW9nFE08QfwpKr5x2ixPSN9rdS5TEHmNmQl Du/U2iLoGAp39JSJcxOaNzWwxeIE6WbUHSNJPCWY= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org 05DF060A06 Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=ilialin@codeaurora.org From: Ilia Lin To: mturquette@baylibre.com, sboyd@kernel.org, robh@kernel.org, mark.rutland@arm.com, andy.gross@linaro.org, david.brown@linaro.org, will.deacon@arm.com Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, linux-soc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, vireshk@kernel.org, ilialin@codeaurora.org, Rajendra Nayak Subject: [PATCH v12 4/8] clk: qcom: Add CPU clock driver for msm8996 Date: Thu, 24 May 2018 10:50:14 +0300 Message-Id: <1527148218-16540-5-git-send-email-ilialin@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1527148218-16540-1-git-send-email-ilialin@codeaurora.org> References: <1527148218-16540-1-git-send-email-ilialin@codeaurora.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Each of the CPU clusters (Power and Perf) on msm8996 are clocked via 2 PLLs, a primary and alternate. There are also 2 Mux'es, a primary and secondary all connected together as shown below +-------+ XO | | +------------------>0 | | | PLL/2 | SMUX +----+ +------->1 | | | | | | | +-------+ | +-------+ | +---->0 | | | | +---------------+ | +----------->1 | CPU clk |Primary PLL +----+ PLL_EARLY | | +------> | +------+-----------+ +------>2 PMUX | +---------------+ | | | | | +------+ | +-->3 | +--^+ ACD +-----+ | +-------+ +---------------+ +------+ | |Alt PLL | | | +---------------------------+ +---------------+ PLL_EARLY The primary PLL is what drives the CPU clk, except for times when we are reprogramming the PLL itself (for rate changes) when we temporarily switch to an alternate PLL. A subsequent patch adds support to switch between primary and alternate PLL during rate changes. The primary PLL operates on a single VCO range, between 600MHz and 3GHz. However the CPUs do support OPPs with frequencies between 300MHz and 600MHz. In order to support running the CPUs at those frequencies we end up having to lock the PLL at twice the rate and drive the CPU clk via the PLL/2 output and SMUX. So for frequencies above 600MHz we follow the following path Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk and for frequencies between 300MHz and 600MHz we follow Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk Support for this is added in a subsequent patch as well. ACD stands for Adaptive Clock Distribution and is used to detect voltage droops. Signed-off-by: Rajendra Nayak Signed-off-by: Ilia Lin --- drivers/clk/qcom/Kconfig | 10 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-alpha-pll.h | 6 + drivers/clk/qcom/clk-cpu-8996.c | 403 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 420 insertions(+) create mode 100644 drivers/clk/qcom/clk-cpu-8996.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index e42e1af..42e84b5 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -33,6 +33,16 @@ config QCOM_CLK_APCS_MSM8916 Say Y if you want to support CPU frequency scaling on devices such as msm8916. +config QCOM_CLK_APCC_MSM8996 + tristate "MSM8996 CPU Clock Controller" + depends on ARM64 + depends on COMMON_CLK_QCOM + select QCOM_KRYO_L2_ACCESSORS + help + Support for the CPU clock controller on msm8996 devices. + Say Y if you want to support CPU clock scaling using CPUfreq + drivers for dyanmic power management. + config QCOM_CLK_RPM tristate "RPM based Clock Controller" depends on COMMON_CLK_QCOM && MFD_QCOM_RPM diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 7c09ab1..a822fc8 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o obj-$(CONFIG_QCOM_A53PLL) += a53-pll.o obj-$(CONFIG_QCOM_CLK_APCS_MSM8916) += apcs-msm8916.o +obj-$(CONFIG_QCOM_CLK_APCC_MSM8996) += clk-cpu-8996.o obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index f981b48..9ce2a32 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -50,6 +50,12 @@ struct pll_vco { u32 val; }; +#define VCO(a, b, c) { \ + .val = a,\ + .min_freq = b,\ + .max_freq = c,\ +} + /** * struct clk_alpha_pll - phase locked loop (PLL) * @offset: base address of registers diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c new file mode 100644 index 0000000..d92cad93 --- /dev/null +++ b/drivers/clk/qcom/clk-cpu-8996.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +/* + * Each of the CPU clusters (Power and Perf) on msm8996 are + * clocked via 2 PLLs, a primary and alternate. There are also + * 2 Mux'es, a primary and secondary all connected together + * as shown below + * + * +-------+ + * XO | | + * +------------------>0 | + * | | + * PLL/2 | SMUX +----+ + * +------->1 | | + * | | | | + * | +-------+ | +-------+ + * | +---->0 | + * | | | + * +---------------+ | +----------->1 | CPU clk + * |Primary PLL +----+ PLL_EARLY | | +------> + * | +------+-----------+ +------>2 PMUX | + * +---------------+ | | | | + * | +------+ | +-->3 | + * +--^+ ACD +-----+ | +-------+ + * +---------------+ +------+ | + * |Alt PLL | | + * | +---------------------------+ + * +---------------+ PLL_EARLY + * + * The primary PLL is what drives the CPU clk, except for times + * when we are reprogramming the PLL itself (for rate changes) when + * we temporarily switch to an alternate PLL. A subsequent patch adds + * support to switch between primary and alternate PLL during rate + * changes. + * + * The primary PLL operates on a single VCO range, between 600MHz + * and 3GHz. However the CPUs do support OPPs with frequencies + * between 300MHz and 600MHz. In order to support running the CPUs + * at those frequencies we end up having to lock the PLL at twice + * the rate and drive the CPU clk via the PLL/2 output and SMUX. + * + * So for frequencies above 600MHz we follow the following path + * Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk + * and for frequencies between 300MHz and 600MHz we follow + * Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk + * Support for this is added in a subsequent patch as well. + * + * ACD stands for Adaptive Clock Distribution and is used to + * detect voltage droops. + */ + +#include +#include +#include + +#include "clk-alpha-pll.h" +#include "clk-regmap.h" + +enum _pmux_input { + DIV_2_INDEX = 0, + PLL_INDEX, + ACD_INDEX, + ALT_INDEX, + NUM_OF_PMUX_INPUTS +}; + +static const u8 prim_pll_regs[PLL_OFF_MAX_REGS] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_ALPHA_VAL] = 0x08, + [PLL_OFF_USER_CTL] = 0x10, + [PLL_OFF_CONFIG_CTL] = 0x18, + [PLL_OFF_CONFIG_CTL_U] = 0x1c, + [PLL_OFF_TEST_CTL] = 0x20, + [PLL_OFF_TEST_CTL_U] = 0x24, + [PLL_OFF_STATUS] = 0x28, +}; + +static const u8 alt_pll_regs[PLL_OFF_MAX_REGS] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_ALPHA_VAL] = 0x08, + [PLL_OFF_ALPHA_VAL_U] = 0x0c, + [PLL_OFF_USER_CTL] = 0x10, + [PLL_OFF_USER_CTL_U] = 0x14, + [PLL_OFF_CONFIG_CTL] = 0x18, + [PLL_OFF_TEST_CTL] = 0x20, + [PLL_OFF_TEST_CTL_U] = 0x24, + [PLL_OFF_STATUS] = 0x28, +}; + +/* PLLs */ + +static const struct alpha_pll_config hfpll_config = { + .l = 60, + .config_ctl_val = 0x200d4828, + .config_ctl_hi_val = 0x006, + .pre_div_mask = BIT(12), + .post_div_mask = 0x3 << 8, + .main_output_mask = BIT(0), + .early_output_mask = BIT(3), +}; + +static struct clk_alpha_pll perfcl_pll = { + .offset = 0x80000, + .regs = prim_pll_regs, + .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, + .clkr.hw.init = &(struct clk_init_data){ + .name = "perfcl_pll", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_huayra_ops, + }, +}; + +static struct clk_alpha_pll pwrcl_pll = { + .offset = 0x0, + .regs = prim_pll_regs, + .flags = SUPPORTS_DYNAMIC_UPDATE | SUPPORTS_FSM_MODE, + .clkr.hw.init = &(struct clk_init_data){ + .name = "pwrcl_pll", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_huayra_ops, + }, +}; + +static const struct pll_vco alt_pll_vco_modes[] = { + VCO(3, 250000000, 500000000), + VCO(2, 500000000, 750000000), + VCO(1, 750000000, 1000000000), + VCO(0, 1000000000, 2150400000), +}; + +static const struct alpha_pll_config altpll_config = { + .l = 16, + .vco_val = 0x3 << 20, + .vco_mask = 0x3 << 20, + .config_ctl_val = 0x4001051b, + .post_div_mask = 0x3 << 8, + .post_div_val = 0x1, + .main_output_mask = BIT(0), + .early_output_mask = BIT(3), +}; + +static struct clk_alpha_pll perfcl_alt_pll = { + .offset = 0x80100, + .regs = alt_pll_regs, + .vco_table = alt_pll_vco_modes, + .num_vco = ARRAY_SIZE(alt_pll_vco_modes), + .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, + .clkr.hw.init = &(struct clk_init_data) { + .name = "perfcl_alt_pll", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_hwfsm_ops, + }, +}; + +static struct clk_alpha_pll pwrcl_alt_pll = { + .offset = 0x100, + .regs = alt_pll_regs, + .vco_table = alt_pll_vco_modes, + .num_vco = ARRAY_SIZE(alt_pll_vco_modes), + .flags = SUPPORTS_OFFLINE_REQ | SUPPORTS_FSM_MODE, + .clkr.hw.init = &(struct clk_init_data) { + .name = "pwrcl_alt_pll", + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_hwfsm_ops, + }, +}; + +/* Mux'es */ + +struct clk_cpu_8996_mux { + u32 reg; + u8 shift; + u8 width; + struct clk_hw *pll; + struct clk_regmap clkr; +}; + +static inline +struct clk_cpu_8996_mux *to_clk_cpu_8996_mux_hw(struct clk_hw *hw) +{ + return container_of(to_clk_regmap(hw), struct clk_cpu_8996_mux, clkr); +} + +static u8 clk_cpu_8996_mux_get_parent(struct clk_hw *hw) +{ + u32 val; + struct clk_regmap *clkr = to_clk_regmap(hw); + struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); + u32 mask = (u32)GENMASK(cpuclk->width - 1, 0); + + regmap_read(clkr->regmap, cpuclk->reg, &val); + val >>= (u32)(cpuclk->shift); + + return (u8)(val & mask); +} + +static int clk_cpu_8996_mux_set_parent(struct clk_hw *hw, u8 index) +{ + u32 val; + struct clk_regmap *clkr = to_clk_regmap(hw); + struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); + unsigned int mask = GENMASK(cpuclk->width + cpuclk->shift - 1, + cpuclk->shift); + + val = (u32)index; + val <<= (u32)(cpuclk->shift); + + return regmap_update_bits(clkr->regmap, cpuclk->reg, mask, val); +} + +static int +clk_cpu_8996_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +{ + struct clk_cpu_8996_mux *cpuclk = to_clk_cpu_8996_mux_hw(hw); + struct clk_hw *parent = cpuclk->pll; + + req->best_parent_rate = clk_hw_round_rate(parent, req->rate); + req->best_parent_hw = parent; + + return 0; +} + +const struct clk_ops clk_cpu_8996_mux_ops = { + .set_parent = clk_cpu_8996_mux_set_parent, + .get_parent = clk_cpu_8996_mux_get_parent, + .determine_rate = clk_cpu_8996_mux_determine_rate, +}; + +static struct clk_cpu_8996_mux pwrcl_smux = { + .reg = 0x40, + .shift = 2, + .width = 2, + .clkr.hw.init = &(struct clk_init_data) { + .name = "pwrcl_smux", + .parent_names = (const char *[]){ + "xo", + "pwrcl_pll_main", + }, + .num_parents = 2, + .ops = &clk_cpu_8996_mux_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_cpu_8996_mux perfcl_smux = { + .reg = 0x80040, + .shift = 2, + .width = 2, + .clkr.hw.init = &(struct clk_init_data) { + .name = "perfcl_smux", + .parent_names = (const char *[]){ + "xo", + "perfcl_pll_main", + }, + .num_parents = 2, + .ops = &clk_cpu_8996_mux_ops, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static struct clk_cpu_8996_mux pwrcl_pmux = { + .reg = 0x40, + .shift = 0, + .width = 2, + .pll = &pwrcl_pll.clkr.hw, + .clkr.hw.init = &(struct clk_init_data) { + .name = "pwrcl_pmux", + .parent_names = (const char *[]){ + "pwrcl_smux", + "pwrcl_pll", + "pwrcl_pll_acd", + "pwrcl_alt_pll", + }, + .num_parents = 4, + .ops = &clk_cpu_8996_mux_ops, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static struct clk_cpu_8996_mux perfcl_pmux = { + .reg = 0x80040, + .shift = 0, + .width = 2, + .pll = &perfcl_pll.clkr.hw, + .clkr.hw.init = &(struct clk_init_data) { + .name = "perfcl_pmux", + .parent_names = (const char *[]){ + "perfcl_smux", + "perfcl_pll", + "perfcl_pll_acd", + "perfcl_alt_pll", + }, + .num_parents = 4, + .ops = &clk_cpu_8996_mux_ops, + .flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + }, +}; + +static const struct regmap_config cpu_msm8996_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x80210, + .fast_io = true, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +struct clk_regmap *clks[] = { + &perfcl_pll.clkr, + &pwrcl_pll.clkr, + &perfcl_alt_pll.clkr, + &pwrcl_alt_pll.clkr, + &perfcl_smux.clkr, + &pwrcl_smux.clkr, + &perfcl_pmux.clkr, + &pwrcl_pmux.clkr, +}; + +static int +qcom_cpu_clk_msm8996_register_clks(struct device *dev, struct regmap *regmap) +{ + int i, ret; + + perfcl_smux.pll = clk_hw_register_fixed_factor(dev, "perfcl_pll_main", + "perfcl_pll", + CLK_SET_RATE_PARENT, 1, 2); + + pwrcl_smux.pll = clk_hw_register_fixed_factor(dev, "pwrcl_pll_main", + "pwrcl_pll", + CLK_SET_RATE_PARENT, 1, 2); + + for (i = 0; i < ARRAY_SIZE(clks); i++) { + ret = devm_clk_register_regmap(dev, clks[i]); + if (ret) + return ret; + } + + clk_alpha_pll_configure(&perfcl_pll, regmap, &hfpll_config); + clk_alpha_pll_configure(&pwrcl_pll, regmap, &hfpll_config); + clk_alpha_pll_configure(&perfcl_alt_pll, regmap, &altpll_config); + clk_alpha_pll_configure(&pwrcl_alt_pll, regmap, &altpll_config); + + return ret; +} + +static int qcom_cpu_clk_msm8996_driver_probe(struct platform_device *pdev) +{ + int ret; + void __iomem *base; + struct resource *res; + struct regmap *regmap; + struct clk_hw_onecell_data *data; + struct device *dev = &pdev->dev; + + data = devm_kzalloc(dev, sizeof(*data) + 2 * sizeof(struct clk_hw *), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, &cpu_msm8996_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = qcom_cpu_clk_msm8996_register_clks(dev, regmap); + if (ret) + return ret; + + data->hws[0] = &pwrcl_pmux.clkr.hw; + data->hws[1] = &perfcl_pmux.clkr.hw; + data->num = 2; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, data); +} + +static const struct of_device_id qcom_cpu_clk_msm8996_match_table[] = { + { .compatible = "qcom,msm8996-apcc" }, + {} +}; + +static struct platform_driver qcom_cpu_clk_msm8996_driver = { + .probe = qcom_cpu_clk_msm8996_driver_probe, + .driver = { + .name = "qcom-msm8996-apcc", + .of_match_table = qcom_cpu_clk_msm8996_match_table, + }, +}; +module_platform_driver(qcom_cpu_clk_msm8996_driver); + +MODULE_ALIAS("platform:msm8996-apcc"); +MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver"); +MODULE_LICENSE("GPL v2"); -- 1.9.1