Loading drivers/clk/qcom/clk-rpmh.c +144 −1 Original line number Diff line number Diff line /* * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -39,6 +39,31 @@ BIT(RPMH_ACTIVE_ONLY_STATE) | \ BIT(RPMH_SLEEP_STATE)) #define BCM_TCS_CMD_COMMIT_MASK 0x40000000 #define BCM_TCS_CMD_VALID_SHIFT 29 #define BCM_TCS_CMD_VOTE_MASK 0x3fff #define BCM_TCS_CMD_VOTE_SHIFT 0 #define BCM_TCS_CMD(valid, vote) \ (BCM_TCS_CMD_COMMIT_MASK | \ ((valid) << BCM_TCS_CMD_VALID_SHIFT) | \ ((vote & BCM_TCS_CMD_VOTE_MASK) \ << BCM_TCS_CMD_VOTE_SHIFT)) /** * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager(BCM) * @unit: divisor used to convert Hz value to an RPMh msg * @width: multiplier used to convert Hz value to an RPMh msg * @vcd: virtual clock domain that this bcm belongs to * @reserved: reserved to pad the struct */ struct bcm_db { __le32 unit; __le16 width; u8 vcd; u8 reserved; }; struct clk_rpmh { const char *res_name; u32 res_addr; Loading @@ -49,6 +74,7 @@ struct clk_rpmh { u32 aggr_state; u32 last_sent_aggr_state; u32 valid_state_mask; u32 unit; struct rsc_type *rsc; unsigned long rate; struct clk_rpmh *peer; Loading Loading @@ -120,6 +146,17 @@ static DEFINE_MUTEX(rpmh_clk_lock); CLK_RPMH_VRM_OFF_VAL, _rsc_id, _rate, _state_mask, \ _state_on_mask) #define DEFINE_CLK_RPMH_BCM(_platform, _name, _res_name, _rsc_id) \ static struct clk_rpmh _platform##_##_name = { \ .res_name = _res_name, \ .valid_state_mask = BIT(RPMH_ACTIVE_ONLY_STATE), \ .rsc = _rsc_id, \ .hw.init = &(struct clk_init_data){ \ .ops = &clk_rpmh_bcm_ops, \ .name = #_name, \ }, \ } #define DEFINE_RSC_TYPE(name, mbox_id, awake_state) \ static struct rsc_type name = { \ .rpmh_client = NULL, \ Loading Loading @@ -271,6 +308,99 @@ static const struct clk_ops clk_rpmh_ops = { .recalc_rate = clk_rpmh_recalc_rate, }; static int clk_rpmh_bcm_send_cmd(struct clk_rpmh *c, bool enable) { struct tcs_cmd cmd = { 0 }; u32 cmd_state; int ret; mutex_lock(&rpmh_clk_lock); cmd_state = 0; if (enable) { cmd_state = 1; if (c->aggr_state) cmd_state = c->aggr_state; } cmd_state = min_t(u32, cmd_state, BCM_TCS_CMD_VOTE_MASK); if (c->last_sent_aggr_state == cmd_state) { mutex_unlock(&rpmh_clk_lock); return 0; } cmd.addr = c->res_addr; cmd.data = BCM_TCS_CMD(enable, cmd_state); ret = rpmh_write_async(c->rsc->rpmh_client, RPMH_ACTIVE_ONLY_STATE, &cmd, 1); if (ret) { pr_err("set active state of %s failed: (%d)\n", c->res_name, ret); mutex_unlock(&rpmh_clk_lock); return ret; } c->last_sent_aggr_state = cmd_state; mutex_unlock(&rpmh_clk_lock); return 0; } static int clk_rpmh_bcm_prepare(struct clk_hw *hw) { struct clk_rpmh *c = to_clk_rpmh(hw); return clk_rpmh_bcm_send_cmd(c, true); }; static void clk_rpmh_bcm_unprepare(struct clk_hw *hw) { struct clk_rpmh *c = to_clk_rpmh(hw); clk_rpmh_bcm_send_cmd(c, false); }; static int clk_rpmh_bcm_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_rpmh *c = to_clk_rpmh(hw); c->aggr_state = rate / c->unit; /* * Since any non-zero value sent to hw would result in enabling the * clock, only send the value if the clock has already been prepared. */ if (clk_hw_is_prepared(hw)) clk_rpmh_bcm_send_cmd(c, true); return 0; }; static long clk_rpmh_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { return rate; } static unsigned long clk_rpmh_bcm_recalc_rate(struct clk_hw *hw, unsigned long prate) { struct clk_rpmh *c = to_clk_rpmh(hw); return c->aggr_state * c->unit; } static const struct clk_ops clk_rpmh_bcm_ops = { .prepare = clk_rpmh_bcm_prepare, .unprepare = clk_rpmh_bcm_unprepare, .set_rate = clk_rpmh_bcm_set_rate, .round_rate = clk_rpmh_round_rate, .recalc_rate = clk_rpmh_bcm_recalc_rate, }; /* Use awake state instead of active-only on RSCs that do not have an AMC. */ DEFINE_RSC_TYPE(apps_rsc, "apps", false); DEFINE_RSC_TYPE(disp_rsc, "display", true); Loading Loading @@ -306,6 +436,7 @@ DEFINE_CLK_RPMH_VRM(sdmshrike, rf_clk3, rf_clk3_ao, "rfclkd3", &apps_rsc, DEFINE_CLK_RPMH_VRM(sdmshrike, rf_clk4, rf_clk4_ao, "rfclkd4", &apps_rsc, 38400000, CLK_RPMH_APPS_RSC_STATE_MASK, CLK_RPMH_APPS_RSC_AO_STATE_MASK); DEFINE_CLK_RPMH_BCM(sdxprairie, qpic_clk, "QP0", &apps_rsc); static struct clk_hw *sm8150_rpmh_clocks[] = { [RPMH_CXO_CLK] = &sm8150_bi_tcxo.hw, Loading Loading @@ -374,6 +505,7 @@ static struct clk_hw *sdxprairie_rpmh_clocks[] = { [RPMH_RF_CLK1_A] = &sdmshrike_rf_clk1_ao.hw, [RPMH_RF_CLK2] = &sdmshrike_rf_clk2.hw, [RPMH_RF_CLK2_A] = &sdmshrike_rf_clk2_ao.hw, [RPMH_QPIC_CLK] = &sdxprairie_qpic_clk.hw, }; static const struct clk_rpmh_desc clk_rpmh_sdxprairie = { Loading Loading @@ -405,6 +537,8 @@ static int clk_rpmh_probe(struct platform_device *pdev) const struct clk_rpmh_desc *desc; struct property *prop; const char *mbox_name; size_t aux_data_len; struct bcm_db db = {0}; desc = of_device_get_match_data(&pdev->dev); if (!desc) { Loading Loading @@ -489,6 +623,15 @@ static int clk_rpmh_probe(struct platform_device *pdev) goto err2; } rpmh_clk->unit = 1000ULL; aux_data_len = cmd_db_get_aux_data_len(rpmh_clk->res_name); if (aux_data_len) { cmd_db_get_aux_data(rpmh_clk->res_name, (u8 *)&db, sizeof(struct bcm_db)); if (db.unit) rpmh_clk->unit *= le32_to_cpu(db.unit); } clk = devm_clk_register(&pdev->dev, hw_clks[i]); if (IS_ERR(clk)) { ret = PTR_ERR(clk); Loading include/dt-bindings/clock/qcom,rpmh.h +2 −1 Original line number Diff line number Diff line /* * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -29,5 +29,6 @@ #define RPMH_RF_CLK3_A 11 #define RPMH_RF_CLK4 12 #define RPMH_RF_CLK4_A 13 #define RPMH_QPIC_CLK 14 #endif Loading
drivers/clk/qcom/clk-rpmh.c +144 −1 Original line number Diff line number Diff line /* * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -39,6 +39,31 @@ BIT(RPMH_ACTIVE_ONLY_STATE) | \ BIT(RPMH_SLEEP_STATE)) #define BCM_TCS_CMD_COMMIT_MASK 0x40000000 #define BCM_TCS_CMD_VALID_SHIFT 29 #define BCM_TCS_CMD_VOTE_MASK 0x3fff #define BCM_TCS_CMD_VOTE_SHIFT 0 #define BCM_TCS_CMD(valid, vote) \ (BCM_TCS_CMD_COMMIT_MASK | \ ((valid) << BCM_TCS_CMD_VALID_SHIFT) | \ ((vote & BCM_TCS_CMD_VOTE_MASK) \ << BCM_TCS_CMD_VOTE_SHIFT)) /** * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager(BCM) * @unit: divisor used to convert Hz value to an RPMh msg * @width: multiplier used to convert Hz value to an RPMh msg * @vcd: virtual clock domain that this bcm belongs to * @reserved: reserved to pad the struct */ struct bcm_db { __le32 unit; __le16 width; u8 vcd; u8 reserved; }; struct clk_rpmh { const char *res_name; u32 res_addr; Loading @@ -49,6 +74,7 @@ struct clk_rpmh { u32 aggr_state; u32 last_sent_aggr_state; u32 valid_state_mask; u32 unit; struct rsc_type *rsc; unsigned long rate; struct clk_rpmh *peer; Loading Loading @@ -120,6 +146,17 @@ static DEFINE_MUTEX(rpmh_clk_lock); CLK_RPMH_VRM_OFF_VAL, _rsc_id, _rate, _state_mask, \ _state_on_mask) #define DEFINE_CLK_RPMH_BCM(_platform, _name, _res_name, _rsc_id) \ static struct clk_rpmh _platform##_##_name = { \ .res_name = _res_name, \ .valid_state_mask = BIT(RPMH_ACTIVE_ONLY_STATE), \ .rsc = _rsc_id, \ .hw.init = &(struct clk_init_data){ \ .ops = &clk_rpmh_bcm_ops, \ .name = #_name, \ }, \ } #define DEFINE_RSC_TYPE(name, mbox_id, awake_state) \ static struct rsc_type name = { \ .rpmh_client = NULL, \ Loading Loading @@ -271,6 +308,99 @@ static const struct clk_ops clk_rpmh_ops = { .recalc_rate = clk_rpmh_recalc_rate, }; static int clk_rpmh_bcm_send_cmd(struct clk_rpmh *c, bool enable) { struct tcs_cmd cmd = { 0 }; u32 cmd_state; int ret; mutex_lock(&rpmh_clk_lock); cmd_state = 0; if (enable) { cmd_state = 1; if (c->aggr_state) cmd_state = c->aggr_state; } cmd_state = min_t(u32, cmd_state, BCM_TCS_CMD_VOTE_MASK); if (c->last_sent_aggr_state == cmd_state) { mutex_unlock(&rpmh_clk_lock); return 0; } cmd.addr = c->res_addr; cmd.data = BCM_TCS_CMD(enable, cmd_state); ret = rpmh_write_async(c->rsc->rpmh_client, RPMH_ACTIVE_ONLY_STATE, &cmd, 1); if (ret) { pr_err("set active state of %s failed: (%d)\n", c->res_name, ret); mutex_unlock(&rpmh_clk_lock); return ret; } c->last_sent_aggr_state = cmd_state; mutex_unlock(&rpmh_clk_lock); return 0; } static int clk_rpmh_bcm_prepare(struct clk_hw *hw) { struct clk_rpmh *c = to_clk_rpmh(hw); return clk_rpmh_bcm_send_cmd(c, true); }; static void clk_rpmh_bcm_unprepare(struct clk_hw *hw) { struct clk_rpmh *c = to_clk_rpmh(hw); clk_rpmh_bcm_send_cmd(c, false); }; static int clk_rpmh_bcm_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_rpmh *c = to_clk_rpmh(hw); c->aggr_state = rate / c->unit; /* * Since any non-zero value sent to hw would result in enabling the * clock, only send the value if the clock has already been prepared. */ if (clk_hw_is_prepared(hw)) clk_rpmh_bcm_send_cmd(c, true); return 0; }; static long clk_rpmh_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { return rate; } static unsigned long clk_rpmh_bcm_recalc_rate(struct clk_hw *hw, unsigned long prate) { struct clk_rpmh *c = to_clk_rpmh(hw); return c->aggr_state * c->unit; } static const struct clk_ops clk_rpmh_bcm_ops = { .prepare = clk_rpmh_bcm_prepare, .unprepare = clk_rpmh_bcm_unprepare, .set_rate = clk_rpmh_bcm_set_rate, .round_rate = clk_rpmh_round_rate, .recalc_rate = clk_rpmh_bcm_recalc_rate, }; /* Use awake state instead of active-only on RSCs that do not have an AMC. */ DEFINE_RSC_TYPE(apps_rsc, "apps", false); DEFINE_RSC_TYPE(disp_rsc, "display", true); Loading Loading @@ -306,6 +436,7 @@ DEFINE_CLK_RPMH_VRM(sdmshrike, rf_clk3, rf_clk3_ao, "rfclkd3", &apps_rsc, DEFINE_CLK_RPMH_VRM(sdmshrike, rf_clk4, rf_clk4_ao, "rfclkd4", &apps_rsc, 38400000, CLK_RPMH_APPS_RSC_STATE_MASK, CLK_RPMH_APPS_RSC_AO_STATE_MASK); DEFINE_CLK_RPMH_BCM(sdxprairie, qpic_clk, "QP0", &apps_rsc); static struct clk_hw *sm8150_rpmh_clocks[] = { [RPMH_CXO_CLK] = &sm8150_bi_tcxo.hw, Loading Loading @@ -374,6 +505,7 @@ static struct clk_hw *sdxprairie_rpmh_clocks[] = { [RPMH_RF_CLK1_A] = &sdmshrike_rf_clk1_ao.hw, [RPMH_RF_CLK2] = &sdmshrike_rf_clk2.hw, [RPMH_RF_CLK2_A] = &sdmshrike_rf_clk2_ao.hw, [RPMH_QPIC_CLK] = &sdxprairie_qpic_clk.hw, }; static const struct clk_rpmh_desc clk_rpmh_sdxprairie = { Loading Loading @@ -405,6 +537,8 @@ static int clk_rpmh_probe(struct platform_device *pdev) const struct clk_rpmh_desc *desc; struct property *prop; const char *mbox_name; size_t aux_data_len; struct bcm_db db = {0}; desc = of_device_get_match_data(&pdev->dev); if (!desc) { Loading Loading @@ -489,6 +623,15 @@ static int clk_rpmh_probe(struct platform_device *pdev) goto err2; } rpmh_clk->unit = 1000ULL; aux_data_len = cmd_db_get_aux_data_len(rpmh_clk->res_name); if (aux_data_len) { cmd_db_get_aux_data(rpmh_clk->res_name, (u8 *)&db, sizeof(struct bcm_db)); if (db.unit) rpmh_clk->unit *= le32_to_cpu(db.unit); } clk = devm_clk_register(&pdev->dev, hw_clks[i]); if (IS_ERR(clk)) { ret = PTR_ERR(clk); Loading
include/dt-bindings/clock/qcom,rpmh.h +2 −1 Original line number Diff line number Diff line /* * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -29,5 +29,6 @@ #define RPMH_RF_CLK3_A 11 #define RPMH_RF_CLK4 12 #define RPMH_RF_CLK4_A 13 #define RPMH_QPIC_CLK 14 #endif