19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2872f91b5SGeorgi Djakov /* 3872f91b5SGeorgi Djakov * Copyright (c) 2016, Linaro Limited 4872f91b5SGeorgi Djakov * Copyright (c) 2014, The Linux Foundation. All rights reserved. 5872f91b5SGeorgi Djakov */ 6872f91b5SGeorgi Djakov 7872f91b5SGeorgi Djakov #include <linux/clk-provider.h> 8872f91b5SGeorgi Djakov #include <linux/err.h> 9872f91b5SGeorgi Djakov #include <linux/export.h> 10872f91b5SGeorgi Djakov #include <linux/init.h> 11872f91b5SGeorgi Djakov #include <linux/kernel.h> 12872f91b5SGeorgi Djakov #include <linux/module.h> 13872f91b5SGeorgi Djakov #include <linux/mutex.h> 14872f91b5SGeorgi Djakov #include <linux/mfd/qcom_rpm.h> 15872f91b5SGeorgi Djakov #include <linux/of.h> 16872f91b5SGeorgi Djakov #include <linux/of_device.h> 17872f91b5SGeorgi Djakov #include <linux/platform_device.h> 18872f91b5SGeorgi Djakov 19872f91b5SGeorgi Djakov #include <dt-bindings/mfd/qcom-rpm.h> 20872f91b5SGeorgi Djakov #include <dt-bindings/clock/qcom,rpmcc.h> 21872f91b5SGeorgi Djakov 22872f91b5SGeorgi Djakov #define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63 23872f91b5SGeorgi Djakov #define QCOM_RPM_SCALING_ENABLE_ID 0x2 248bcde658SSrinivas Kandagatla #define QCOM_RPM_XO_MODE_ON 0x2 25872f91b5SGeorgi Djakov 26872f91b5SGeorgi Djakov #define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \ 27872f91b5SGeorgi Djakov static struct clk_rpm _platform##_##_active; \ 28872f91b5SGeorgi Djakov static struct clk_rpm _platform##_##_name = { \ 29872f91b5SGeorgi Djakov .rpm_clk_id = (r_id), \ 30872f91b5SGeorgi Djakov .peer = &_platform##_##_active, \ 31872f91b5SGeorgi Djakov .rate = INT_MAX, \ 32872f91b5SGeorgi Djakov .hw.init = &(struct clk_init_data){ \ 33872f91b5SGeorgi Djakov .ops = &clk_rpm_ops, \ 34872f91b5SGeorgi Djakov .name = #_name, \ 35872f91b5SGeorgi Djakov .parent_names = (const char *[]){ "pxo_board" }, \ 36872f91b5SGeorgi Djakov .num_parents = 1, \ 37872f91b5SGeorgi Djakov }, \ 38872f91b5SGeorgi Djakov }; \ 39872f91b5SGeorgi Djakov static struct clk_rpm _platform##_##_active = { \ 40872f91b5SGeorgi Djakov .rpm_clk_id = (r_id), \ 41872f91b5SGeorgi Djakov .peer = &_platform##_##_name, \ 42872f91b5SGeorgi Djakov .active_only = true, \ 43872f91b5SGeorgi Djakov .rate = INT_MAX, \ 44872f91b5SGeorgi Djakov .hw.init = &(struct clk_init_data){ \ 45872f91b5SGeorgi Djakov .ops = &clk_rpm_ops, \ 46872f91b5SGeorgi Djakov .name = #_active, \ 47872f91b5SGeorgi Djakov .parent_names = (const char *[]){ "pxo_board" }, \ 48872f91b5SGeorgi Djakov .num_parents = 1, \ 49872f91b5SGeorgi Djakov }, \ 50872f91b5SGeorgi Djakov } 51872f91b5SGeorgi Djakov 528bcde658SSrinivas Kandagatla #define DEFINE_CLK_RPM_XO_BUFFER(_platform, _name, _active, offset) \ 538bcde658SSrinivas Kandagatla static struct clk_rpm _platform##_##_name = { \ 548bcde658SSrinivas Kandagatla .rpm_clk_id = QCOM_RPM_CXO_BUFFERS, \ 558bcde658SSrinivas Kandagatla .xo_offset = (offset), \ 568bcde658SSrinivas Kandagatla .hw.init = &(struct clk_init_data){ \ 578bcde658SSrinivas Kandagatla .ops = &clk_rpm_xo_ops, \ 588bcde658SSrinivas Kandagatla .name = #_name, \ 598bcde658SSrinivas Kandagatla .parent_names = (const char *[]){ "cxo_board" }, \ 608bcde658SSrinivas Kandagatla .num_parents = 1, \ 618bcde658SSrinivas Kandagatla }, \ 628bcde658SSrinivas Kandagatla } 638bcde658SSrinivas Kandagatla 64d4a69583SLinus Walleij #define DEFINE_CLK_RPM_FIXED(_platform, _name, _active, r_id, r) \ 65d4a69583SLinus Walleij static struct clk_rpm _platform##_##_name = { \ 66d4a69583SLinus Walleij .rpm_clk_id = (r_id), \ 67d4a69583SLinus Walleij .rate = (r), \ 68d4a69583SLinus Walleij .hw.init = &(struct clk_init_data){ \ 69d4a69583SLinus Walleij .ops = &clk_rpm_fixed_ops, \ 70d4a69583SLinus Walleij .name = #_name, \ 71d4a69583SLinus Walleij .parent_names = (const char *[]){ "pxo" }, \ 72d4a69583SLinus Walleij .num_parents = 1, \ 73d4a69583SLinus Walleij }, \ 74d4a69583SLinus Walleij } 75d4a69583SLinus Walleij 76872f91b5SGeorgi Djakov #define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \ 77872f91b5SGeorgi Djakov static struct clk_rpm _platform##_##_active; \ 78872f91b5SGeorgi Djakov static struct clk_rpm _platform##_##_name = { \ 79872f91b5SGeorgi Djakov .rpm_clk_id = (r_id), \ 80872f91b5SGeorgi Djakov .active_only = true, \ 81872f91b5SGeorgi Djakov .peer = &_platform##_##_active, \ 82872f91b5SGeorgi Djakov .rate = (r), \ 83872f91b5SGeorgi Djakov .branch = true, \ 84872f91b5SGeorgi Djakov .hw.init = &(struct clk_init_data){ \ 85872f91b5SGeorgi Djakov .ops = &clk_rpm_branch_ops, \ 86872f91b5SGeorgi Djakov .name = #_name, \ 87872f91b5SGeorgi Djakov .parent_names = (const char *[]){ "pxo_board" }, \ 88872f91b5SGeorgi Djakov .num_parents = 1, \ 89872f91b5SGeorgi Djakov }, \ 90872f91b5SGeorgi Djakov }; \ 91872f91b5SGeorgi Djakov static struct clk_rpm _platform##_##_active = { \ 92872f91b5SGeorgi Djakov .rpm_clk_id = (r_id), \ 93872f91b5SGeorgi Djakov .peer = &_platform##_##_name, \ 94872f91b5SGeorgi Djakov .rate = (r), \ 95872f91b5SGeorgi Djakov .branch = true, \ 96872f91b5SGeorgi Djakov .hw.init = &(struct clk_init_data){ \ 97872f91b5SGeorgi Djakov .ops = &clk_rpm_branch_ops, \ 98872f91b5SGeorgi Djakov .name = #_active, \ 99872f91b5SGeorgi Djakov .parent_names = (const char *[]){ "pxo_board" }, \ 100872f91b5SGeorgi Djakov .num_parents = 1, \ 101872f91b5SGeorgi Djakov }, \ 102872f91b5SGeorgi Djakov } 103872f91b5SGeorgi Djakov 104872f91b5SGeorgi Djakov #define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \ 105872f91b5SGeorgi Djakov static struct clk_rpm _platform##_##_active; \ 106872f91b5SGeorgi Djakov static struct clk_rpm _platform##_##_name = { \ 107872f91b5SGeorgi Djakov .rpm_clk_id = (r_id), \ 108872f91b5SGeorgi Djakov .peer = &_platform##_##_active, \ 109872f91b5SGeorgi Djakov .rate = (r), \ 110872f91b5SGeorgi Djakov .branch = true, \ 111872f91b5SGeorgi Djakov .hw.init = &(struct clk_init_data){ \ 112872f91b5SGeorgi Djakov .ops = &clk_rpm_branch_ops, \ 113872f91b5SGeorgi Djakov .name = #_name, \ 114872f91b5SGeorgi Djakov .parent_names = (const char *[]){ "cxo_board" }, \ 115872f91b5SGeorgi Djakov .num_parents = 1, \ 116872f91b5SGeorgi Djakov }, \ 117872f91b5SGeorgi Djakov }; \ 118872f91b5SGeorgi Djakov static struct clk_rpm _platform##_##_active = { \ 119872f91b5SGeorgi Djakov .rpm_clk_id = (r_id), \ 120872f91b5SGeorgi Djakov .active_only = true, \ 121872f91b5SGeorgi Djakov .peer = &_platform##_##_name, \ 122872f91b5SGeorgi Djakov .rate = (r), \ 123872f91b5SGeorgi Djakov .branch = true, \ 124872f91b5SGeorgi Djakov .hw.init = &(struct clk_init_data){ \ 125872f91b5SGeorgi Djakov .ops = &clk_rpm_branch_ops, \ 126872f91b5SGeorgi Djakov .name = #_active, \ 127872f91b5SGeorgi Djakov .parent_names = (const char *[]){ "cxo_board" }, \ 128872f91b5SGeorgi Djakov .num_parents = 1, \ 129872f91b5SGeorgi Djakov }, \ 130872f91b5SGeorgi Djakov } 131872f91b5SGeorgi Djakov 132872f91b5SGeorgi Djakov #define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw) 133872f91b5SGeorgi Djakov 1348bcde658SSrinivas Kandagatla struct rpm_cc; 1358bcde658SSrinivas Kandagatla 136872f91b5SGeorgi Djakov struct clk_rpm { 137872f91b5SGeorgi Djakov const int rpm_clk_id; 1388bcde658SSrinivas Kandagatla const int xo_offset; 139872f91b5SGeorgi Djakov const bool active_only; 140872f91b5SGeorgi Djakov unsigned long rate; 141872f91b5SGeorgi Djakov bool enabled; 142872f91b5SGeorgi Djakov bool branch; 143872f91b5SGeorgi Djakov struct clk_rpm *peer; 144872f91b5SGeorgi Djakov struct clk_hw hw; 145872f91b5SGeorgi Djakov struct qcom_rpm *rpm; 1468bcde658SSrinivas Kandagatla struct rpm_cc *rpm_cc; 147872f91b5SGeorgi Djakov }; 148872f91b5SGeorgi Djakov 149872f91b5SGeorgi Djakov struct rpm_cc { 150872f91b5SGeorgi Djakov struct qcom_rpm *rpm; 151c260524aSGeorgi Djakov struct clk_rpm **clks; 152c260524aSGeorgi Djakov size_t num_clks; 1538bcde658SSrinivas Kandagatla u32 xo_buffer_value; 1548bcde658SSrinivas Kandagatla struct mutex xo_lock; 155872f91b5SGeorgi Djakov }; 156872f91b5SGeorgi Djakov 157872f91b5SGeorgi Djakov struct rpm_clk_desc { 158872f91b5SGeorgi Djakov struct clk_rpm **clks; 159872f91b5SGeorgi Djakov size_t num_clks; 160872f91b5SGeorgi Djakov }; 161872f91b5SGeorgi Djakov 162872f91b5SGeorgi Djakov static DEFINE_MUTEX(rpm_clk_lock); 163872f91b5SGeorgi Djakov 164872f91b5SGeorgi Djakov static int clk_rpm_handoff(struct clk_rpm *r) 165872f91b5SGeorgi Djakov { 166872f91b5SGeorgi Djakov int ret; 167872f91b5SGeorgi Djakov u32 value = INT_MAX; 168872f91b5SGeorgi Djakov 169d4a69583SLinus Walleij /* 170d4a69583SLinus Walleij * The vendor tree simply reads the status for this 171d4a69583SLinus Walleij * RPM clock. 172d4a69583SLinus Walleij */ 1738bcde658SSrinivas Kandagatla if (r->rpm_clk_id == QCOM_RPM_PLL_4 || 1748bcde658SSrinivas Kandagatla r->rpm_clk_id == QCOM_RPM_CXO_BUFFERS) 175d4a69583SLinus Walleij return 0; 176d4a69583SLinus Walleij 177872f91b5SGeorgi Djakov ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 178872f91b5SGeorgi Djakov r->rpm_clk_id, &value, 1); 179872f91b5SGeorgi Djakov if (ret) 180872f91b5SGeorgi Djakov return ret; 181872f91b5SGeorgi Djakov ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, 182872f91b5SGeorgi Djakov r->rpm_clk_id, &value, 1); 183872f91b5SGeorgi Djakov if (ret) 184872f91b5SGeorgi Djakov return ret; 185872f91b5SGeorgi Djakov 186872f91b5SGeorgi Djakov return 0; 187872f91b5SGeorgi Djakov } 188872f91b5SGeorgi Djakov 189872f91b5SGeorgi Djakov static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate) 190872f91b5SGeorgi Djakov { 191872f91b5SGeorgi Djakov u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ 192872f91b5SGeorgi Djakov 193872f91b5SGeorgi Djakov return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 194872f91b5SGeorgi Djakov r->rpm_clk_id, &value, 1); 195872f91b5SGeorgi Djakov } 196872f91b5SGeorgi Djakov 197872f91b5SGeorgi Djakov static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate) 198872f91b5SGeorgi Djakov { 199872f91b5SGeorgi Djakov u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ 200872f91b5SGeorgi Djakov 201872f91b5SGeorgi Djakov return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, 202872f91b5SGeorgi Djakov r->rpm_clk_id, &value, 1); 203872f91b5SGeorgi Djakov } 204872f91b5SGeorgi Djakov 205872f91b5SGeorgi Djakov static void to_active_sleep(struct clk_rpm *r, unsigned long rate, 206872f91b5SGeorgi Djakov unsigned long *active, unsigned long *sleep) 207872f91b5SGeorgi Djakov { 208872f91b5SGeorgi Djakov *active = rate; 209872f91b5SGeorgi Djakov 210872f91b5SGeorgi Djakov /* 211872f91b5SGeorgi Djakov * Active-only clocks don't care what the rate is during sleep. So, 212872f91b5SGeorgi Djakov * they vote for zero. 213872f91b5SGeorgi Djakov */ 214872f91b5SGeorgi Djakov if (r->active_only) 215872f91b5SGeorgi Djakov *sleep = 0; 216872f91b5SGeorgi Djakov else 217872f91b5SGeorgi Djakov *sleep = *active; 218872f91b5SGeorgi Djakov } 219872f91b5SGeorgi Djakov 220872f91b5SGeorgi Djakov static int clk_rpm_prepare(struct clk_hw *hw) 221872f91b5SGeorgi Djakov { 222872f91b5SGeorgi Djakov struct clk_rpm *r = to_clk_rpm(hw); 223872f91b5SGeorgi Djakov struct clk_rpm *peer = r->peer; 224872f91b5SGeorgi Djakov unsigned long this_rate = 0, this_sleep_rate = 0; 225872f91b5SGeorgi Djakov unsigned long peer_rate = 0, peer_sleep_rate = 0; 226872f91b5SGeorgi Djakov unsigned long active_rate, sleep_rate; 227872f91b5SGeorgi Djakov int ret = 0; 228872f91b5SGeorgi Djakov 229872f91b5SGeorgi Djakov mutex_lock(&rpm_clk_lock); 230872f91b5SGeorgi Djakov 231872f91b5SGeorgi Djakov /* Don't send requests to the RPM if the rate has not been set. */ 232872f91b5SGeorgi Djakov if (!r->rate) 233872f91b5SGeorgi Djakov goto out; 234872f91b5SGeorgi Djakov 235872f91b5SGeorgi Djakov to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); 236872f91b5SGeorgi Djakov 237872f91b5SGeorgi Djakov /* Take peer clock's rate into account only if it's enabled. */ 238872f91b5SGeorgi Djakov if (peer->enabled) 239872f91b5SGeorgi Djakov to_active_sleep(peer, peer->rate, 240872f91b5SGeorgi Djakov &peer_rate, &peer_sleep_rate); 241872f91b5SGeorgi Djakov 242872f91b5SGeorgi Djakov active_rate = max(this_rate, peer_rate); 243872f91b5SGeorgi Djakov 244872f91b5SGeorgi Djakov if (r->branch) 245872f91b5SGeorgi Djakov active_rate = !!active_rate; 246872f91b5SGeorgi Djakov 247872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_active(r, active_rate); 248872f91b5SGeorgi Djakov if (ret) 249872f91b5SGeorgi Djakov goto out; 250872f91b5SGeorgi Djakov 251872f91b5SGeorgi Djakov sleep_rate = max(this_sleep_rate, peer_sleep_rate); 252872f91b5SGeorgi Djakov if (r->branch) 253872f91b5SGeorgi Djakov sleep_rate = !!sleep_rate; 254872f91b5SGeorgi Djakov 255872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_sleep(r, sleep_rate); 256872f91b5SGeorgi Djakov if (ret) 257872f91b5SGeorgi Djakov /* Undo the active set vote and restore it */ 258872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_active(r, peer_rate); 259872f91b5SGeorgi Djakov 260872f91b5SGeorgi Djakov out: 261872f91b5SGeorgi Djakov if (!ret) 262872f91b5SGeorgi Djakov r->enabled = true; 263872f91b5SGeorgi Djakov 264872f91b5SGeorgi Djakov mutex_unlock(&rpm_clk_lock); 265872f91b5SGeorgi Djakov 266872f91b5SGeorgi Djakov return ret; 267872f91b5SGeorgi Djakov } 268872f91b5SGeorgi Djakov 269872f91b5SGeorgi Djakov static void clk_rpm_unprepare(struct clk_hw *hw) 270872f91b5SGeorgi Djakov { 271872f91b5SGeorgi Djakov struct clk_rpm *r = to_clk_rpm(hw); 272872f91b5SGeorgi Djakov struct clk_rpm *peer = r->peer; 273872f91b5SGeorgi Djakov unsigned long peer_rate = 0, peer_sleep_rate = 0; 274872f91b5SGeorgi Djakov unsigned long active_rate, sleep_rate; 275872f91b5SGeorgi Djakov int ret; 276872f91b5SGeorgi Djakov 277872f91b5SGeorgi Djakov mutex_lock(&rpm_clk_lock); 278872f91b5SGeorgi Djakov 279872f91b5SGeorgi Djakov if (!r->rate) 280872f91b5SGeorgi Djakov goto out; 281872f91b5SGeorgi Djakov 282872f91b5SGeorgi Djakov /* Take peer clock's rate into account only if it's enabled. */ 283872f91b5SGeorgi Djakov if (peer->enabled) 284872f91b5SGeorgi Djakov to_active_sleep(peer, peer->rate, &peer_rate, 285872f91b5SGeorgi Djakov &peer_sleep_rate); 286872f91b5SGeorgi Djakov 287872f91b5SGeorgi Djakov active_rate = r->branch ? !!peer_rate : peer_rate; 288872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_active(r, active_rate); 289872f91b5SGeorgi Djakov if (ret) 290872f91b5SGeorgi Djakov goto out; 291872f91b5SGeorgi Djakov 292872f91b5SGeorgi Djakov sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; 293872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_sleep(r, sleep_rate); 294872f91b5SGeorgi Djakov if (ret) 295872f91b5SGeorgi Djakov goto out; 296872f91b5SGeorgi Djakov 297872f91b5SGeorgi Djakov r->enabled = false; 298872f91b5SGeorgi Djakov 299872f91b5SGeorgi Djakov out: 300872f91b5SGeorgi Djakov mutex_unlock(&rpm_clk_lock); 301872f91b5SGeorgi Djakov } 302872f91b5SGeorgi Djakov 3038bcde658SSrinivas Kandagatla static int clk_rpm_xo_prepare(struct clk_hw *hw) 3048bcde658SSrinivas Kandagatla { 3058bcde658SSrinivas Kandagatla struct clk_rpm *r = to_clk_rpm(hw); 3068bcde658SSrinivas Kandagatla struct rpm_cc *rcc = r->rpm_cc; 3078bcde658SSrinivas Kandagatla int ret, clk_id = r->rpm_clk_id; 3088bcde658SSrinivas Kandagatla u32 value; 3098bcde658SSrinivas Kandagatla 3108bcde658SSrinivas Kandagatla mutex_lock(&rcc->xo_lock); 3118bcde658SSrinivas Kandagatla 3128bcde658SSrinivas Kandagatla value = rcc->xo_buffer_value | (QCOM_RPM_XO_MODE_ON << r->xo_offset); 3138bcde658SSrinivas Kandagatla ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, clk_id, &value, 1); 3148bcde658SSrinivas Kandagatla if (!ret) { 3158bcde658SSrinivas Kandagatla r->enabled = true; 3168bcde658SSrinivas Kandagatla rcc->xo_buffer_value = value; 3178bcde658SSrinivas Kandagatla } 3188bcde658SSrinivas Kandagatla 3198bcde658SSrinivas Kandagatla mutex_unlock(&rcc->xo_lock); 3208bcde658SSrinivas Kandagatla 3218bcde658SSrinivas Kandagatla return ret; 3228bcde658SSrinivas Kandagatla } 3238bcde658SSrinivas Kandagatla 3248bcde658SSrinivas Kandagatla static void clk_rpm_xo_unprepare(struct clk_hw *hw) 3258bcde658SSrinivas Kandagatla { 3268bcde658SSrinivas Kandagatla struct clk_rpm *r = to_clk_rpm(hw); 3278bcde658SSrinivas Kandagatla struct rpm_cc *rcc = r->rpm_cc; 3288bcde658SSrinivas Kandagatla int ret, clk_id = r->rpm_clk_id; 3298bcde658SSrinivas Kandagatla u32 value; 3308bcde658SSrinivas Kandagatla 3318bcde658SSrinivas Kandagatla mutex_lock(&rcc->xo_lock); 3328bcde658SSrinivas Kandagatla 3338bcde658SSrinivas Kandagatla value = rcc->xo_buffer_value & ~(QCOM_RPM_XO_MODE_ON << r->xo_offset); 3348bcde658SSrinivas Kandagatla ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, clk_id, &value, 1); 3358bcde658SSrinivas Kandagatla if (!ret) { 3368bcde658SSrinivas Kandagatla r->enabled = false; 3378bcde658SSrinivas Kandagatla rcc->xo_buffer_value = value; 3388bcde658SSrinivas Kandagatla } 3398bcde658SSrinivas Kandagatla 3408bcde658SSrinivas Kandagatla mutex_unlock(&rcc->xo_lock); 3418bcde658SSrinivas Kandagatla } 3428bcde658SSrinivas Kandagatla 343d4a69583SLinus Walleij static int clk_rpm_fixed_prepare(struct clk_hw *hw) 344d4a69583SLinus Walleij { 345d4a69583SLinus Walleij struct clk_rpm *r = to_clk_rpm(hw); 346d4a69583SLinus Walleij u32 value = 1; 347d4a69583SLinus Walleij int ret; 348d4a69583SLinus Walleij 349d4a69583SLinus Walleij ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 350d4a69583SLinus Walleij r->rpm_clk_id, &value, 1); 351d4a69583SLinus Walleij if (!ret) 352d4a69583SLinus Walleij r->enabled = true; 353d4a69583SLinus Walleij 354d4a69583SLinus Walleij return ret; 355d4a69583SLinus Walleij } 356d4a69583SLinus Walleij 357d4a69583SLinus Walleij static void clk_rpm_fixed_unprepare(struct clk_hw *hw) 358d4a69583SLinus Walleij { 359d4a69583SLinus Walleij struct clk_rpm *r = to_clk_rpm(hw); 360d4a69583SLinus Walleij u32 value = 0; 361d4a69583SLinus Walleij int ret; 362d4a69583SLinus Walleij 363d4a69583SLinus Walleij ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 364d4a69583SLinus Walleij r->rpm_clk_id, &value, 1); 365d4a69583SLinus Walleij if (!ret) 366d4a69583SLinus Walleij r->enabled = false; 367d4a69583SLinus Walleij } 368d4a69583SLinus Walleij 369872f91b5SGeorgi Djakov static int clk_rpm_set_rate(struct clk_hw *hw, 370872f91b5SGeorgi Djakov unsigned long rate, unsigned long parent_rate) 371872f91b5SGeorgi Djakov { 372872f91b5SGeorgi Djakov struct clk_rpm *r = to_clk_rpm(hw); 373872f91b5SGeorgi Djakov struct clk_rpm *peer = r->peer; 374872f91b5SGeorgi Djakov unsigned long active_rate, sleep_rate; 375872f91b5SGeorgi Djakov unsigned long this_rate = 0, this_sleep_rate = 0; 376872f91b5SGeorgi Djakov unsigned long peer_rate = 0, peer_sleep_rate = 0; 377872f91b5SGeorgi Djakov int ret = 0; 378872f91b5SGeorgi Djakov 379872f91b5SGeorgi Djakov mutex_lock(&rpm_clk_lock); 380872f91b5SGeorgi Djakov 381872f91b5SGeorgi Djakov if (!r->enabled) 382872f91b5SGeorgi Djakov goto out; 383872f91b5SGeorgi Djakov 384872f91b5SGeorgi Djakov to_active_sleep(r, rate, &this_rate, &this_sleep_rate); 385872f91b5SGeorgi Djakov 386872f91b5SGeorgi Djakov /* Take peer clock's rate into account only if it's enabled. */ 387872f91b5SGeorgi Djakov if (peer->enabled) 388872f91b5SGeorgi Djakov to_active_sleep(peer, peer->rate, 389872f91b5SGeorgi Djakov &peer_rate, &peer_sleep_rate); 390872f91b5SGeorgi Djakov 391872f91b5SGeorgi Djakov active_rate = max(this_rate, peer_rate); 392872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_active(r, active_rate); 393872f91b5SGeorgi Djakov if (ret) 394872f91b5SGeorgi Djakov goto out; 395872f91b5SGeorgi Djakov 396872f91b5SGeorgi Djakov sleep_rate = max(this_sleep_rate, peer_sleep_rate); 397872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_sleep(r, sleep_rate); 398872f91b5SGeorgi Djakov if (ret) 399872f91b5SGeorgi Djakov goto out; 400872f91b5SGeorgi Djakov 401872f91b5SGeorgi Djakov r->rate = rate; 402872f91b5SGeorgi Djakov 403872f91b5SGeorgi Djakov out: 404872f91b5SGeorgi Djakov mutex_unlock(&rpm_clk_lock); 405872f91b5SGeorgi Djakov 406872f91b5SGeorgi Djakov return ret; 407872f91b5SGeorgi Djakov } 408872f91b5SGeorgi Djakov 409872f91b5SGeorgi Djakov static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate, 410872f91b5SGeorgi Djakov unsigned long *parent_rate) 411872f91b5SGeorgi Djakov { 412872f91b5SGeorgi Djakov /* 413872f91b5SGeorgi Djakov * RPM handles rate rounding and we don't have a way to 414872f91b5SGeorgi Djakov * know what the rate will be, so just return whatever 415872f91b5SGeorgi Djakov * rate is requested. 416872f91b5SGeorgi Djakov */ 417872f91b5SGeorgi Djakov return rate; 418872f91b5SGeorgi Djakov } 419872f91b5SGeorgi Djakov 420872f91b5SGeorgi Djakov static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, 421872f91b5SGeorgi Djakov unsigned long parent_rate) 422872f91b5SGeorgi Djakov { 423872f91b5SGeorgi Djakov struct clk_rpm *r = to_clk_rpm(hw); 424872f91b5SGeorgi Djakov 425872f91b5SGeorgi Djakov /* 426872f91b5SGeorgi Djakov * RPM handles rate rounding and we don't have a way to 427872f91b5SGeorgi Djakov * know what the rate will be, so just return whatever 428872f91b5SGeorgi Djakov * rate was set. 429872f91b5SGeorgi Djakov */ 430872f91b5SGeorgi Djakov return r->rate; 431872f91b5SGeorgi Djakov } 432872f91b5SGeorgi Djakov 4338bcde658SSrinivas Kandagatla static const struct clk_ops clk_rpm_xo_ops = { 4348bcde658SSrinivas Kandagatla .prepare = clk_rpm_xo_prepare, 4358bcde658SSrinivas Kandagatla .unprepare = clk_rpm_xo_unprepare, 4368bcde658SSrinivas Kandagatla }; 4378bcde658SSrinivas Kandagatla 438d4a69583SLinus Walleij static const struct clk_ops clk_rpm_fixed_ops = { 439d4a69583SLinus Walleij .prepare = clk_rpm_fixed_prepare, 440d4a69583SLinus Walleij .unprepare = clk_rpm_fixed_unprepare, 441d4a69583SLinus Walleij .round_rate = clk_rpm_round_rate, 442d4a69583SLinus Walleij .recalc_rate = clk_rpm_recalc_rate, 443d4a69583SLinus Walleij }; 444d4a69583SLinus Walleij 445872f91b5SGeorgi Djakov static const struct clk_ops clk_rpm_ops = { 446872f91b5SGeorgi Djakov .prepare = clk_rpm_prepare, 447872f91b5SGeorgi Djakov .unprepare = clk_rpm_unprepare, 448872f91b5SGeorgi Djakov .set_rate = clk_rpm_set_rate, 449872f91b5SGeorgi Djakov .round_rate = clk_rpm_round_rate, 450872f91b5SGeorgi Djakov .recalc_rate = clk_rpm_recalc_rate, 451872f91b5SGeorgi Djakov }; 452872f91b5SGeorgi Djakov 453872f91b5SGeorgi Djakov static const struct clk_ops clk_rpm_branch_ops = { 454872f91b5SGeorgi Djakov .prepare = clk_rpm_prepare, 455872f91b5SGeorgi Djakov .unprepare = clk_rpm_unprepare, 456872f91b5SGeorgi Djakov .round_rate = clk_rpm_round_rate, 457872f91b5SGeorgi Djakov .recalc_rate = clk_rpm_recalc_rate, 458872f91b5SGeorgi Djakov }; 459872f91b5SGeorgi Djakov 460d4a69583SLinus Walleij /* MSM8660/APQ8060 */ 461d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); 462d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); 463d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); 464d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); 465d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); 466d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); 467d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); 468d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, smi_clk, smi_a_clk, QCOM_RPM_SMI_CLK); 469d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); 470d4a69583SLinus Walleij DEFINE_CLK_RPM_FIXED(msm8660, pll4_clk, pll4_a_clk, QCOM_RPM_PLL_4, 540672000); 471d4a69583SLinus Walleij 472d4a69583SLinus Walleij static struct clk_rpm *msm8660_clks[] = { 473d4a69583SLinus Walleij [RPM_APPS_FABRIC_CLK] = &msm8660_afab_clk, 474d4a69583SLinus Walleij [RPM_APPS_FABRIC_A_CLK] = &msm8660_afab_a_clk, 475d4a69583SLinus Walleij [RPM_SYS_FABRIC_CLK] = &msm8660_sfab_clk, 476d4a69583SLinus Walleij [RPM_SYS_FABRIC_A_CLK] = &msm8660_sfab_a_clk, 477d4a69583SLinus Walleij [RPM_MM_FABRIC_CLK] = &msm8660_mmfab_clk, 478d4a69583SLinus Walleij [RPM_MM_FABRIC_A_CLK] = &msm8660_mmfab_a_clk, 479d4a69583SLinus Walleij [RPM_DAYTONA_FABRIC_CLK] = &msm8660_daytona_clk, 480d4a69583SLinus Walleij [RPM_DAYTONA_FABRIC_A_CLK] = &msm8660_daytona_a_clk, 481d4a69583SLinus Walleij [RPM_SFPB_CLK] = &msm8660_sfpb_clk, 482d4a69583SLinus Walleij [RPM_SFPB_A_CLK] = &msm8660_sfpb_a_clk, 483d4a69583SLinus Walleij [RPM_CFPB_CLK] = &msm8660_cfpb_clk, 484d4a69583SLinus Walleij [RPM_CFPB_A_CLK] = &msm8660_cfpb_a_clk, 485d4a69583SLinus Walleij [RPM_MMFPB_CLK] = &msm8660_mmfpb_clk, 486d4a69583SLinus Walleij [RPM_MMFPB_A_CLK] = &msm8660_mmfpb_a_clk, 487d4a69583SLinus Walleij [RPM_SMI_CLK] = &msm8660_smi_clk, 488d4a69583SLinus Walleij [RPM_SMI_A_CLK] = &msm8660_smi_a_clk, 489d4a69583SLinus Walleij [RPM_EBI1_CLK] = &msm8660_ebi1_clk, 490d4a69583SLinus Walleij [RPM_EBI1_A_CLK] = &msm8660_ebi1_a_clk, 491d4a69583SLinus Walleij [RPM_PLL4_CLK] = &msm8660_pll4_clk, 492d4a69583SLinus Walleij }; 493d4a69583SLinus Walleij 494d4a69583SLinus Walleij static const struct rpm_clk_desc rpm_clk_msm8660 = { 495d4a69583SLinus Walleij .clks = msm8660_clks, 496d4a69583SLinus Walleij .num_clks = ARRAY_SIZE(msm8660_clks), 497d4a69583SLinus Walleij }; 498d4a69583SLinus Walleij 499872f91b5SGeorgi Djakov /* apq8064 */ 500872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); 501872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); 502872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); 503872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); 504872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); 505872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); 506872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); 507872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); 508872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); 5098bcde658SSrinivas Kandagatla DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_d0_clk, xo_d0_a_clk, 0); 5108bcde658SSrinivas Kandagatla DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_d1_clk, xo_d1_a_clk, 8); 5118bcde658SSrinivas Kandagatla DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_a0_clk, xo_a0_a_clk, 16); 5128bcde658SSrinivas Kandagatla DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_a1_clk, xo_a1_a_clk, 24); 5138bcde658SSrinivas Kandagatla DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_a2_clk, xo_a2_a_clk, 28); 514872f91b5SGeorgi Djakov 515872f91b5SGeorgi Djakov static struct clk_rpm *apq8064_clks[] = { 516872f91b5SGeorgi Djakov [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, 517872f91b5SGeorgi Djakov [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, 518872f91b5SGeorgi Djakov [RPM_CFPB_CLK] = &apq8064_cfpb_clk, 519872f91b5SGeorgi Djakov [RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk, 520872f91b5SGeorgi Djakov [RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk, 521872f91b5SGeorgi Djakov [RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk, 522872f91b5SGeorgi Djakov [RPM_EBI1_CLK] = &apq8064_ebi1_clk, 523872f91b5SGeorgi Djakov [RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk, 524872f91b5SGeorgi Djakov [RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk, 525872f91b5SGeorgi Djakov [RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk, 526872f91b5SGeorgi Djakov [RPM_MMFPB_CLK] = &apq8064_mmfpb_clk, 527872f91b5SGeorgi Djakov [RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk, 528872f91b5SGeorgi Djakov [RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk, 529872f91b5SGeorgi Djakov [RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk, 530872f91b5SGeorgi Djakov [RPM_SFPB_CLK] = &apq8064_sfpb_clk, 531872f91b5SGeorgi Djakov [RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk, 532872f91b5SGeorgi Djakov [RPM_QDSS_CLK] = &apq8064_qdss_clk, 533872f91b5SGeorgi Djakov [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, 5348bcde658SSrinivas Kandagatla [RPM_XO_D0] = &apq8064_xo_d0_clk, 5358bcde658SSrinivas Kandagatla [RPM_XO_D1] = &apq8064_xo_d1_clk, 5368bcde658SSrinivas Kandagatla [RPM_XO_A0] = &apq8064_xo_a0_clk, 5378bcde658SSrinivas Kandagatla [RPM_XO_A1] = &apq8064_xo_a1_clk, 5388bcde658SSrinivas Kandagatla [RPM_XO_A2] = &apq8064_xo_a2_clk, 539872f91b5SGeorgi Djakov }; 540872f91b5SGeorgi Djakov 541872f91b5SGeorgi Djakov static const struct rpm_clk_desc rpm_clk_apq8064 = { 542872f91b5SGeorgi Djakov .clks = apq8064_clks, 543872f91b5SGeorgi Djakov .num_clks = ARRAY_SIZE(apq8064_clks), 544872f91b5SGeorgi Djakov }; 545872f91b5SGeorgi Djakov 546*eec15273SAnsuel Smith /* ipq806x */ 547*eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); 548*eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); 549*eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); 550*eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); 551*eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); 552*eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); 553*eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK); 554*eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK); 555*eec15273SAnsuel Smith 556*eec15273SAnsuel Smith static struct clk_rpm *ipq806x_clks[] = { 557*eec15273SAnsuel Smith [RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk, 558*eec15273SAnsuel Smith [RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk, 559*eec15273SAnsuel Smith [RPM_CFPB_CLK] = &ipq806x_cfpb_clk, 560*eec15273SAnsuel Smith [RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk, 561*eec15273SAnsuel Smith [RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk, 562*eec15273SAnsuel Smith [RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk, 563*eec15273SAnsuel Smith [RPM_EBI1_CLK] = &ipq806x_ebi1_clk, 564*eec15273SAnsuel Smith [RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk, 565*eec15273SAnsuel Smith [RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk, 566*eec15273SAnsuel Smith [RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk, 567*eec15273SAnsuel Smith [RPM_SFPB_CLK] = &ipq806x_sfpb_clk, 568*eec15273SAnsuel Smith [RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk, 569*eec15273SAnsuel Smith [RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk, 570*eec15273SAnsuel Smith [RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk, 571*eec15273SAnsuel Smith [RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk, 572*eec15273SAnsuel Smith [RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk, 573*eec15273SAnsuel Smith }; 574*eec15273SAnsuel Smith 575*eec15273SAnsuel Smith static const struct rpm_clk_desc rpm_clk_ipq806x = { 576*eec15273SAnsuel Smith .clks = ipq806x_clks, 577*eec15273SAnsuel Smith .num_clks = ARRAY_SIZE(ipq806x_clks), 578*eec15273SAnsuel Smith }; 579*eec15273SAnsuel Smith 580872f91b5SGeorgi Djakov static const struct of_device_id rpm_clk_match_table[] = { 581d4a69583SLinus Walleij { .compatible = "qcom,rpmcc-msm8660", .data = &rpm_clk_msm8660 }, 582d4a69583SLinus Walleij { .compatible = "qcom,rpmcc-apq8060", .data = &rpm_clk_msm8660 }, 583872f91b5SGeorgi Djakov { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, 584*eec15273SAnsuel Smith { .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x }, 585872f91b5SGeorgi Djakov { } 586872f91b5SGeorgi Djakov }; 587872f91b5SGeorgi Djakov MODULE_DEVICE_TABLE(of, rpm_clk_match_table); 588872f91b5SGeorgi Djakov 589c260524aSGeorgi Djakov static struct clk_hw *qcom_rpm_clk_hw_get(struct of_phandle_args *clkspec, 590c260524aSGeorgi Djakov void *data) 591c260524aSGeorgi Djakov { 592c260524aSGeorgi Djakov struct rpm_cc *rcc = data; 593c260524aSGeorgi Djakov unsigned int idx = clkspec->args[0]; 594c260524aSGeorgi Djakov 595c260524aSGeorgi Djakov if (idx >= rcc->num_clks) { 596c260524aSGeorgi Djakov pr_err("%s: invalid index %u\n", __func__, idx); 597c260524aSGeorgi Djakov return ERR_PTR(-EINVAL); 598c260524aSGeorgi Djakov } 599c260524aSGeorgi Djakov 600c260524aSGeorgi Djakov return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT); 601c260524aSGeorgi Djakov } 602c260524aSGeorgi Djakov 603872f91b5SGeorgi Djakov static int rpm_clk_probe(struct platform_device *pdev) 604872f91b5SGeorgi Djakov { 605872f91b5SGeorgi Djakov struct rpm_cc *rcc; 606872f91b5SGeorgi Djakov int ret; 607872f91b5SGeorgi Djakov size_t num_clks, i; 608872f91b5SGeorgi Djakov struct qcom_rpm *rpm; 609872f91b5SGeorgi Djakov struct clk_rpm **rpm_clks; 610872f91b5SGeorgi Djakov const struct rpm_clk_desc *desc; 611872f91b5SGeorgi Djakov 612872f91b5SGeorgi Djakov rpm = dev_get_drvdata(pdev->dev.parent); 613872f91b5SGeorgi Djakov if (!rpm) { 614872f91b5SGeorgi Djakov dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); 615872f91b5SGeorgi Djakov return -ENODEV; 616872f91b5SGeorgi Djakov } 617872f91b5SGeorgi Djakov 618872f91b5SGeorgi Djakov desc = of_device_get_match_data(&pdev->dev); 619872f91b5SGeorgi Djakov if (!desc) 620872f91b5SGeorgi Djakov return -EINVAL; 621872f91b5SGeorgi Djakov 622872f91b5SGeorgi Djakov rpm_clks = desc->clks; 623872f91b5SGeorgi Djakov num_clks = desc->num_clks; 624872f91b5SGeorgi Djakov 625c260524aSGeorgi Djakov rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL); 626872f91b5SGeorgi Djakov if (!rcc) 627872f91b5SGeorgi Djakov return -ENOMEM; 628872f91b5SGeorgi Djakov 629c260524aSGeorgi Djakov rcc->clks = rpm_clks; 630c260524aSGeorgi Djakov rcc->num_clks = num_clks; 6318bcde658SSrinivas Kandagatla mutex_init(&rcc->xo_lock); 632872f91b5SGeorgi Djakov 633872f91b5SGeorgi Djakov for (i = 0; i < num_clks; i++) { 634872f91b5SGeorgi Djakov if (!rpm_clks[i]) 635872f91b5SGeorgi Djakov continue; 636872f91b5SGeorgi Djakov 637872f91b5SGeorgi Djakov rpm_clks[i]->rpm = rpm; 6388bcde658SSrinivas Kandagatla rpm_clks[i]->rpm_cc = rcc; 639872f91b5SGeorgi Djakov 640872f91b5SGeorgi Djakov ret = clk_rpm_handoff(rpm_clks[i]); 641872f91b5SGeorgi Djakov if (ret) 642872f91b5SGeorgi Djakov goto err; 643872f91b5SGeorgi Djakov } 644872f91b5SGeorgi Djakov 645872f91b5SGeorgi Djakov for (i = 0; i < num_clks; i++) { 646c260524aSGeorgi Djakov if (!rpm_clks[i]) 647872f91b5SGeorgi Djakov continue; 648872f91b5SGeorgi Djakov 649872f91b5SGeorgi Djakov ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); 650872f91b5SGeorgi Djakov if (ret) 651872f91b5SGeorgi Djakov goto err; 652872f91b5SGeorgi Djakov } 653872f91b5SGeorgi Djakov 654c260524aSGeorgi Djakov ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_rpm_clk_hw_get, 655c260524aSGeorgi Djakov rcc); 656872f91b5SGeorgi Djakov if (ret) 657872f91b5SGeorgi Djakov goto err; 658872f91b5SGeorgi Djakov 659872f91b5SGeorgi Djakov return 0; 660872f91b5SGeorgi Djakov err: 661872f91b5SGeorgi Djakov dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret); 662872f91b5SGeorgi Djakov return ret; 663872f91b5SGeorgi Djakov } 664872f91b5SGeorgi Djakov 665872f91b5SGeorgi Djakov static int rpm_clk_remove(struct platform_device *pdev) 666872f91b5SGeorgi Djakov { 667872f91b5SGeorgi Djakov of_clk_del_provider(pdev->dev.of_node); 668872f91b5SGeorgi Djakov return 0; 669872f91b5SGeorgi Djakov } 670872f91b5SGeorgi Djakov 671872f91b5SGeorgi Djakov static struct platform_driver rpm_clk_driver = { 672872f91b5SGeorgi Djakov .driver = { 673872f91b5SGeorgi Djakov .name = "qcom-clk-rpm", 674872f91b5SGeorgi Djakov .of_match_table = rpm_clk_match_table, 675872f91b5SGeorgi Djakov }, 676872f91b5SGeorgi Djakov .probe = rpm_clk_probe, 677872f91b5SGeorgi Djakov .remove = rpm_clk_remove, 678872f91b5SGeorgi Djakov }; 679872f91b5SGeorgi Djakov 680872f91b5SGeorgi Djakov static int __init rpm_clk_init(void) 681872f91b5SGeorgi Djakov { 682872f91b5SGeorgi Djakov return platform_driver_register(&rpm_clk_driver); 683872f91b5SGeorgi Djakov } 684872f91b5SGeorgi Djakov core_initcall(rpm_clk_init); 685872f91b5SGeorgi Djakov 686872f91b5SGeorgi Djakov static void __exit rpm_clk_exit(void) 687872f91b5SGeorgi Djakov { 688872f91b5SGeorgi Djakov platform_driver_unregister(&rpm_clk_driver); 689872f91b5SGeorgi Djakov } 690872f91b5SGeorgi Djakov module_exit(rpm_clk_exit); 691872f91b5SGeorgi Djakov 692872f91b5SGeorgi Djakov MODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver"); 693872f91b5SGeorgi Djakov MODULE_LICENSE("GPL v2"); 694872f91b5SGeorgi Djakov MODULE_ALIAS("platform:qcom-clk-rpm"); 695