From: Pavel Kubelun Date: Wed, 23 Nov 2016 15:53:52 +0000 (+0300) Subject: ipq806x: refactor rpm clock controller patches X-Git-Tag: v17.01.0-rc1~665 X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fopenwrt.git;a=commitdiff_plain;h=550f71ec8d1562c0deec8259cd753e27aa922e33;ds=sidebyside ipq806x: refactor rpm clock controller patches RPM clock controller driver had made its way upstream and previous approach of directly redoing a driver to support ipq806x is a no go anymore. Thus reverting mentioned patches to upstream state and renaming in correct patch numbering accordance. To make the driver work on ipq806x boards we introduce a custom patch. Signed-off-by: Pavel Kubelun --- diff --git a/target/linux/ipq806x/patches-4.4/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.4/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch new file mode 100644 index 0000000000..83b97bf5cf --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/012-1-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch @@ -0,0 +1,746 @@ +From patchwork Wed Nov 2 15:56:56 2016 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: [v9,1/3] clk: qcom: Add support for SMD-RPM Clocks +From: Georgi Djakov +X-Patchwork-Id: 9409419 +Message-Id: <20161102155658.32203-2-georgi.djakov@linaro.org> +To: sboyd@codeaurora.org, mturquette@baylibre.com +Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, + robh+dt@kernel.org, mark.rutland@arm.com, + linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, + georgi.djakov@linaro.org +Date: Wed, 2 Nov 2016 17:56:56 +0200 + +This adds initial support for clocks controlled by the Resource +Power Manager (RPM) processor on some Qualcomm SoCs, which use +the qcom_smd_rpm driver to communicate with RPM. +Such platforms are msm8916, apq8084 and msm8974. + +The RPM is a dedicated hardware engine for managing the shared +SoC resources in order to keep the lowest power profile. It +communicates with other hardware subsystems via shared memory +and accepts clock requests, aggregates the requests and turns +the clocks on/off or scales them on demand. + +This driver is based on the codeaurora.org driver: +https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/clk/qcom/clock-rpm.c + +Signed-off-by: Georgi Djakov +--- + .../devicetree/bindings/clock/qcom,rpmcc.txt | 36 ++ + drivers/clk/qcom/Kconfig | 16 + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-smd-rpm.c | 571 +++++++++++++++++++++ + include/dt-bindings/clock/qcom,rpmcc.h | 45 ++ + 5 files changed, 669 insertions(+) + create mode 100644 Documentation/devicetree/bindings/clock/qcom,rpmcc.txt + create mode 100644 drivers/clk/qcom/clk-smd-rpm.c + create mode 100644 include/dt-bindings/clock/qcom,rpmcc.h + +-- +To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in +the body of a message to majordomo@vger.kernel.org +More majordomo info at http://vger.kernel.org/majordomo-info.html + +--- /dev/null ++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +@@ -0,0 +1,36 @@ ++Qualcomm RPM Clock Controller Binding ++------------------------------------------------ ++The RPM is a dedicated hardware engine for managing the shared ++SoC resources in order to keep the lowest power profile. It ++communicates with other hardware subsystems via shared memory ++and accepts clock requests, aggregates the requests and turns ++the clocks on/off or scales them on demand. ++ ++Required properties : ++- compatible : shall contain only one of the following. The generic ++ compatible "qcom,rpmcc" should be also included. ++ ++ "qcom,rpmcc-msm8916", "qcom,rpmcc" ++ ++- #clock-cells : shall contain 1 ++ ++Example: ++ smd { ++ compatible = "qcom,smd"; ++ ++ rpm { ++ interrupts = <0 168 1>; ++ qcom,ipc = <&apcs 8 0>; ++ qcom,smd-edge = <15>; ++ ++ rpm_requests { ++ compatible = "qcom,rpm-msm8916"; ++ qcom,smd-channels = "rpm_requests"; ++ ++ rpmcc: clock-controller { ++ compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc"; ++ #clock-cells = <1>; ++ }; ++ }; ++ }; ++ }; +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -2,6 +2,9 @@ config QCOM_GDSC + bool + select PM_GENERIC_DOMAINS if PM + ++config QCOM_RPMCC ++ bool ++ + config COMMON_CLK_QCOM + tristate "Support for Qualcomm's clock controllers" + depends on OF +@@ -9,6 +12,19 @@ config COMMON_CLK_QCOM + select REGMAP_MMIO + select RESET_CONTROLLER + ++config QCOM_CLK_SMD_RPM ++ tristate "RPM over SMD based Clock Controller" ++ depends on COMMON_CLK_QCOM && QCOM_SMD_RPM ++ select QCOM_RPMCC ++ help ++ The RPM (Resource Power Manager) is a dedicated hardware engine for ++ managing the shared SoC resources in order to keep the lowest power ++ profile. It communicates with other hardware subsystems via shared ++ memory and accepts clock requests, aggregates the requests and turns ++ the clocks on/off or scales them on demand. ++ Say Y if you want to support the clocks exposed by the RPM on ++ platforms such as apq8016, apq8084, msm8974 etc. ++ + config APQ_GCC_8084 + tristate "APQ8084 Global Clock Controller" + select QCOM_GDSC +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -22,3 +22,4 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896 + obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o + obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o ++obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o +--- /dev/null ++++ b/drivers/clk/qcom/clk-smd-rpm.c +@@ -0,0 +1,571 @@ ++/* ++ * Copyright (c) 2016, Linaro Limited ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773 ++#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370 ++#define QCOM_RPM_SMD_KEY_RATE 0x007a484b ++#define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45 ++#define QCOM_RPM_SMD_KEY_STATE 0x54415453 ++#define QCOM_RPM_SCALING_ENABLE_ID 0x2 ++ ++#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \ ++ key) \ ++ static struct clk_smd_rpm _platform##_##_active; \ ++ static struct clk_smd_rpm _platform##_##_name = { \ ++ .rpm_res_type = (type), \ ++ .rpm_clk_id = (r_id), \ ++ .rpm_status_id = (stat_id), \ ++ .rpm_key = (key), \ ++ .peer = &_platform##_##_active, \ ++ .rate = INT_MAX, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_smd_rpm_ops, \ ++ .name = #_name, \ ++ .parent_names = (const char *[]){ "xo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ }; \ ++ static struct clk_smd_rpm _platform##_##_active = { \ ++ .rpm_res_type = (type), \ ++ .rpm_clk_id = (r_id), \ ++ .rpm_status_id = (stat_id), \ ++ .active_only = true, \ ++ .rpm_key = (key), \ ++ .peer = &_platform##_##_name, \ ++ .rate = INT_MAX, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_smd_rpm_ops, \ ++ .name = #_active, \ ++ .parent_names = (const char *[]){ "xo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ } ++ ++#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \ ++ stat_id, r, key) \ ++ static struct clk_smd_rpm _platform##_##_active; \ ++ static struct clk_smd_rpm _platform##_##_name = { \ ++ .rpm_res_type = (type), \ ++ .rpm_clk_id = (r_id), \ ++ .rpm_status_id = (stat_id), \ ++ .rpm_key = (key), \ ++ .branch = true, \ ++ .peer = &_platform##_##_active, \ ++ .rate = (r), \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_smd_rpm_branch_ops, \ ++ .name = #_name, \ ++ .parent_names = (const char *[]){ "xo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ }; \ ++ static struct clk_smd_rpm _platform##_##_active = { \ ++ .rpm_res_type = (type), \ ++ .rpm_clk_id = (r_id), \ ++ .rpm_status_id = (stat_id), \ ++ .active_only = true, \ ++ .rpm_key = (key), \ ++ .branch = true, \ ++ .peer = &_platform##_##_name, \ ++ .rate = (r), \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_smd_rpm_branch_ops, \ ++ .name = #_active, \ ++ .parent_names = (const char *[]){ "xo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ } ++ ++#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \ ++ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ ++ 0, QCOM_RPM_SMD_KEY_RATE) ++ ++#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \ ++ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \ ++ r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE) ++ ++#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \ ++ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ ++ 0, QCOM_RPM_SMD_KEY_STATE) ++ ++#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \ ++ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ ++ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ ++ QCOM_RPM_KEY_SOFTWARE_ENABLE) ++ ++#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \ ++ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ ++ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ ++ QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY) ++ ++#define to_clk_smd_rpm(_hw) container_of(_hw, struct clk_smd_rpm, hw) ++ ++struct clk_smd_rpm { ++ const int rpm_res_type; ++ const int rpm_key; ++ const int rpm_clk_id; ++ const int rpm_status_id; ++ const bool active_only; ++ bool enabled; ++ bool branch; ++ struct clk_smd_rpm *peer; ++ struct clk_hw hw; ++ unsigned long rate; ++ struct qcom_smd_rpm *rpm; ++}; ++ ++struct clk_smd_rpm_req { ++ __le32 key; ++ __le32 nbytes; ++ __le32 value; ++}; ++ ++struct rpm_cc { ++ struct qcom_rpm *rpm; ++ struct clk_hw_onecell_data data; ++ struct clk_hw *hws[]; ++}; ++ ++struct rpm_smd_clk_desc { ++ struct clk_smd_rpm **clks; ++ size_t num_clks; ++}; ++ ++static DEFINE_MUTEX(rpm_smd_clk_lock); ++ ++static int clk_smd_rpm_handoff(struct clk_smd_rpm *r) ++{ ++ int ret; ++ struct clk_smd_rpm_req req = { ++ .key = cpu_to_le32(r->rpm_key), ++ .nbytes = cpu_to_le32(sizeof(u32)), ++ .value = cpu_to_le32(INT_MAX), ++ }; ++ ++ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, ++ r->rpm_res_type, r->rpm_clk_id, &req, ++ sizeof(req)); ++ if (ret) ++ return ret; ++ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, ++ r->rpm_res_type, r->rpm_clk_id, &req, ++ sizeof(req)); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r, ++ unsigned long rate) ++{ ++ struct clk_smd_rpm_req req = { ++ .key = cpu_to_le32(r->rpm_key), ++ .nbytes = cpu_to_le32(sizeof(u32)), ++ .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */ ++ }; ++ ++ return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, ++ r->rpm_res_type, r->rpm_clk_id, &req, ++ sizeof(req)); ++} ++ ++static int clk_smd_rpm_set_rate_sleep(struct clk_smd_rpm *r, ++ unsigned long rate) ++{ ++ struct clk_smd_rpm_req req = { ++ .key = cpu_to_le32(r->rpm_key), ++ .nbytes = cpu_to_le32(sizeof(u32)), ++ .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */ ++ }; ++ ++ return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, ++ r->rpm_res_type, r->rpm_clk_id, &req, ++ sizeof(req)); ++} ++ ++static void to_active_sleep(struct clk_smd_rpm *r, unsigned long rate, ++ unsigned long *active, unsigned long *sleep) ++{ ++ *active = rate; ++ ++ /* ++ * Active-only clocks don't care what the rate is during sleep. So, ++ * they vote for zero. ++ */ ++ if (r->active_only) ++ *sleep = 0; ++ else ++ *sleep = *active; ++} ++ ++static int clk_smd_rpm_prepare(struct clk_hw *hw) ++{ ++ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); ++ struct clk_smd_rpm *peer = r->peer; ++ unsigned long this_rate = 0, this_sleep_rate = 0; ++ unsigned long peer_rate = 0, peer_sleep_rate = 0; ++ unsigned long active_rate, sleep_rate; ++ int ret = 0; ++ ++ mutex_lock(&rpm_smd_clk_lock); ++ ++ /* Don't send requests to the RPM if the rate has not been set. */ ++ if (!r->rate) ++ goto out; ++ ++ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); ++ ++ /* Take peer clock's rate into account only if it's enabled. */ ++ if (peer->enabled) ++ to_active_sleep(peer, peer->rate, ++ &peer_rate, &peer_sleep_rate); ++ ++ active_rate = max(this_rate, peer_rate); ++ ++ if (r->branch) ++ active_rate = !!active_rate; ++ ++ ret = clk_smd_rpm_set_rate_active(r, active_rate); ++ if (ret) ++ goto out; ++ ++ sleep_rate = max(this_sleep_rate, peer_sleep_rate); ++ if (r->branch) ++ sleep_rate = !!sleep_rate; ++ ++ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); ++ if (ret) ++ /* Undo the active set vote and restore it */ ++ ret = clk_smd_rpm_set_rate_active(r, peer_rate); ++ ++out: ++ if (!ret) ++ r->enabled = true; ++ ++ mutex_unlock(&rpm_smd_clk_lock); ++ ++ return ret; ++} ++ ++static void clk_smd_rpm_unprepare(struct clk_hw *hw) ++{ ++ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); ++ struct clk_smd_rpm *peer = r->peer; ++ unsigned long peer_rate = 0, peer_sleep_rate = 0; ++ unsigned long active_rate, sleep_rate; ++ int ret; ++ ++ mutex_lock(&rpm_smd_clk_lock); ++ ++ if (!r->rate) ++ goto out; ++ ++ /* Take peer clock's rate into account only if it's enabled. */ ++ if (peer->enabled) ++ to_active_sleep(peer, peer->rate, &peer_rate, ++ &peer_sleep_rate); ++ ++ active_rate = r->branch ? !!peer_rate : peer_rate; ++ ret = clk_smd_rpm_set_rate_active(r, active_rate); ++ if (ret) ++ goto out; ++ ++ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; ++ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); ++ if (ret) ++ goto out; ++ ++ r->enabled = false; ++ ++out: ++ mutex_unlock(&rpm_smd_clk_lock); ++} ++ ++static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); ++ struct clk_smd_rpm *peer = r->peer; ++ unsigned long active_rate, sleep_rate; ++ unsigned long this_rate = 0, this_sleep_rate = 0; ++ unsigned long peer_rate = 0, peer_sleep_rate = 0; ++ int ret = 0; ++ ++ mutex_lock(&rpm_smd_clk_lock); ++ ++ if (!r->enabled) ++ goto out; ++ ++ to_active_sleep(r, rate, &this_rate, &this_sleep_rate); ++ ++ /* Take peer clock's rate into account only if it's enabled. */ ++ if (peer->enabled) ++ to_active_sleep(peer, peer->rate, ++ &peer_rate, &peer_sleep_rate); ++ ++ active_rate = max(this_rate, peer_rate); ++ ret = clk_smd_rpm_set_rate_active(r, active_rate); ++ if (ret) ++ goto out; ++ ++ sleep_rate = max(this_sleep_rate, peer_sleep_rate); ++ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); ++ if (ret) ++ goto out; ++ ++ r->rate = rate; ++ ++out: ++ mutex_unlock(&rpm_smd_clk_lock); ++ ++ return ret; ++} ++ ++static long clk_smd_rpm_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ /* ++ * RPM handles rate rounding and we don't have a way to ++ * know what the rate will be, so just return whatever ++ * rate is requested. ++ */ ++ return rate; ++} ++ ++static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); ++ ++ /* ++ * RPM handles rate rounding and we don't have a way to ++ * know what the rate will be, so just return whatever ++ * rate was set. ++ */ ++ return r->rate; ++} ++ ++static int clk_smd_rpm_enable_scaling(struct qcom_smd_rpm *rpm) ++{ ++ int ret; ++ struct clk_smd_rpm_req req = { ++ .key = cpu_to_le32(QCOM_RPM_SMD_KEY_ENABLE), ++ .nbytes = cpu_to_le32(sizeof(u32)), ++ .value = cpu_to_le32(1), ++ }; ++ ++ ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_SLEEP_STATE, ++ QCOM_SMD_RPM_MISC_CLK, ++ QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req)); ++ if (ret) { ++ pr_err("RPM clock scaling (sleep set) not enabled!\n"); ++ return ret; ++ } ++ ++ ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_ACTIVE_STATE, ++ QCOM_SMD_RPM_MISC_CLK, ++ QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req)); ++ if (ret) { ++ pr_err("RPM clock scaling (active set) not enabled!\n"); ++ return ret; ++ } ++ ++ pr_debug("%s: RPM clock scaling is enabled\n", __func__); ++ return 0; ++} ++ ++static const struct clk_ops clk_smd_rpm_ops = { ++ .prepare = clk_smd_rpm_prepare, ++ .unprepare = clk_smd_rpm_unprepare, ++ .set_rate = clk_smd_rpm_set_rate, ++ .round_rate = clk_smd_rpm_round_rate, ++ .recalc_rate = clk_smd_rpm_recalc_rate, ++}; ++ ++static const struct clk_ops clk_smd_rpm_branch_ops = { ++ .prepare = clk_smd_rpm_prepare, ++ .unprepare = clk_smd_rpm_unprepare, ++ .round_rate = clk_smd_rpm_round_rate, ++ .recalc_rate = clk_smd_rpm_recalc_rate, ++}; ++ ++/* msm8916 */ ++DEFINE_CLK_SMD_RPM(msm8916, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); ++DEFINE_CLK_SMD_RPM(msm8916, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); ++DEFINE_CLK_SMD_RPM(msm8916, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); ++DEFINE_CLK_SMD_RPM_QDSS(msm8916, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); ++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk1, bb_clk1_a, 1); ++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk2, bb_clk2_a, 2); ++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk1, rf_clk1_a, 4); ++DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk2, rf_clk2_a, 5); ++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk1_pin, bb_clk1_a_pin, 1); ++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk2_pin, bb_clk2_a_pin, 2); ++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk1_pin, rf_clk1_a_pin, 4); ++DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5); ++ ++static struct clk_smd_rpm *msm8916_clks[] = { ++ [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk, ++ [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk, ++ [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk, ++ [RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk, ++ [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk, ++ [RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk, ++ [RPM_SMD_QDSS_CLK] = &msm8916_qdss_clk, ++ [RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk, ++ [RPM_SMD_BB_CLK1] = &msm8916_bb_clk1, ++ [RPM_SMD_BB_CLK1_A] = &msm8916_bb_clk1_a, ++ [RPM_SMD_BB_CLK2] = &msm8916_bb_clk2, ++ [RPM_SMD_BB_CLK2_A] = &msm8916_bb_clk2_a, ++ [RPM_SMD_RF_CLK1] = &msm8916_rf_clk1, ++ [RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a, ++ [RPM_SMD_RF_CLK2] = &msm8916_rf_clk2, ++ [RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a, ++ [RPM_SMD_BB_CLK1_PIN] = &msm8916_bb_clk1_pin, ++ [RPM_SMD_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin, ++ [RPM_SMD_BB_CLK2_PIN] = &msm8916_bb_clk2_pin, ++ [RPM_SMD_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin, ++ [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin, ++ [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin, ++ [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin, ++ [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin, ++}; ++ ++static const struct rpm_smd_clk_desc rpm_clk_msm8916 = { ++ .clks = msm8916_clks, ++ .num_clks = ARRAY_SIZE(msm8916_clks), ++}; ++ ++static const struct of_device_id rpm_smd_clk_match_table[] = { ++ { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); ++ ++static int rpm_smd_clk_probe(struct platform_device *pdev) ++{ ++ struct clk_hw **hws; ++ struct rpm_cc *rcc; ++ struct clk_hw_onecell_data *data; ++ int ret; ++ size_t num_clks, i; ++ struct qcom_smd_rpm *rpm; ++ struct clk_smd_rpm **rpm_smd_clks; ++ const struct rpm_smd_clk_desc *desc; ++ ++ rpm = dev_get_drvdata(pdev->dev.parent); ++ if (!rpm) { ++ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); ++ return -ENODEV; ++ } ++ ++ desc = of_device_get_match_data(&pdev->dev); ++ if (!desc) ++ return -EINVAL; ++ ++ rpm_smd_clks = desc->clks; ++ num_clks = desc->num_clks; ++ ++ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, ++ GFP_KERNEL); ++ if (!rcc) ++ return -ENOMEM; ++ ++ hws = rcc->hws; ++ data = &rcc->data; ++ data->num = num_clks; ++ ++ for (i = 0; i < num_clks; i++) { ++ if (!rpm_smd_clks[i]) { ++ continue; ++ } ++ ++ rpm_smd_clks[i]->rpm = rpm; ++ ++ ret = clk_smd_rpm_handoff(rpm_smd_clks[i]); ++ if (ret) ++ goto err; ++ } ++ ++ ret = clk_smd_rpm_enable_scaling(rpm); ++ if (ret) ++ goto err; ++ ++ for (i = 0; i < num_clks; i++) { ++ if (!rpm_smd_clks[i]) { ++ data->hws[i] = ERR_PTR(-ENOENT); ++ continue; ++ } ++ ++ ret = devm_clk_hw_register(&pdev->dev, &rpm_smd_clks[i]->hw); ++ if (ret) ++ goto err; ++ } ++ ++ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, ++ data); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret); ++ return ret; ++} ++ ++static int rpm_smd_clk_remove(struct platform_device *pdev) ++{ ++ of_clk_del_provider(pdev->dev.of_node); ++ return 0; ++} ++ ++static struct platform_driver rpm_smd_clk_driver = { ++ .driver = { ++ .name = "qcom-clk-smd-rpm", ++ .of_match_table = rpm_smd_clk_match_table, ++ }, ++ .probe = rpm_smd_clk_probe, ++ .remove = rpm_smd_clk_remove, ++}; ++ ++static int __init rpm_smd_clk_init(void) ++{ ++ return platform_driver_register(&rpm_smd_clk_driver); ++} ++core_initcall(rpm_smd_clk_init); ++ ++static void __exit rpm_smd_clk_exit(void) ++{ ++ platform_driver_unregister(&rpm_smd_clk_driver); ++} ++module_exit(rpm_smd_clk_exit); ++ ++MODULE_DESCRIPTION("Qualcomm RPM over SMD Clock Controller Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qcom-clk-smd-rpm"); +--- /dev/null ++++ b/include/dt-bindings/clock/qcom,rpmcc.h +@@ -0,0 +1,45 @@ ++/* ++ * Copyright 2015 Linaro Limited ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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. ++ */ ++ ++#ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H ++#define _DT_BINDINGS_CLK_MSM_RPMCC_H ++ ++/* msm8916 */ ++#define RPM_SMD_XO_CLK_SRC 0 ++#define RPM_SMD_XO_A_CLK_SRC 1 ++#define RPM_SMD_PCNOC_CLK 2 ++#define RPM_SMD_PCNOC_A_CLK 3 ++#define RPM_SMD_SNOC_CLK 4 ++#define RPM_SMD_SNOC_A_CLK 5 ++#define RPM_SMD_BIMC_CLK 6 ++#define RPM_SMD_BIMC_A_CLK 7 ++#define RPM_SMD_QDSS_CLK 8 ++#define RPM_SMD_QDSS_A_CLK 9 ++#define RPM_SMD_BB_CLK1 10 ++#define RPM_SMD_BB_CLK1_A 11 ++#define RPM_SMD_BB_CLK2 12 ++#define RPM_SMD_BB_CLK2_A 13 ++#define RPM_SMD_RF_CLK1 14 ++#define RPM_SMD_RF_CLK1_A 15 ++#define RPM_SMD_RF_CLK2 16 ++#define RPM_SMD_RF_CLK2_A 17 ++#define RPM_SMD_BB_CLK1_PIN 18 ++#define RPM_SMD_BB_CLK1_A_PIN 19 ++#define RPM_SMD_BB_CLK2_PIN 20 ++#define RPM_SMD_BB_CLK2_A_PIN 21 ++#define RPM_SMD_RF_CLK1_PIN 22 ++#define RPM_SMD_RF_CLK1_A_PIN 23 ++#define RPM_SMD_RF_CLK2_PIN 24 ++#define RPM_SMD_RF_CLK2_A_PIN 25 ++ ++#endif diff --git a/target/linux/ipq806x/patches-4.4/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.4/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch new file mode 100644 index 0000000000..8a38ead567 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/012-2-clk-qcom-Add-support-for-RPM-Clocks.patch @@ -0,0 +1,586 @@ +From 872f91b5ea720c72f81fb46d353c43ecb3263ffa Mon Sep 17 00:00:00 2001 +From: Georgi Djakov +Date: Wed, 2 Nov 2016 17:56:57 +0200 +Subject: clk: qcom: Add support for RPM Clocks + +This adds initial support for clocks controlled by the Resource +Power Manager (RPM) processor on some Qualcomm SoCs, which use +the qcom_rpm driver to communicate with RPM. +Such platforms are apq8064 and msm8960. + +Signed-off-by: Georgi Djakov +Acked-by: Rob Herring +Signed-off-by: Stephen Boyd +--- + .../devicetree/bindings/clock/qcom,rpmcc.txt | 1 + + drivers/clk/qcom/Kconfig | 13 + + drivers/clk/qcom/Makefile | 1 + + drivers/clk/qcom/clk-rpm.c | 489 +++++++++++++++++++++ + include/dt-bindings/clock/qcom,rpmcc.h | 24 + + 5 files changed, 528 insertions(+) + create mode 100644 drivers/clk/qcom/clk-rpm.c + +--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt ++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +@@ -11,6 +11,7 @@ Required properties : + compatible "qcom,rpmcc" should be also included. + + "qcom,rpmcc-msm8916", "qcom,rpmcc" ++ "qcom,rpmcc-apq8064", "qcom,rpmcc" + + - #clock-cells : shall contain 1 + +--- a/drivers/clk/qcom/Kconfig ++++ b/drivers/clk/qcom/Kconfig +@@ -12,6 +12,19 @@ config COMMON_CLK_QCOM + select REGMAP_MMIO + select RESET_CONTROLLER + ++config QCOM_CLK_RPM ++ tristate "RPM based Clock Controller" ++ depends on COMMON_CLK_QCOM && MFD_QCOM_RPM ++ select QCOM_RPMCC ++ help ++ The RPM (Resource Power Manager) is a dedicated hardware engine for ++ managing the shared SoC resources in order to keep the lowest power ++ profile. It communicates with other hardware subsystems via shared ++ memory and accepts clock requests, aggregates the requests and turns ++ the clocks on/off or scales them on demand. ++ Say Y if you want to support the clocks exposed by the RPM on ++ platforms such as ipq806x, msm8660, msm8960 etc. ++ + config QCOM_CLK_SMD_RPM + tristate "RPM over SMD based Clock Controller" + depends on COMMON_CLK_QCOM && QCOM_SMD_RPM +--- a/drivers/clk/qcom/Makefile ++++ b/drivers/clk/qcom/Makefile +@@ -23,3 +23,4 @@ obj-$(CONFIG_MSM_GCC_8974) += gcc-msm897 + obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o + obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o + obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o ++obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o +--- /dev/null ++++ b/drivers/clk/qcom/clk-rpm.c +@@ -0,0 +1,489 @@ ++/* ++ * Copyright (c) 2016, Linaro Limited ++ * Copyright (c) 2014, The Linux Foundation. All rights reserved. ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63 ++#define QCOM_RPM_SCALING_ENABLE_ID 0x2 ++ ++#define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \ ++ static struct clk_rpm _platform##_##_active; \ ++ static struct clk_rpm _platform##_##_name = { \ ++ .rpm_clk_id = (r_id), \ ++ .peer = &_platform##_##_active, \ ++ .rate = INT_MAX, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_rpm_ops, \ ++ .name = #_name, \ ++ .parent_names = (const char *[]){ "pxo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ }; \ ++ static struct clk_rpm _platform##_##_active = { \ ++ .rpm_clk_id = (r_id), \ ++ .peer = &_platform##_##_name, \ ++ .active_only = true, \ ++ .rate = INT_MAX, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_rpm_ops, \ ++ .name = #_active, \ ++ .parent_names = (const char *[]){ "pxo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ } ++ ++#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \ ++ static struct clk_rpm _platform##_##_active; \ ++ static struct clk_rpm _platform##_##_name = { \ ++ .rpm_clk_id = (r_id), \ ++ .active_only = true, \ ++ .peer = &_platform##_##_active, \ ++ .rate = (r), \ ++ .branch = true, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_rpm_branch_ops, \ ++ .name = #_name, \ ++ .parent_names = (const char *[]){ "pxo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ }; \ ++ static struct clk_rpm _platform##_##_active = { \ ++ .rpm_clk_id = (r_id), \ ++ .peer = &_platform##_##_name, \ ++ .rate = (r), \ ++ .branch = true, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_rpm_branch_ops, \ ++ .name = #_active, \ ++ .parent_names = (const char *[]){ "pxo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ } ++ ++#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \ ++ static struct clk_rpm _platform##_##_active; \ ++ static struct clk_rpm _platform##_##_name = { \ ++ .rpm_clk_id = (r_id), \ ++ .peer = &_platform##_##_active, \ ++ .rate = (r), \ ++ .branch = true, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_rpm_branch_ops, \ ++ .name = #_name, \ ++ .parent_names = (const char *[]){ "cxo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ }; \ ++ static struct clk_rpm _platform##_##_active = { \ ++ .rpm_clk_id = (r_id), \ ++ .active_only = true, \ ++ .peer = &_platform##_##_name, \ ++ .rate = (r), \ ++ .branch = true, \ ++ .hw.init = &(struct clk_init_data){ \ ++ .ops = &clk_rpm_branch_ops, \ ++ .name = #_active, \ ++ .parent_names = (const char *[]){ "cxo_board" }, \ ++ .num_parents = 1, \ ++ }, \ ++ } ++ ++#define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw) ++ ++struct clk_rpm { ++ const int rpm_clk_id; ++ const bool active_only; ++ unsigned long rate; ++ bool enabled; ++ bool branch; ++ struct clk_rpm *peer; ++ struct clk_hw hw; ++ struct qcom_rpm *rpm; ++}; ++ ++struct rpm_cc { ++ struct qcom_rpm *rpm; ++ struct clk_hw_onecell_data data; ++ struct clk_hw *hws[]; ++}; ++ ++struct rpm_clk_desc { ++ struct clk_rpm **clks; ++ size_t num_clks; ++}; ++ ++static DEFINE_MUTEX(rpm_clk_lock); ++ ++static int clk_rpm_handoff(struct clk_rpm *r) ++{ ++ int ret; ++ u32 value = INT_MAX; ++ ++ ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, ++ r->rpm_clk_id, &value, 1); ++ if (ret) ++ return ret; ++ ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, ++ r->rpm_clk_id, &value, 1); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate) ++{ ++ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ ++ ++ return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, ++ r->rpm_clk_id, &value, 1); ++} ++ ++static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate) ++{ ++ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ ++ ++ return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, ++ r->rpm_clk_id, &value, 1); ++} ++ ++static void to_active_sleep(struct clk_rpm *r, unsigned long rate, ++ unsigned long *active, unsigned long *sleep) ++{ ++ *active = rate; ++ ++ /* ++ * Active-only clocks don't care what the rate is during sleep. So, ++ * they vote for zero. ++ */ ++ if (r->active_only) ++ *sleep = 0; ++ else ++ *sleep = *active; ++} ++ ++static int clk_rpm_prepare(struct clk_hw *hw) ++{ ++ struct clk_rpm *r = to_clk_rpm(hw); ++ struct clk_rpm *peer = r->peer; ++ unsigned long this_rate = 0, this_sleep_rate = 0; ++ unsigned long peer_rate = 0, peer_sleep_rate = 0; ++ unsigned long active_rate, sleep_rate; ++ int ret = 0; ++ ++ mutex_lock(&rpm_clk_lock); ++ ++ /* Don't send requests to the RPM if the rate has not been set. */ ++ if (!r->rate) ++ goto out; ++ ++ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); ++ ++ /* Take peer clock's rate into account only if it's enabled. */ ++ if (peer->enabled) ++ to_active_sleep(peer, peer->rate, ++ &peer_rate, &peer_sleep_rate); ++ ++ active_rate = max(this_rate, peer_rate); ++ ++ if (r->branch) ++ active_rate = !!active_rate; ++ ++ ret = clk_rpm_set_rate_active(r, active_rate); ++ if (ret) ++ goto out; ++ ++ sleep_rate = max(this_sleep_rate, peer_sleep_rate); ++ if (r->branch) ++ sleep_rate = !!sleep_rate; ++ ++ ret = clk_rpm_set_rate_sleep(r, sleep_rate); ++ if (ret) ++ /* Undo the active set vote and restore it */ ++ ret = clk_rpm_set_rate_active(r, peer_rate); ++ ++out: ++ if (!ret) ++ r->enabled = true; ++ ++ mutex_unlock(&rpm_clk_lock); ++ ++ return ret; ++} ++ ++static void clk_rpm_unprepare(struct clk_hw *hw) ++{ ++ struct clk_rpm *r = to_clk_rpm(hw); ++ struct clk_rpm *peer = r->peer; ++ unsigned long peer_rate = 0, peer_sleep_rate = 0; ++ unsigned long active_rate, sleep_rate; ++ int ret; ++ ++ mutex_lock(&rpm_clk_lock); ++ ++ if (!r->rate) ++ goto out; ++ ++ /* Take peer clock's rate into account only if it's enabled. */ ++ if (peer->enabled) ++ to_active_sleep(peer, peer->rate, &peer_rate, ++ &peer_sleep_rate); ++ ++ active_rate = r->branch ? !!peer_rate : peer_rate; ++ ret = clk_rpm_set_rate_active(r, active_rate); ++ if (ret) ++ goto out; ++ ++ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; ++ ret = clk_rpm_set_rate_sleep(r, sleep_rate); ++ if (ret) ++ goto out; ++ ++ r->enabled = false; ++ ++out: ++ mutex_unlock(&rpm_clk_lock); ++} ++ ++static int clk_rpm_set_rate(struct clk_hw *hw, ++ unsigned long rate, unsigned long parent_rate) ++{ ++ struct clk_rpm *r = to_clk_rpm(hw); ++ struct clk_rpm *peer = r->peer; ++ unsigned long active_rate, sleep_rate; ++ unsigned long this_rate = 0, this_sleep_rate = 0; ++ unsigned long peer_rate = 0, peer_sleep_rate = 0; ++ int ret = 0; ++ ++ mutex_lock(&rpm_clk_lock); ++ ++ if (!r->enabled) ++ goto out; ++ ++ to_active_sleep(r, rate, &this_rate, &this_sleep_rate); ++ ++ /* Take peer clock's rate into account only if it's enabled. */ ++ if (peer->enabled) ++ to_active_sleep(peer, peer->rate, ++ &peer_rate, &peer_sleep_rate); ++ ++ active_rate = max(this_rate, peer_rate); ++ ret = clk_rpm_set_rate_active(r, active_rate); ++ if (ret) ++ goto out; ++ ++ sleep_rate = max(this_sleep_rate, peer_sleep_rate); ++ ret = clk_rpm_set_rate_sleep(r, sleep_rate); ++ if (ret) ++ goto out; ++ ++ r->rate = rate; ++ ++out: ++ mutex_unlock(&rpm_clk_lock); ++ ++ return ret; ++} ++ ++static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *parent_rate) ++{ ++ /* ++ * RPM handles rate rounding and we don't have a way to ++ * know what the rate will be, so just return whatever ++ * rate is requested. ++ */ ++ return rate; ++} ++ ++static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct clk_rpm *r = to_clk_rpm(hw); ++ ++ /* ++ * RPM handles rate rounding and we don't have a way to ++ * know what the rate will be, so just return whatever ++ * rate was set. ++ */ ++ return r->rate; ++} ++ ++static const struct clk_ops clk_rpm_ops = { ++ .prepare = clk_rpm_prepare, ++ .unprepare = clk_rpm_unprepare, ++ .set_rate = clk_rpm_set_rate, ++ .round_rate = clk_rpm_round_rate, ++ .recalc_rate = clk_rpm_recalc_rate, ++}; ++ ++static const struct clk_ops clk_rpm_branch_ops = { ++ .prepare = clk_rpm_prepare, ++ .unprepare = clk_rpm_unprepare, ++ .round_rate = clk_rpm_round_rate, ++ .recalc_rate = clk_rpm_recalc_rate, ++}; ++ ++/* apq8064 */ ++DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); ++DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); ++DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); ++DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); ++DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); ++DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); ++DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); ++DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); ++DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); ++ ++static struct clk_rpm *apq8064_clks[] = { ++ [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, ++ [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, ++ [RPM_CFPB_CLK] = &apq8064_cfpb_clk, ++ [RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk, ++ [RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk, ++ [RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk, ++ [RPM_EBI1_CLK] = &apq8064_ebi1_clk, ++ [RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk, ++ [RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk, ++ [RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk, ++ [RPM_MMFPB_CLK] = &apq8064_mmfpb_clk, ++ [RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk, ++ [RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk, ++ [RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk, ++ [RPM_SFPB_CLK] = &apq8064_sfpb_clk, ++ [RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk, ++ [RPM_QDSS_CLK] = &apq8064_qdss_clk, ++ [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, ++}; ++ ++static const struct rpm_clk_desc rpm_clk_apq8064 = { ++ .clks = apq8064_clks, ++ .num_clks = ARRAY_SIZE(apq8064_clks), ++}; ++ ++static const struct of_device_id rpm_clk_match_table[] = { ++ { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, rpm_clk_match_table); ++ ++static int rpm_clk_probe(struct platform_device *pdev) ++{ ++ struct clk_hw **hws; ++ struct rpm_cc *rcc; ++ struct clk_hw_onecell_data *data; ++ int ret; ++ size_t num_clks, i; ++ struct qcom_rpm *rpm; ++ struct clk_rpm **rpm_clks; ++ const struct rpm_clk_desc *desc; ++ ++ rpm = dev_get_drvdata(pdev->dev.parent); ++ if (!rpm) { ++ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); ++ return -ENODEV; ++ } ++ ++ desc = of_device_get_match_data(&pdev->dev); ++ if (!desc) ++ return -EINVAL; ++ ++ rpm_clks = desc->clks; ++ num_clks = desc->num_clks; ++ ++ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, ++ GFP_KERNEL); ++ if (!rcc) ++ return -ENOMEM; ++ ++ hws = rcc->hws; ++ data = &rcc->data; ++ data->num = num_clks; ++ ++ for (i = 0; i < num_clks; i++) { ++ if (!rpm_clks[i]) ++ continue; ++ ++ rpm_clks[i]->rpm = rpm; ++ ++ ret = clk_rpm_handoff(rpm_clks[i]); ++ if (ret) ++ goto err; ++ } ++ ++ for (i = 0; i < num_clks; i++) { ++ if (!rpm_clks[i]) { ++ data->hws[i] = ERR_PTR(-ENOENT); ++ continue; ++ } ++ ++ ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); ++ if (ret) ++ goto err; ++ } ++ ++ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, ++ data); ++ if (ret) ++ goto err; ++ ++ return 0; ++err: ++ dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret); ++ return ret; ++} ++ ++static int rpm_clk_remove(struct platform_device *pdev) ++{ ++ of_clk_del_provider(pdev->dev.of_node); ++ return 0; ++} ++ ++static struct platform_driver rpm_clk_driver = { ++ .driver = { ++ .name = "qcom-clk-rpm", ++ .of_match_table = rpm_clk_match_table, ++ }, ++ .probe = rpm_clk_probe, ++ .remove = rpm_clk_remove, ++}; ++ ++static int __init rpm_clk_init(void) ++{ ++ return platform_driver_register(&rpm_clk_driver); ++} ++core_initcall(rpm_clk_init); ++ ++static void __exit rpm_clk_exit(void) ++{ ++ platform_driver_unregister(&rpm_clk_driver); ++} ++module_exit(rpm_clk_exit); ++ ++MODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qcom-clk-rpm"); +--- a/include/dt-bindings/clock/qcom,rpmcc.h ++++ b/include/dt-bindings/clock/qcom,rpmcc.h +@@ -14,6 +14,30 @@ + #ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H + #define _DT_BINDINGS_CLK_MSM_RPMCC_H + ++/* apq8064 */ ++#define RPM_PXO_CLK 0 ++#define RPM_PXO_A_CLK 1 ++#define RPM_CXO_CLK 2 ++#define RPM_CXO_A_CLK 3 ++#define RPM_APPS_FABRIC_CLK 4 ++#define RPM_APPS_FABRIC_A_CLK 5 ++#define RPM_CFPB_CLK 6 ++#define RPM_CFPB_A_CLK 7 ++#define RPM_QDSS_CLK 8 ++#define RPM_QDSS_A_CLK 9 ++#define RPM_DAYTONA_FABRIC_CLK 10 ++#define RPM_DAYTONA_FABRIC_A_CLK 11 ++#define RPM_EBI1_CLK 12 ++#define RPM_EBI1_A_CLK 13 ++#define RPM_MM_FABRIC_CLK 14 ++#define RPM_MM_FABRIC_A_CLK 15 ++#define RPM_MMFPB_CLK 16 ++#define RPM_MMFPB_A_CLK 17 ++#define RPM_SYS_FABRIC_CLK 18 ++#define RPM_SYS_FABRIC_A_CLK 19 ++#define RPM_SFPB_CLK 20 ++#define RPM_SFPB_A_CLK 21 ++ + /* msm8916 */ + #define RPM_SMD_XO_CLK_SRC 0 + #define RPM_SMD_XO_A_CLK_SRC 1 diff --git a/target/linux/ipq806x/patches-4.4/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch b/target/linux/ipq806x/patches-4.4/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch new file mode 100644 index 0000000000..e5c1870eab --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/012-3-clk-qcom-clk-rpm-Fix-clk_hw-references.patch @@ -0,0 +1,94 @@ +From c260524aba53b57f72b5446ed553080df30b4d09 Mon Sep 17 00:00:00 2001 +From: Georgi Djakov +Date: Wed, 23 Nov 2016 16:52:49 +0200 +Subject: clk: qcom: clk-rpm: Fix clk_hw references + +Fix the clk_hw references to the actual clocks and add a xlate function +to return the hw pointers from the already existing static array. + +Reported-by: Michael Scott +Signed-off-by: Georgi Djakov +Signed-off-by: Stephen Boyd +--- + drivers/clk/qcom/clk-rpm.c | 36 ++++++++++++++++++++++-------------- + 1 file changed, 22 insertions(+), 14 deletions(-) + +--- a/drivers/clk/qcom/clk-rpm.c ++++ b/drivers/clk/qcom/clk-rpm.c +@@ -127,8 +127,8 @@ struct clk_rpm { + + struct rpm_cc { + struct qcom_rpm *rpm; +- struct clk_hw_onecell_data data; +- struct clk_hw *hws[]; ++ struct clk_rpm **clks; ++ size_t num_clks; + }; + + struct rpm_clk_desc { +@@ -391,11 +391,23 @@ static const struct of_device_id rpm_clk + }; + MODULE_DEVICE_TABLE(of, rpm_clk_match_table); + ++static struct clk_hw *qcom_rpm_clk_hw_get(struct of_phandle_args *clkspec, ++ void *data) ++{ ++ struct rpm_cc *rcc = data; ++ unsigned int idx = clkspec->args[0]; ++ ++ if (idx >= rcc->num_clks) { ++ pr_err("%s: invalid index %u\n", __func__, idx); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT); ++} ++ + static int rpm_clk_probe(struct platform_device *pdev) + { +- struct clk_hw **hws; + struct rpm_cc *rcc; +- struct clk_hw_onecell_data *data; + int ret; + size_t num_clks, i; + struct qcom_rpm *rpm; +@@ -415,14 +427,12 @@ static int rpm_clk_probe(struct platform + rpm_clks = desc->clks; + num_clks = desc->num_clks; + +- rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, +- GFP_KERNEL); ++ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL); + if (!rcc) + return -ENOMEM; + +- hws = rcc->hws; +- data = &rcc->data; +- data->num = num_clks; ++ rcc->clks = rpm_clks; ++ rcc->num_clks = num_clks; + + for (i = 0; i < num_clks; i++) { + if (!rpm_clks[i]) +@@ -436,18 +446,16 @@ static int rpm_clk_probe(struct platform + } + + for (i = 0; i < num_clks; i++) { +- if (!rpm_clks[i]) { +- data->hws[i] = ERR_PTR(-ENOENT); ++ if (!rpm_clks[i]) + continue; +- } + + ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); + if (ret) + goto err; + } + +- ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, +- data); ++ ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_rpm_clk_hw_get, ++ rcc); + if (ret) + goto err; + diff --git a/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch b/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch index cc7c4cd177..83623206ce 100644 --- a/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch +++ b/target/linux/ipq806x/patches-4.4/138-clk-qcom-Add-HFPLL-driver.patch @@ -73,7 +73,7 @@ Signed-off-by: Stephen Boyd + }; --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig -@@ -106,3 +106,11 @@ config MSM_MMCC_8974 +@@ -135,3 +135,11 @@ config MSM_MMCC_8974 Support for the multimedia clock controller on msm8974 devices. Say Y if you want to support multimedia devices such as display, graphics, video encode/decode, camera, etc. @@ -87,10 +87,10 @@ Signed-off-by: Stephen Boyd + such as MSM8974, APQ8084, etc. --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile -@@ -23,3 +23,4 @@ obj-$(CONFIG_MSM_LCC_8960) += lcc-msm896 - obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o - obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o +@@ -25,3 +25,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8 obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o + obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o + obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o +obj-$(CONFIG_QCOM_HFPLL) += hfpll.o --- /dev/null +++ b/drivers/clk/qcom/hfpll.c diff --git a/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch index b1720b0898..bbfcab9cc0 100644 --- a/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch +++ b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch @@ -30,7 +30,7 @@ drivers/clk/qcom/Kconfig | 4 ++ --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig -@@ -114,3 +114,7 @@ config QCOM_HFPLL +@@ -143,3 +143,7 @@ config QCOM_HFPLL Support for the high-frequency PLLs present on Qualcomm devices. Say Y if you want to support CPU frequency scaling on devices such as MSM8974, APQ8084, etc. diff --git a/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch b/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch index 2b0bbb01a3..56374c5d0c 100644 --- a/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch +++ b/target/linux/ipq806x/patches-4.4/141-clk-qcom-Add-KPSS-ACC-GCC-driver.patch @@ -82,7 +82,7 @@ Signed-off-by: Stephen Boyd + }; --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig -@@ -115,6 +115,14 @@ config QCOM_HFPLL +@@ -144,6 +144,14 @@ config QCOM_HFPLL Say Y if you want to support CPU frequency scaling on devices such as MSM8974, APQ8084, etc. diff --git a/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch b/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch index 813366fdca..bd4e7bca3d 100644 --- a/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch +++ b/target/linux/ipq806x/patches-4.4/142-clk-qcom-Add-Krait-clock-controller-driver.patch @@ -56,7 +56,7 @@ Signed-off-by: Stephen Boyd + }; --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig -@@ -123,6 +123,14 @@ config KPSS_XCC +@@ -152,6 +152,14 @@ config KPSS_XCC if you want to support CPU frequency scaling on devices such as MSM8960, APQ8064, etc. @@ -73,9 +73,9 @@ Signed-off-by: Stephen Boyd select KRAIT_L2_ACCESSORS --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile -@@ -26,3 +26,4 @@ obj-$(CONFIG_MSM_GCC_8974) += gcc-msm897 - obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o - obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o +@@ -28,3 +28,4 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8 + obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o + obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o obj-$(CONFIG_QCOM_HFPLL) += hfpll.o +obj-$(CONFIG_KRAITCC) += krait-cc.o --- /dev/null diff --git a/target/linux/ipq806x/patches-4.4/178-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.4/178-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch deleted file mode 100644 index 8126c7c51e..0000000000 --- a/target/linux/ipq806x/patches-4.4/178-clk-qcom-Add-support-for-SMD-RPM-Clocks.patch +++ /dev/null @@ -1,746 +0,0 @@ -From patchwork Wed Nov 2 15:56:56 2016 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v9,1/3] clk: qcom: Add support for SMD-RPM Clocks -From: Georgi Djakov -X-Patchwork-Id: 9409419 -Message-Id: <20161102155658.32203-2-georgi.djakov@linaro.org> -To: sboyd@codeaurora.org, mturquette@baylibre.com -Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, - robh+dt@kernel.org, mark.rutland@arm.com, - linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - georgi.djakov@linaro.org -Date: Wed, 2 Nov 2016 17:56:56 +0200 - -This adds initial support for clocks controlled by the Resource -Power Manager (RPM) processor on some Qualcomm SoCs, which use -the qcom_smd_rpm driver to communicate with RPM. -Such platforms are msm8916, apq8084 and msm8974. - -The RPM is a dedicated hardware engine for managing the shared -SoC resources in order to keep the lowest power profile. It -communicates with other hardware subsystems via shared memory -and accepts clock requests, aggregates the requests and turns -the clocks on/off or scales them on demand. - -This driver is based on the codeaurora.org driver: -https://www.codeaurora.org/cgit/quic/la/kernel/msm-3.10/tree/drivers/clk/qcom/clock-rpm.c - -Signed-off-by: Georgi Djakov ---- - .../devicetree/bindings/clock/qcom,rpmcc.txt | 36 ++ - drivers/clk/qcom/Kconfig | 16 + - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/clk-smd-rpm.c | 571 +++++++++++++++++++++ - include/dt-bindings/clock/qcom,rpmcc.h | 45 ++ - 5 files changed, 669 insertions(+) - create mode 100644 Documentation/devicetree/bindings/clock/qcom,rpmcc.txt - create mode 100644 drivers/clk/qcom/clk-smd-rpm.c - create mode 100644 include/dt-bindings/clock/qcom,rpmcc.h - --- -To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in -the body of a message to majordomo@vger.kernel.org -More majordomo info at http://vger.kernel.org/majordomo-info.html - ---- /dev/null -+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt -@@ -0,0 +1,36 @@ -+Qualcomm RPM Clock Controller Binding -+------------------------------------------------ -+The RPM is a dedicated hardware engine for managing the shared -+SoC resources in order to keep the lowest power profile. It -+communicates with other hardware subsystems via shared memory -+and accepts clock requests, aggregates the requests and turns -+the clocks on/off or scales them on demand. -+ -+Required properties : -+- compatible : shall contain only one of the following. The generic -+ compatible "qcom,rpmcc" should be also included. -+ -+ "qcom,rpmcc-msm8916", "qcom,rpmcc" -+ -+- #clock-cells : shall contain 1 -+ -+Example: -+ smd { -+ compatible = "qcom,smd"; -+ -+ rpm { -+ interrupts = <0 168 1>; -+ qcom,ipc = <&apcs 8 0>; -+ qcom,smd-edge = <15>; -+ -+ rpm_requests { -+ compatible = "qcom,rpm-msm8916"; -+ qcom,smd-channels = "rpm_requests"; -+ -+ rpmcc: clock-controller { -+ compatible = "qcom,rpmcc-msm8916", "qcom,rpmcc"; -+ #clock-cells = <1>; -+ }; -+ }; -+ }; -+ }; ---- a/drivers/clk/qcom/Kconfig -+++ b/drivers/clk/qcom/Kconfig -@@ -2,6 +2,9 @@ config QCOM_GDSC - bool - select PM_GENERIC_DOMAINS if PM - -+config QCOM_RPMCC -+ bool -+ - config COMMON_CLK_QCOM - tristate "Support for Qualcomm's clock controllers" - depends on OF -@@ -9,6 +12,19 @@ config COMMON_CLK_QCOM - select REGMAP_MMIO - select RESET_CONTROLLER - -+config QCOM_CLK_SMD_RPM -+ tristate "RPM over SMD based Clock Controller" -+ depends on COMMON_CLK_QCOM && QCOM_SMD_RPM -+ select QCOM_RPMCC -+ help -+ The RPM (Resource Power Manager) is a dedicated hardware engine for -+ managing the shared SoC resources in order to keep the lowest power -+ profile. It communicates with other hardware subsystems via shared -+ memory and accepts clock requests, aggregates the requests and turns -+ the clocks on/off or scales them on demand. -+ Say Y if you want to support the clocks exposed by the RPM on -+ platforms such as apq8016, apq8084, msm8974 etc. -+ - config APQ_GCC_8084 - tristate "APQ8084 Global Clock Controller" - select QCOM_GDSC ---- a/drivers/clk/qcom/Makefile -+++ b/drivers/clk/qcom/Makefile -@@ -28,3 +28,4 @@ obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8 - obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o - obj-$(CONFIG_QCOM_HFPLL) += hfpll.o - obj-$(CONFIG_KRAITCC) += krait-cc.o -+obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o ---- /dev/null -+++ b/drivers/clk/qcom/clk-smd-rpm.c -@@ -0,0 +1,571 @@ -+/* -+ * Copyright (c) 2016, Linaro Limited -+ * Copyright (c) 2014, The Linux Foundation. All rights reserved. -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773 -+#define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370 -+#define QCOM_RPM_SMD_KEY_RATE 0x007a484b -+#define QCOM_RPM_SMD_KEY_ENABLE 0x62616e45 -+#define QCOM_RPM_SMD_KEY_STATE 0x54415453 -+#define QCOM_RPM_SCALING_ENABLE_ID 0x2 -+ -+#define __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, stat_id, \ -+ key) \ -+ static struct clk_smd_rpm _platform##_##_active; \ -+ static struct clk_smd_rpm _platform##_##_name = { \ -+ .rpm_res_type = (type), \ -+ .rpm_clk_id = (r_id), \ -+ .rpm_status_id = (stat_id), \ -+ .rpm_key = (key), \ -+ .peer = &_platform##_##_active, \ -+ .rate = INT_MAX, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_smd_rpm_ops, \ -+ .name = #_name, \ -+ .parent_names = (const char *[]){ "xo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ }; \ -+ static struct clk_smd_rpm _platform##_##_active = { \ -+ .rpm_res_type = (type), \ -+ .rpm_clk_id = (r_id), \ -+ .rpm_status_id = (stat_id), \ -+ .active_only = true, \ -+ .rpm_key = (key), \ -+ .peer = &_platform##_##_name, \ -+ .rate = INT_MAX, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_smd_rpm_ops, \ -+ .name = #_active, \ -+ .parent_names = (const char *[]){ "xo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ } -+ -+#define __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, \ -+ stat_id, r, key) \ -+ static struct clk_smd_rpm _platform##_##_active; \ -+ static struct clk_smd_rpm _platform##_##_name = { \ -+ .rpm_res_type = (type), \ -+ .rpm_clk_id = (r_id), \ -+ .rpm_status_id = (stat_id), \ -+ .rpm_key = (key), \ -+ .branch = true, \ -+ .peer = &_platform##_##_active, \ -+ .rate = (r), \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_smd_rpm_branch_ops, \ -+ .name = #_name, \ -+ .parent_names = (const char *[]){ "xo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ }; \ -+ static struct clk_smd_rpm _platform##_##_active = { \ -+ .rpm_res_type = (type), \ -+ .rpm_clk_id = (r_id), \ -+ .rpm_status_id = (stat_id), \ -+ .active_only = true, \ -+ .rpm_key = (key), \ -+ .branch = true, \ -+ .peer = &_platform##_##_name, \ -+ .rate = (r), \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_smd_rpm_branch_ops, \ -+ .name = #_active, \ -+ .parent_names = (const char *[]){ "xo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ } -+ -+#define DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id) \ -+ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ -+ 0, QCOM_RPM_SMD_KEY_RATE) -+ -+#define DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, r_id, r) \ -+ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, type, \ -+ r_id, 0, r, QCOM_RPM_SMD_KEY_ENABLE) -+ -+#define DEFINE_CLK_SMD_RPM_QDSS(_platform, _name, _active, type, r_id) \ -+ __DEFINE_CLK_SMD_RPM(_platform, _name, _active, type, r_id, \ -+ 0, QCOM_RPM_SMD_KEY_STATE) -+ -+#define DEFINE_CLK_SMD_RPM_XO_BUFFER(_platform, _name, _active, r_id) \ -+ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ -+ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ -+ QCOM_RPM_KEY_SOFTWARE_ENABLE) -+ -+#define DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(_platform, _name, _active, r_id) \ -+ __DEFINE_CLK_SMD_RPM_BRANCH(_platform, _name, _active, \ -+ QCOM_SMD_RPM_CLK_BUF_A, r_id, 0, 1000, \ -+ QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY) -+ -+#define to_clk_smd_rpm(_hw) container_of(_hw, struct clk_smd_rpm, hw) -+ -+struct clk_smd_rpm { -+ const int rpm_res_type; -+ const int rpm_key; -+ const int rpm_clk_id; -+ const int rpm_status_id; -+ const bool active_only; -+ bool enabled; -+ bool branch; -+ struct clk_smd_rpm *peer; -+ struct clk_hw hw; -+ unsigned long rate; -+ struct qcom_smd_rpm *rpm; -+}; -+ -+struct clk_smd_rpm_req { -+ __le32 key; -+ __le32 nbytes; -+ __le32 value; -+}; -+ -+struct rpm_cc { -+ struct qcom_rpm *rpm; -+ struct clk_hw_onecell_data data; -+ struct clk_hw *hws[]; -+}; -+ -+struct rpm_smd_clk_desc { -+ struct clk_smd_rpm **clks; -+ size_t num_clks; -+}; -+ -+static DEFINE_MUTEX(rpm_smd_clk_lock); -+ -+static int clk_smd_rpm_handoff(struct clk_smd_rpm *r) -+{ -+ int ret; -+ struct clk_smd_rpm_req req = { -+ .key = cpu_to_le32(r->rpm_key), -+ .nbytes = cpu_to_le32(sizeof(u32)), -+ .value = cpu_to_le32(INT_MAX), -+ }; -+ -+ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, -+ r->rpm_res_type, r->rpm_clk_id, &req, -+ sizeof(req)); -+ if (ret) -+ return ret; -+ ret = qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, -+ r->rpm_res_type, r->rpm_clk_id, &req, -+ sizeof(req)); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int clk_smd_rpm_set_rate_active(struct clk_smd_rpm *r, -+ unsigned long rate) -+{ -+ struct clk_smd_rpm_req req = { -+ .key = cpu_to_le32(r->rpm_key), -+ .nbytes = cpu_to_le32(sizeof(u32)), -+ .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */ -+ }; -+ -+ return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_ACTIVE_STATE, -+ r->rpm_res_type, r->rpm_clk_id, &req, -+ sizeof(req)); -+} -+ -+static int clk_smd_rpm_set_rate_sleep(struct clk_smd_rpm *r, -+ unsigned long rate) -+{ -+ struct clk_smd_rpm_req req = { -+ .key = cpu_to_le32(r->rpm_key), -+ .nbytes = cpu_to_le32(sizeof(u32)), -+ .value = cpu_to_le32(DIV_ROUND_UP(rate, 1000)), /* to kHz */ -+ }; -+ -+ return qcom_rpm_smd_write(r->rpm, QCOM_SMD_RPM_SLEEP_STATE, -+ r->rpm_res_type, r->rpm_clk_id, &req, -+ sizeof(req)); -+} -+ -+static void to_active_sleep(struct clk_smd_rpm *r, unsigned long rate, -+ unsigned long *active, unsigned long *sleep) -+{ -+ *active = rate; -+ -+ /* -+ * Active-only clocks don't care what the rate is during sleep. So, -+ * they vote for zero. -+ */ -+ if (r->active_only) -+ *sleep = 0; -+ else -+ *sleep = *active; -+} -+ -+static int clk_smd_rpm_prepare(struct clk_hw *hw) -+{ -+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); -+ struct clk_smd_rpm *peer = r->peer; -+ unsigned long this_rate = 0, this_sleep_rate = 0; -+ unsigned long peer_rate = 0, peer_sleep_rate = 0; -+ unsigned long active_rate, sleep_rate; -+ int ret = 0; -+ -+ mutex_lock(&rpm_smd_clk_lock); -+ -+ /* Don't send requests to the RPM if the rate has not been set. */ -+ if (!r->rate) -+ goto out; -+ -+ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); -+ -+ /* Take peer clock's rate into account only if it's enabled. */ -+ if (peer->enabled) -+ to_active_sleep(peer, peer->rate, -+ &peer_rate, &peer_sleep_rate); -+ -+ active_rate = max(this_rate, peer_rate); -+ -+ if (r->branch) -+ active_rate = !!active_rate; -+ -+ ret = clk_smd_rpm_set_rate_active(r, active_rate); -+ if (ret) -+ goto out; -+ -+ sleep_rate = max(this_sleep_rate, peer_sleep_rate); -+ if (r->branch) -+ sleep_rate = !!sleep_rate; -+ -+ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); -+ if (ret) -+ /* Undo the active set vote and restore it */ -+ ret = clk_smd_rpm_set_rate_active(r, peer_rate); -+ -+out: -+ if (!ret) -+ r->enabled = true; -+ -+ mutex_unlock(&rpm_smd_clk_lock); -+ -+ return ret; -+} -+ -+static void clk_smd_rpm_unprepare(struct clk_hw *hw) -+{ -+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); -+ struct clk_smd_rpm *peer = r->peer; -+ unsigned long peer_rate = 0, peer_sleep_rate = 0; -+ unsigned long active_rate, sleep_rate; -+ int ret; -+ -+ mutex_lock(&rpm_smd_clk_lock); -+ -+ if (!r->rate) -+ goto out; -+ -+ /* Take peer clock's rate into account only if it's enabled. */ -+ if (peer->enabled) -+ to_active_sleep(peer, peer->rate, &peer_rate, -+ &peer_sleep_rate); -+ -+ active_rate = r->branch ? !!peer_rate : peer_rate; -+ ret = clk_smd_rpm_set_rate_active(r, active_rate); -+ if (ret) -+ goto out; -+ -+ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; -+ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); -+ if (ret) -+ goto out; -+ -+ r->enabled = false; -+ -+out: -+ mutex_unlock(&rpm_smd_clk_lock); -+} -+ -+static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long parent_rate) -+{ -+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); -+ struct clk_smd_rpm *peer = r->peer; -+ unsigned long active_rate, sleep_rate; -+ unsigned long this_rate = 0, this_sleep_rate = 0; -+ unsigned long peer_rate = 0, peer_sleep_rate = 0; -+ int ret = 0; -+ -+ mutex_lock(&rpm_smd_clk_lock); -+ -+ if (!r->enabled) -+ goto out; -+ -+ to_active_sleep(r, rate, &this_rate, &this_sleep_rate); -+ -+ /* Take peer clock's rate into account only if it's enabled. */ -+ if (peer->enabled) -+ to_active_sleep(peer, peer->rate, -+ &peer_rate, &peer_sleep_rate); -+ -+ active_rate = max(this_rate, peer_rate); -+ ret = clk_smd_rpm_set_rate_active(r, active_rate); -+ if (ret) -+ goto out; -+ -+ sleep_rate = max(this_sleep_rate, peer_sleep_rate); -+ ret = clk_smd_rpm_set_rate_sleep(r, sleep_rate); -+ if (ret) -+ goto out; -+ -+ r->rate = rate; -+ -+out: -+ mutex_unlock(&rpm_smd_clk_lock); -+ -+ return ret; -+} -+ -+static long clk_smd_rpm_round_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long *parent_rate) -+{ -+ /* -+ * RPM handles rate rounding and we don't have a way to -+ * know what the rate will be, so just return whatever -+ * rate is requested. -+ */ -+ return rate; -+} -+ -+static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw, -+ unsigned long parent_rate) -+{ -+ struct clk_smd_rpm *r = to_clk_smd_rpm(hw); -+ -+ /* -+ * RPM handles rate rounding and we don't have a way to -+ * know what the rate will be, so just return whatever -+ * rate was set. -+ */ -+ return r->rate; -+} -+ -+static int clk_smd_rpm_enable_scaling(struct qcom_smd_rpm *rpm) -+{ -+ int ret; -+ struct clk_smd_rpm_req req = { -+ .key = cpu_to_le32(QCOM_RPM_SMD_KEY_ENABLE), -+ .nbytes = cpu_to_le32(sizeof(u32)), -+ .value = cpu_to_le32(1), -+ }; -+ -+ ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_SLEEP_STATE, -+ QCOM_SMD_RPM_MISC_CLK, -+ QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req)); -+ if (ret) { -+ pr_err("RPM clock scaling (sleep set) not enabled!\n"); -+ return ret; -+ } -+ -+ ret = qcom_rpm_smd_write(rpm, QCOM_SMD_RPM_ACTIVE_STATE, -+ QCOM_SMD_RPM_MISC_CLK, -+ QCOM_RPM_SCALING_ENABLE_ID, &req, sizeof(req)); -+ if (ret) { -+ pr_err("RPM clock scaling (active set) not enabled!\n"); -+ return ret; -+ } -+ -+ pr_debug("%s: RPM clock scaling is enabled\n", __func__); -+ return 0; -+} -+ -+static const struct clk_ops clk_smd_rpm_ops = { -+ .prepare = clk_smd_rpm_prepare, -+ .unprepare = clk_smd_rpm_unprepare, -+ .set_rate = clk_smd_rpm_set_rate, -+ .round_rate = clk_smd_rpm_round_rate, -+ .recalc_rate = clk_smd_rpm_recalc_rate, -+}; -+ -+static const struct clk_ops clk_smd_rpm_branch_ops = { -+ .prepare = clk_smd_rpm_prepare, -+ .unprepare = clk_smd_rpm_unprepare, -+ .round_rate = clk_smd_rpm_round_rate, -+ .recalc_rate = clk_smd_rpm_recalc_rate, -+}; -+ -+/* msm8916 */ -+DEFINE_CLK_SMD_RPM(msm8916, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); -+DEFINE_CLK_SMD_RPM(msm8916, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); -+DEFINE_CLK_SMD_RPM(msm8916, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); -+DEFINE_CLK_SMD_RPM_QDSS(msm8916, qdss_clk, qdss_a_clk, QCOM_SMD_RPM_MISC_CLK, 1); -+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk1, bb_clk1_a, 1); -+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, bb_clk2, bb_clk2_a, 2); -+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk1, rf_clk1_a, 4); -+DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8916, rf_clk2, rf_clk2_a, 5); -+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk1_pin, bb_clk1_a_pin, 1); -+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, bb_clk2_pin, bb_clk2_a_pin, 2); -+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk1_pin, rf_clk1_a_pin, 4); -+DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8916, rf_clk2_pin, rf_clk2_a_pin, 5); -+ -+static struct clk_smd_rpm *msm8916_clks[] = { -+ [RPM_SMD_PCNOC_CLK] = &msm8916_pcnoc_clk, -+ [RPM_SMD_PCNOC_A_CLK] = &msm8916_pcnoc_a_clk, -+ [RPM_SMD_SNOC_CLK] = &msm8916_snoc_clk, -+ [RPM_SMD_SNOC_A_CLK] = &msm8916_snoc_a_clk, -+ [RPM_SMD_BIMC_CLK] = &msm8916_bimc_clk, -+ [RPM_SMD_BIMC_A_CLK] = &msm8916_bimc_a_clk, -+ [RPM_SMD_QDSS_CLK] = &msm8916_qdss_clk, -+ [RPM_SMD_QDSS_A_CLK] = &msm8916_qdss_a_clk, -+ [RPM_SMD_BB_CLK1] = &msm8916_bb_clk1, -+ [RPM_SMD_BB_CLK1_A] = &msm8916_bb_clk1_a, -+ [RPM_SMD_BB_CLK2] = &msm8916_bb_clk2, -+ [RPM_SMD_BB_CLK2_A] = &msm8916_bb_clk2_a, -+ [RPM_SMD_RF_CLK1] = &msm8916_rf_clk1, -+ [RPM_SMD_RF_CLK1_A] = &msm8916_rf_clk1_a, -+ [RPM_SMD_RF_CLK2] = &msm8916_rf_clk2, -+ [RPM_SMD_RF_CLK2_A] = &msm8916_rf_clk2_a, -+ [RPM_SMD_BB_CLK1_PIN] = &msm8916_bb_clk1_pin, -+ [RPM_SMD_BB_CLK1_A_PIN] = &msm8916_bb_clk1_a_pin, -+ [RPM_SMD_BB_CLK2_PIN] = &msm8916_bb_clk2_pin, -+ [RPM_SMD_BB_CLK2_A_PIN] = &msm8916_bb_clk2_a_pin, -+ [RPM_SMD_RF_CLK1_PIN] = &msm8916_rf_clk1_pin, -+ [RPM_SMD_RF_CLK1_A_PIN] = &msm8916_rf_clk1_a_pin, -+ [RPM_SMD_RF_CLK2_PIN] = &msm8916_rf_clk2_pin, -+ [RPM_SMD_RF_CLK2_A_PIN] = &msm8916_rf_clk2_a_pin, -+}; -+ -+static const struct rpm_smd_clk_desc rpm_clk_msm8916 = { -+ .clks = msm8916_clks, -+ .num_clks = ARRAY_SIZE(msm8916_clks), -+}; -+ -+static const struct of_device_id rpm_smd_clk_match_table[] = { -+ { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); -+ -+static int rpm_smd_clk_probe(struct platform_device *pdev) -+{ -+ struct clk_hw **hws; -+ struct rpm_cc *rcc; -+ struct clk_hw_onecell_data *data; -+ int ret; -+ size_t num_clks, i; -+ struct qcom_smd_rpm *rpm; -+ struct clk_smd_rpm **rpm_smd_clks; -+ const struct rpm_smd_clk_desc *desc; -+ -+ rpm = dev_get_drvdata(pdev->dev.parent); -+ if (!rpm) { -+ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); -+ return -ENODEV; -+ } -+ -+ desc = of_device_get_match_data(&pdev->dev); -+ if (!desc) -+ return -EINVAL; -+ -+ rpm_smd_clks = desc->clks; -+ num_clks = desc->num_clks; -+ -+ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, -+ GFP_KERNEL); -+ if (!rcc) -+ return -ENOMEM; -+ -+ hws = rcc->hws; -+ data = &rcc->data; -+ data->num = num_clks; -+ -+ for (i = 0; i < num_clks; i++) { -+ if (!rpm_smd_clks[i]) { -+ continue; -+ } -+ -+ rpm_smd_clks[i]->rpm = rpm; -+ -+ ret = clk_smd_rpm_handoff(rpm_smd_clks[i]); -+ if (ret) -+ goto err; -+ } -+ -+ ret = clk_smd_rpm_enable_scaling(rpm); -+ if (ret) -+ goto err; -+ -+ for (i = 0; i < num_clks; i++) { -+ if (!rpm_smd_clks[i]) { -+ data->hws[i] = ERR_PTR(-ENOENT); -+ continue; -+ } -+ -+ ret = devm_clk_hw_register(&pdev->dev, &rpm_smd_clks[i]->hw); -+ if (ret) -+ goto err; -+ } -+ -+ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, -+ data); -+ if (ret) -+ goto err; -+ -+ return 0; -+err: -+ dev_err(&pdev->dev, "Error registering SMD clock driver (%d)\n", ret); -+ return ret; -+} -+ -+static int rpm_smd_clk_remove(struct platform_device *pdev) -+{ -+ of_clk_del_provider(pdev->dev.of_node); -+ return 0; -+} -+ -+static struct platform_driver rpm_smd_clk_driver = { -+ .driver = { -+ .name = "qcom-clk-smd-rpm", -+ .of_match_table = rpm_smd_clk_match_table, -+ }, -+ .probe = rpm_smd_clk_probe, -+ .remove = rpm_smd_clk_remove, -+}; -+ -+static int __init rpm_smd_clk_init(void) -+{ -+ return platform_driver_register(&rpm_smd_clk_driver); -+} -+core_initcall(rpm_smd_clk_init); -+ -+static void __exit rpm_smd_clk_exit(void) -+{ -+ platform_driver_unregister(&rpm_smd_clk_driver); -+} -+module_exit(rpm_smd_clk_exit); -+ -+MODULE_DESCRIPTION("Qualcomm RPM over SMD Clock Controller Driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:qcom-clk-smd-rpm"); ---- /dev/null -+++ b/include/dt-bindings/clock/qcom,rpmcc.h -@@ -0,0 +1,45 @@ -+/* -+ * Copyright 2015 Linaro Limited -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * 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. -+ */ -+ -+#ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H -+#define _DT_BINDINGS_CLK_MSM_RPMCC_H -+ -+/* msm8916 */ -+#define RPM_SMD_XO_CLK_SRC 0 -+#define RPM_SMD_XO_A_CLK_SRC 1 -+#define RPM_SMD_PCNOC_CLK 2 -+#define RPM_SMD_PCNOC_A_CLK 3 -+#define RPM_SMD_SNOC_CLK 4 -+#define RPM_SMD_SNOC_A_CLK 5 -+#define RPM_SMD_BIMC_CLK 6 -+#define RPM_SMD_BIMC_A_CLK 7 -+#define RPM_SMD_QDSS_CLK 8 -+#define RPM_SMD_QDSS_A_CLK 9 -+#define RPM_SMD_BB_CLK1 10 -+#define RPM_SMD_BB_CLK1_A 11 -+#define RPM_SMD_BB_CLK2 12 -+#define RPM_SMD_BB_CLK2_A 13 -+#define RPM_SMD_RF_CLK1 14 -+#define RPM_SMD_RF_CLK1_A 15 -+#define RPM_SMD_RF_CLK2 16 -+#define RPM_SMD_RF_CLK2_A 17 -+#define RPM_SMD_BB_CLK1_PIN 18 -+#define RPM_SMD_BB_CLK1_A_PIN 19 -+#define RPM_SMD_BB_CLK2_PIN 20 -+#define RPM_SMD_BB_CLK2_A_PIN 21 -+#define RPM_SMD_RF_CLK1_PIN 22 -+#define RPM_SMD_RF_CLK1_A_PIN 23 -+#define RPM_SMD_RF_CLK2_PIN 24 -+#define RPM_SMD_RF_CLK2_A_PIN 25 -+ -+#endif diff --git a/target/linux/ipq806x/patches-4.4/179-clk-qcom-Add-support-for-RPM-Clocks.patch b/target/linux/ipq806x/patches-4.4/179-clk-qcom-Add-support-for-RPM-Clocks.patch deleted file mode 100644 index 27e20be9f4..0000000000 --- a/target/linux/ipq806x/patches-4.4/179-clk-qcom-Add-support-for-RPM-Clocks.patch +++ /dev/null @@ -1,598 +0,0 @@ -!This is the adjusted version of patch that has been submitted to upstream and that, unfortunately, provides support for RPM clocks only for apq8064 board. - -!TODO: make a patch that adds support for ipq806x along with apq8064 and not replaces it. - -From patchwork Wed Nov 2 15:56:57 2016 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v9,2/3] clk: qcom: Add support for RPM Clocks -From: Georgi Djakov -X-Patchwork-Id: 9409425 -Message-Id: <20161102155658.32203-3-georgi.djakov@linaro.org> -To: sboyd@codeaurora.org, mturquette@baylibre.com -Cc: linux-clk@vger.kernel.org, devicetree@vger.kernel.org, - robh+dt@kernel.org, mark.rutland@arm.com, - linux-kernel@vger.kernel.org, linux-arm-msm@vger.kernel.org, - georgi.djakov@linaro.org -Date: Wed, 2 Nov 2016 17:56:57 +0200 - -This adds initial support for clocks controlled by the Resource -Power Manager (RPM) processor on some Qualcomm SoCs, which use -the qcom_rpm driver to communicate with RPM. -Such platforms are ipq806x and msm8960. - -Signed-off-by: Georgi Djakov ---- - .../devicetree/bindings/clock/qcom,rpmcc.txt | 1 + - drivers/clk/qcom/Kconfig | 13 + - drivers/clk/qcom/Makefile | 1 + - drivers/clk/qcom/clk-rpm.c | 489 +++++++++++++++++++++ - include/dt-bindings/clock/qcom,rpmcc.h | 24 + - 5 files changed, 528 insertions(+) - create mode 100644 drivers/clk/qcom/clk-rpm.c - --- -To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in -the body of a message to majordomo@vger.kernel.org -More majordomo info at http://vger.kernel.org/majordomo-info.html - ---- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt -+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt -@@ -11,6 +11,7 @@ Required properties : - compatible "qcom,rpmcc" should be also included. - - "qcom,rpmcc-msm8916", "qcom,rpmcc" -+ "qcom,rpmcc-ipq806x", "qcom,rpmcc" - - - #clock-cells : shall contain 1 - ---- a/drivers/clk/qcom/Kconfig -+++ b/drivers/clk/qcom/Kconfig -@@ -12,6 +12,19 @@ config COMMON_CLK_QCOM - select REGMAP_MMIO - select RESET_CONTROLLER - -+config QCOM_CLK_RPM -+ tristate "RPM based Clock Controller" -+ depends on COMMON_CLK_QCOM && MFD_QCOM_RPM -+ select QCOM_RPMCC -+ help -+ The RPM (Resource Power Manager) is a dedicated hardware engine for -+ managing the shared SoC resources in order to keep the lowest power -+ profile. It communicates with other hardware subsystems via shared -+ memory and accepts clock requests, aggregates the requests and turns -+ the clocks on/off or scales them on demand. -+ Say Y if you want to support the clocks exposed by the RPM on -+ platforms such as ipq806x, msm8660, msm8960 etc. -+ - config QCOM_CLK_SMD_RPM - tristate "RPM over SMD based Clock Controller" - depends on COMMON_CLK_QCOM && QCOM_SMD_RPM ---- a/drivers/clk/qcom/Makefile -+++ b/drivers/clk/qcom/Makefile -@@ -29,3 +29,4 @@ obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8 - obj-$(CONFIG_QCOM_HFPLL) += hfpll.o - obj-$(CONFIG_KRAITCC) += krait-cc.o - obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o -+obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o ---- /dev/null -+++ b/drivers/clk/qcom/clk-rpm.c -@@ -0,0 +1,486 @@ -+/* -+ * Copyright (c) 2016, Linaro Limited -+ * Copyright (c) 2014, The Linux Foundation. All rights reserved. -+ * -+ * This software is licensed under the terms of the GNU General Public -+ * License version 2, as published by the Free Software Foundation, and -+ * may be copied, distributed, and modified under those terms. -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63 -+#define QCOM_RPM_SCALING_ENABLE_ID 0x2 -+ -+#define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \ -+ static struct clk_rpm _platform##_##_active; \ -+ static struct clk_rpm _platform##_##_name = { \ -+ .rpm_clk_id = (r_id), \ -+ .peer = &_platform##_##_active, \ -+ .rate = INT_MAX, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_rpm_ops, \ -+ .name = #_name, \ -+ .parent_names = (const char *[]){ "pxo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ }; \ -+ static struct clk_rpm _platform##_##_active = { \ -+ .rpm_clk_id = (r_id), \ -+ .peer = &_platform##_##_name, \ -+ .active_only = true, \ -+ .rate = INT_MAX, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_rpm_ops, \ -+ .name = #_active, \ -+ .parent_names = (const char *[]){ "pxo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ } -+ -+#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \ -+ static struct clk_rpm _platform##_##_active; \ -+ static struct clk_rpm _platform##_##_name = { \ -+ .rpm_clk_id = (r_id), \ -+ .active_only = true, \ -+ .peer = &_platform##_##_active, \ -+ .rate = (r), \ -+ .branch = true, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_rpm_branch_ops, \ -+ .name = #_name, \ -+ .parent_names = (const char *[]){ "pxo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ }; \ -+ static struct clk_rpm _platform##_##_active = { \ -+ .rpm_clk_id = (r_id), \ -+ .peer = &_platform##_##_name, \ -+ .rate = (r), \ -+ .branch = true, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_rpm_branch_ops, \ -+ .name = #_active, \ -+ .parent_names = (const char *[]){ "pxo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ } -+ -+#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \ -+ static struct clk_rpm _platform##_##_active; \ -+ static struct clk_rpm _platform##_##_name = { \ -+ .rpm_clk_id = (r_id), \ -+ .peer = &_platform##_##_active, \ -+ .rate = (r), \ -+ .branch = true, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_rpm_branch_ops, \ -+ .name = #_name, \ -+ .parent_names = (const char *[]){ "cxo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ }; \ -+ static struct clk_rpm _platform##_##_active = { \ -+ .rpm_clk_id = (r_id), \ -+ .active_only = true, \ -+ .peer = &_platform##_##_name, \ -+ .rate = (r), \ -+ .branch = true, \ -+ .hw.init = &(struct clk_init_data){ \ -+ .ops = &clk_rpm_branch_ops, \ -+ .name = #_active, \ -+ .parent_names = (const char *[]){ "cxo_board" }, \ -+ .num_parents = 1, \ -+ }, \ -+ } -+ -+#define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw) -+ -+struct clk_rpm { -+ const int rpm_clk_id; -+ const bool active_only; -+ unsigned long rate; -+ bool enabled; -+ bool branch; -+ struct clk_rpm *peer; -+ struct clk_hw hw; -+ struct qcom_rpm *rpm; -+}; -+ -+struct rpm_cc { -+ struct qcom_rpm *rpm; -+ struct clk_hw_onecell_data data; -+ struct clk_hw *hws[]; -+}; -+ -+struct rpm_clk_desc { -+ struct clk_rpm **clks; -+ size_t num_clks; -+}; -+ -+static DEFINE_MUTEX(rpm_clk_lock); -+ -+static int clk_rpm_handoff(struct clk_rpm *r) -+{ -+ int ret; -+ u32 value = INT_MAX; -+ -+ ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, -+ r->rpm_clk_id, &value, 1); -+ if (ret) -+ return ret; -+ ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, -+ r->rpm_clk_id, &value, 1); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate) -+{ -+ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ -+ -+ return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, -+ r->rpm_clk_id, &value, 1); -+} -+ -+static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate) -+{ -+ u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ -+ -+ return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, -+ r->rpm_clk_id, &value, 1); -+} -+ -+static void to_active_sleep(struct clk_rpm *r, unsigned long rate, -+ unsigned long *active, unsigned long *sleep) -+{ -+ *active = rate; -+ -+ /* -+ * Active-only clocks don't care what the rate is during sleep. So, -+ * they vote for zero. -+ */ -+ if (r->active_only) -+ *sleep = 0; -+ else -+ *sleep = *active; -+} -+ -+static int clk_rpm_prepare(struct clk_hw *hw) -+{ -+ struct clk_rpm *r = to_clk_rpm(hw); -+ struct clk_rpm *peer = r->peer; -+ unsigned long this_rate = 0, this_sleep_rate = 0; -+ unsigned long peer_rate = 0, peer_sleep_rate = 0; -+ unsigned long active_rate, sleep_rate; -+ int ret = 0; -+ -+ mutex_lock(&rpm_clk_lock); -+ -+ /* Don't send requests to the RPM if the rate has not been set. */ -+ if (!r->rate) -+ goto out; -+ -+ to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); -+ -+ /* Take peer clock's rate into account only if it's enabled. */ -+ if (peer->enabled) -+ to_active_sleep(peer, peer->rate, -+ &peer_rate, &peer_sleep_rate); -+ -+ active_rate = max(this_rate, peer_rate); -+ -+ if (r->branch) -+ active_rate = !!active_rate; -+ -+ ret = clk_rpm_set_rate_active(r, active_rate); -+ if (ret) -+ goto out; -+ -+ sleep_rate = max(this_sleep_rate, peer_sleep_rate); -+ if (r->branch) -+ sleep_rate = !!sleep_rate; -+ -+ ret = clk_rpm_set_rate_sleep(r, sleep_rate); -+ if (ret) -+ /* Undo the active set vote and restore it */ -+ ret = clk_rpm_set_rate_active(r, peer_rate); -+ -+out: -+ if (!ret) -+ r->enabled = true; -+ -+ mutex_unlock(&rpm_clk_lock); -+ -+ return ret; -+} -+ -+static void clk_rpm_unprepare(struct clk_hw *hw) -+{ -+ struct clk_rpm *r = to_clk_rpm(hw); -+ struct clk_rpm *peer = r->peer; -+ unsigned long peer_rate = 0, peer_sleep_rate = 0; -+ unsigned long active_rate, sleep_rate; -+ int ret; -+ -+ mutex_lock(&rpm_clk_lock); -+ -+ if (!r->rate) -+ goto out; -+ -+ /* Take peer clock's rate into account only if it's enabled. */ -+ if (peer->enabled) -+ to_active_sleep(peer, peer->rate, &peer_rate, -+ &peer_sleep_rate); -+ -+ active_rate = r->branch ? !!peer_rate : peer_rate; -+ ret = clk_rpm_set_rate_active(r, active_rate); -+ if (ret) -+ goto out; -+ -+ sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; -+ ret = clk_rpm_set_rate_sleep(r, sleep_rate); -+ if (ret) -+ goto out; -+ -+ r->enabled = false; -+ -+out: -+ mutex_unlock(&rpm_clk_lock); -+} -+ -+static int clk_rpm_set_rate(struct clk_hw *hw, -+ unsigned long rate, unsigned long parent_rate) -+{ -+ struct clk_rpm *r = to_clk_rpm(hw); -+ struct clk_rpm *peer = r->peer; -+ unsigned long active_rate, sleep_rate; -+ unsigned long this_rate = 0, this_sleep_rate = 0; -+ unsigned long peer_rate = 0, peer_sleep_rate = 0; -+ int ret = 0; -+ -+ mutex_lock(&rpm_clk_lock); -+ -+ if (!r->enabled) -+ goto out; -+ -+ to_active_sleep(r, rate, &this_rate, &this_sleep_rate); -+ -+ /* Take peer clock's rate into account only if it's enabled. */ -+ if (peer->enabled) -+ to_active_sleep(peer, peer->rate, -+ &peer_rate, &peer_sleep_rate); -+ -+ active_rate = max(this_rate, peer_rate); -+ ret = clk_rpm_set_rate_active(r, active_rate); -+ if (ret) -+ goto out; -+ -+ sleep_rate = max(this_sleep_rate, peer_sleep_rate); -+ ret = clk_rpm_set_rate_sleep(r, sleep_rate); -+ if (ret) -+ goto out; -+ -+ r->rate = rate; -+ -+out: -+ mutex_unlock(&rpm_clk_lock); -+ -+ return ret; -+} -+ -+static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate, -+ unsigned long *parent_rate) -+{ -+ /* -+ * RPM handles rate rounding and we don't have a way to -+ * know what the rate will be, so just return whatever -+ * rate is requested. -+ */ -+ return rate; -+} -+ -+static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, -+ unsigned long parent_rate) -+{ -+ struct clk_rpm *r = to_clk_rpm(hw); -+ -+ /* -+ * RPM handles rate rounding and we don't have a way to -+ * know what the rate will be, so just return whatever -+ * rate was set. -+ */ -+ return r->rate; -+} -+ -+static const struct clk_ops clk_rpm_ops = { -+ .prepare = clk_rpm_prepare, -+ .unprepare = clk_rpm_unprepare, -+ .set_rate = clk_rpm_set_rate, -+ .round_rate = clk_rpm_round_rate, -+ .recalc_rate = clk_rpm_recalc_rate, -+}; -+ -+static const struct clk_ops clk_rpm_branch_ops = { -+ .prepare = clk_rpm_prepare, -+ .unprepare = clk_rpm_unprepare, -+ .round_rate = clk_rpm_round_rate, -+ .recalc_rate = clk_rpm_recalc_rate, -+}; -+ -+/* ipq806x */ -+DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); -+DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); -+DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); -+DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); -+DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK); -+DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK); -+DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); -+DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); -+ -+static struct clk_rpm *ipq806x_clks[] = { -+ [RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk, -+ [RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk, -+ [RPM_CFPB_CLK] = &ipq806x_cfpb_clk, -+ [RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk, -+ [RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk, -+ [RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk, -+ [RPM_EBI1_CLK] = &ipq806x_ebi1_clk, -+ [RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk, -+ [RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk, -+ [RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk, -+ [RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk, -+ [RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk, -+ [RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk, -+ [RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk, -+ [RPM_SFPB_CLK] = &ipq806x_sfpb_clk, -+ [RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk, -+}; -+ -+static const struct rpm_clk_desc rpm_clk_ipq806x = { -+ .clks = ipq806x_clks, -+ .num_clks = ARRAY_SIZE(ipq806x_clks), -+}; -+ -+static const struct of_device_id rpm_clk_match_table[] = { -+ { .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, rpm_clk_match_table); -+ -+static int rpm_clk_probe(struct platform_device *pdev) -+{ -+ struct clk_hw **hws; -+ struct rpm_cc *rcc; -+ struct clk_hw_onecell_data *data; -+ int ret; -+ size_t num_clks, i; -+ struct qcom_rpm *rpm; -+ struct clk_rpm **rpm_clks; -+ const struct rpm_clk_desc *desc; -+ -+ rpm = dev_get_drvdata(pdev->dev.parent); -+ if (!rpm) { -+ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); -+ return -ENODEV; -+ } -+ -+ desc = of_device_get_match_data(&pdev->dev); -+ if (!desc) -+ return -EINVAL; -+ -+ rpm_clks = desc->clks; -+ num_clks = desc->num_clks; -+ -+ rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc) + sizeof(*hws) * num_clks, -+ GFP_KERNEL); -+ if (!rcc) -+ return -ENOMEM; -+ -+ hws = rcc->hws; -+ data = &rcc->data; -+ data->num = num_clks; -+ -+ for (i = 0; i < num_clks; i++) { -+ if (!rpm_clks[i]) -+ continue; -+ -+ rpm_clks[i]->rpm = rpm; -+ -+ ret = clk_rpm_handoff(rpm_clks[i]); -+ if (ret) -+ goto err; -+ } -+ -+ for (i = 0; i < num_clks; i++) { -+ if (!rpm_clks[i]) { -+ data->hws[i] = ERR_PTR(-ENOENT); -+ continue; -+ } -+ -+ ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); -+ if (ret) -+ goto err; -+ } -+ -+ ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, -+ data); -+ if (ret) -+ goto err; -+ -+ return 0; -+err: -+ dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret); -+ return ret; -+} -+ -+static int rpm_clk_remove(struct platform_device *pdev) -+{ -+ of_clk_del_provider(pdev->dev.of_node); -+ return 0; -+} -+ -+static struct platform_driver rpm_clk_driver = { -+ .driver = { -+ .name = "qcom-clk-rpm", -+ .of_match_table = rpm_clk_match_table, -+ }, -+ .probe = rpm_clk_probe, -+ .remove = rpm_clk_remove, -+}; -+ -+static int __init rpm_clk_init(void) -+{ -+ return platform_driver_register(&rpm_clk_driver); -+} -+core_initcall(rpm_clk_init); -+ -+static void __exit rpm_clk_exit(void) -+{ -+ platform_driver_unregister(&rpm_clk_driver); -+} -+module_exit(rpm_clk_exit); -+ -+MODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:qcom-clk-rpm"); ---- a/include/dt-bindings/clock/qcom,rpmcc.h -+++ b/include/dt-bindings/clock/qcom,rpmcc.h -@@ -14,6 +14,28 @@ - #ifndef _DT_BINDINGS_CLK_MSM_RPMCC_H - #define _DT_BINDINGS_CLK_MSM_RPMCC_H - -+/* ipq806x */ -+#define RPM_PXO_CLK 0 -+#define RPM_PXO_A_CLK 1 -+#define RPM_CXO_CLK 2 -+#define RPM_CXO_A_CLK 3 -+#define RPM_APPS_FABRIC_CLK 4 -+#define RPM_APPS_FABRIC_A_CLK 5 -+#define RPM_CFPB_CLK 6 -+#define RPM_CFPB_A_CLK 7 -+#define RPM_DAYTONA_FABRIC_CLK 8 -+#define RPM_DAYTONA_FABRIC_A_CLK 9 -+#define RPM_EBI1_CLK 10 -+#define RPM_EBI1_A_CLK 11 -+#define RPM_NSS_FABRIC_0_CLK 12 -+#define RPM_NSS_FABRIC_0_A_CLK 13 -+#define RPM_NSS_FABRIC_1_CLK 14 -+#define RPM_NSS_FABRIC_1_A_CLK 15 -+#define RPM_SYS_FABRIC_CLK 16 -+#define RPM_SYS_FABRIC_A_CLK 17 -+#define RPM_SFPB_CLK 18 -+#define RPM_SFPB_A_CLK 19 -+ - /* msm8916 */ - #define RPM_SMD_XO_CLK_SRC 0 - #define RPM_SMD_XO_A_CLK_SRC 1 diff --git a/target/linux/ipq806x/patches-4.4/311-add-rpmcc-for-ipq806x.patch b/target/linux/ipq806x/patches-4.4/311-add-rpmcc-for-ipq806x.patch new file mode 100644 index 0000000000..bb9dd87dd6 --- /dev/null +++ b/target/linux/ipq806x/patches-4.4/311-add-rpmcc-for-ipq806x.patch @@ -0,0 +1,81 @@ +--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt ++++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +@@ -12,6 +12,7 @@ Required properties : + + "qcom,rpmcc-msm8916", "qcom,rpmcc" + "qcom,rpmcc-apq8064", "qcom,rpmcc" ++ "qcom,rpmcc-ipq806x", "qcom,rpmcc" + + - #clock-cells : shall contain 1 + +--- a/drivers/clk/qcom/clk-rpm.c ++++ b/drivers/clk/qcom/clk-rpm.c +@@ -359,6 +359,16 @@ DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a + DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); + DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); + ++/* ipq806x */ ++DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); ++DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); ++DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); ++DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); ++DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); ++DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); ++DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK); ++DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK); ++ + static struct clk_rpm *apq8064_clks[] = { + [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, + [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, +@@ -380,13 +390,38 @@ static struct clk_rpm *apq8064_clks[] = + [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, + }; + ++static struct clk_rpm *ipq806x_clks[] = { ++ [RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk, ++ [RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk, ++ [RPM_CFPB_CLK] = &ipq806x_cfpb_clk, ++ [RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk, ++ [RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk, ++ [RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk, ++ [RPM_EBI1_CLK] = &ipq806x_ebi1_clk, ++ [RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk, ++ [RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk, ++ [RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk, ++ [RPM_SFPB_CLK] = &ipq806x_sfpb_clk, ++ [RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk, ++ [RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk, ++ [RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk, ++ [RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk, ++ [RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk, ++}; ++ + static const struct rpm_clk_desc rpm_clk_apq8064 = { + .clks = apq8064_clks, + .num_clks = ARRAY_SIZE(apq8064_clks), + }; + ++static const struct rpm_clk_desc rpm_clk_ipq806x = { ++ .clks = ipq806x_clks, ++ .num_clks = ARRAY_SIZE(ipq806x_clks), ++}; ++ + static const struct of_device_id rpm_clk_match_table[] = { + { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, ++ { .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x }, + { } + }; + MODULE_DEVICE_TABLE(of, rpm_clk_match_table); +--- a/include/dt-bindings/clock/qcom,rpmcc.h ++++ b/include/dt-bindings/clock/qcom,rpmcc.h +@@ -37,6 +37,10 @@ + #define RPM_SYS_FABRIC_A_CLK 19 + #define RPM_SFPB_CLK 20 + #define RPM_SFPB_A_CLK 21 ++#define RPM_NSS_FABRIC_0_CLK 22 ++#define RPM_NSS_FABRIC_0_A_CLK 23 ++#define RPM_NSS_FABRIC_1_CLK 24 ++#define RPM_NSS_FABRIC_1_A_CLK 25 + + /* msm8916 */ + #define RPM_SMD_XO_CLK_SRC 0