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 26129d9cd9SChristian Marangi static const struct clk_parent_data gcc_pxo[] = { 27129d9cd9SChristian Marangi { .fw_name = "pxo", .name = "pxo_board" }, 28129d9cd9SChristian Marangi }; 29129d9cd9SChristian Marangi 30129d9cd9SChristian Marangi static const struct clk_parent_data gcc_cxo[] = { 31129d9cd9SChristian Marangi { .fw_name = "cxo", .name = "cxo_board" }, 32129d9cd9SChristian Marangi }; 33129d9cd9SChristian Marangi 34872f91b5SGeorgi Djakov #define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \ 35872f91b5SGeorgi Djakov static struct clk_rpm _platform##_##_active; \ 36872f91b5SGeorgi Djakov static struct clk_rpm _platform##_##_name = { \ 37872f91b5SGeorgi Djakov .rpm_clk_id = (r_id), \ 38872f91b5SGeorgi Djakov .peer = &_platform##_##_active, \ 39872f91b5SGeorgi Djakov .rate = INT_MAX, \ 40872f91b5SGeorgi Djakov .hw.init = &(struct clk_init_data){ \ 41872f91b5SGeorgi Djakov .ops = &clk_rpm_ops, \ 42872f91b5SGeorgi Djakov .name = #_name, \ 43129d9cd9SChristian Marangi .parent_data = gcc_pxo, \ 44129d9cd9SChristian Marangi .num_parents = ARRAY_SIZE(gcc_pxo), \ 45872f91b5SGeorgi Djakov }, \ 46872f91b5SGeorgi Djakov }; \ 47872f91b5SGeorgi Djakov static struct clk_rpm _platform##_##_active = { \ 48872f91b5SGeorgi Djakov .rpm_clk_id = (r_id), \ 49872f91b5SGeorgi Djakov .peer = &_platform##_##_name, \ 50872f91b5SGeorgi Djakov .active_only = true, \ 51872f91b5SGeorgi Djakov .rate = INT_MAX, \ 52872f91b5SGeorgi Djakov .hw.init = &(struct clk_init_data){ \ 53872f91b5SGeorgi Djakov .ops = &clk_rpm_ops, \ 54872f91b5SGeorgi Djakov .name = #_active, \ 55129d9cd9SChristian Marangi .parent_data = gcc_pxo, \ 56129d9cd9SChristian Marangi .num_parents = ARRAY_SIZE(gcc_pxo), \ 57872f91b5SGeorgi Djakov }, \ 58872f91b5SGeorgi Djakov } 59872f91b5SGeorgi Djakov 60*fbd5a2cbSDmitry Baryshkov #define DEFINE_CLK_RPM_XO_BUFFER(_platform, _name, offset) \ 618bcde658SSrinivas Kandagatla static struct clk_rpm _platform##_##_name = { \ 628bcde658SSrinivas Kandagatla .rpm_clk_id = QCOM_RPM_CXO_BUFFERS, \ 638bcde658SSrinivas Kandagatla .xo_offset = (offset), \ 648bcde658SSrinivas Kandagatla .hw.init = &(struct clk_init_data){ \ 658bcde658SSrinivas Kandagatla .ops = &clk_rpm_xo_ops, \ 668bcde658SSrinivas Kandagatla .name = #_name, \ 67129d9cd9SChristian Marangi .parent_data = gcc_cxo, \ 68129d9cd9SChristian Marangi .num_parents = ARRAY_SIZE(gcc_cxo), \ 698bcde658SSrinivas Kandagatla }, \ 708bcde658SSrinivas Kandagatla } 718bcde658SSrinivas Kandagatla 72*fbd5a2cbSDmitry Baryshkov #define DEFINE_CLK_RPM_FIXED(_platform, _name, r_id, r) \ 73d4a69583SLinus Walleij static struct clk_rpm _platform##_##_name = { \ 74d4a69583SLinus Walleij .rpm_clk_id = (r_id), \ 75d4a69583SLinus Walleij .rate = (r), \ 76d4a69583SLinus Walleij .hw.init = &(struct clk_init_data){ \ 77d4a69583SLinus Walleij .ops = &clk_rpm_fixed_ops, \ 78d4a69583SLinus Walleij .name = #_name, \ 79129d9cd9SChristian Marangi .parent_data = gcc_pxo, \ 80129d9cd9SChristian Marangi .num_parents = ARRAY_SIZE(gcc_pxo), \ 81d4a69583SLinus Walleij }, \ 82d4a69583SLinus Walleij } 83d4a69583SLinus Walleij 84872f91b5SGeorgi Djakov #define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw) 85872f91b5SGeorgi Djakov 868bcde658SSrinivas Kandagatla struct rpm_cc; 878bcde658SSrinivas Kandagatla 88872f91b5SGeorgi Djakov struct clk_rpm { 89872f91b5SGeorgi Djakov const int rpm_clk_id; 908bcde658SSrinivas Kandagatla const int xo_offset; 91872f91b5SGeorgi Djakov const bool active_only; 92872f91b5SGeorgi Djakov unsigned long rate; 93872f91b5SGeorgi Djakov bool enabled; 94872f91b5SGeorgi Djakov bool branch; 95872f91b5SGeorgi Djakov struct clk_rpm *peer; 96872f91b5SGeorgi Djakov struct clk_hw hw; 97872f91b5SGeorgi Djakov struct qcom_rpm *rpm; 988bcde658SSrinivas Kandagatla struct rpm_cc *rpm_cc; 99872f91b5SGeorgi Djakov }; 100872f91b5SGeorgi Djakov 101872f91b5SGeorgi Djakov struct rpm_cc { 102872f91b5SGeorgi Djakov struct qcom_rpm *rpm; 103c260524aSGeorgi Djakov struct clk_rpm **clks; 104c260524aSGeorgi Djakov size_t num_clks; 1058bcde658SSrinivas Kandagatla u32 xo_buffer_value; 1068bcde658SSrinivas Kandagatla struct mutex xo_lock; 107872f91b5SGeorgi Djakov }; 108872f91b5SGeorgi Djakov 109872f91b5SGeorgi Djakov struct rpm_clk_desc { 110872f91b5SGeorgi Djakov struct clk_rpm **clks; 111872f91b5SGeorgi Djakov size_t num_clks; 112872f91b5SGeorgi Djakov }; 113872f91b5SGeorgi Djakov 114872f91b5SGeorgi Djakov static DEFINE_MUTEX(rpm_clk_lock); 115872f91b5SGeorgi Djakov 116872f91b5SGeorgi Djakov static int clk_rpm_handoff(struct clk_rpm *r) 117872f91b5SGeorgi Djakov { 118872f91b5SGeorgi Djakov int ret; 119872f91b5SGeorgi Djakov u32 value = INT_MAX; 120872f91b5SGeorgi Djakov 121d4a69583SLinus Walleij /* 122d4a69583SLinus Walleij * The vendor tree simply reads the status for this 123d4a69583SLinus Walleij * RPM clock. 124d4a69583SLinus Walleij */ 1258bcde658SSrinivas Kandagatla if (r->rpm_clk_id == QCOM_RPM_PLL_4 || 1268bcde658SSrinivas Kandagatla r->rpm_clk_id == QCOM_RPM_CXO_BUFFERS) 127d4a69583SLinus Walleij return 0; 128d4a69583SLinus Walleij 129872f91b5SGeorgi Djakov ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 130872f91b5SGeorgi Djakov r->rpm_clk_id, &value, 1); 131872f91b5SGeorgi Djakov if (ret) 132872f91b5SGeorgi Djakov return ret; 133872f91b5SGeorgi Djakov ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, 134872f91b5SGeorgi Djakov r->rpm_clk_id, &value, 1); 135872f91b5SGeorgi Djakov if (ret) 136872f91b5SGeorgi Djakov return ret; 137872f91b5SGeorgi Djakov 138872f91b5SGeorgi Djakov return 0; 139872f91b5SGeorgi Djakov } 140872f91b5SGeorgi Djakov 141872f91b5SGeorgi Djakov static int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate) 142872f91b5SGeorgi Djakov { 143872f91b5SGeorgi Djakov u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ 144872f91b5SGeorgi Djakov 145872f91b5SGeorgi Djakov return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 146872f91b5SGeorgi Djakov r->rpm_clk_id, &value, 1); 147872f91b5SGeorgi Djakov } 148872f91b5SGeorgi Djakov 149872f91b5SGeorgi Djakov static int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate) 150872f91b5SGeorgi Djakov { 151872f91b5SGeorgi Djakov u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ 152872f91b5SGeorgi Djakov 153872f91b5SGeorgi Djakov return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, 154872f91b5SGeorgi Djakov r->rpm_clk_id, &value, 1); 155872f91b5SGeorgi Djakov } 156872f91b5SGeorgi Djakov 157872f91b5SGeorgi Djakov static void to_active_sleep(struct clk_rpm *r, unsigned long rate, 158872f91b5SGeorgi Djakov unsigned long *active, unsigned long *sleep) 159872f91b5SGeorgi Djakov { 160872f91b5SGeorgi Djakov *active = rate; 161872f91b5SGeorgi Djakov 162872f91b5SGeorgi Djakov /* 163872f91b5SGeorgi Djakov * Active-only clocks don't care what the rate is during sleep. So, 164872f91b5SGeorgi Djakov * they vote for zero. 165872f91b5SGeorgi Djakov */ 166872f91b5SGeorgi Djakov if (r->active_only) 167872f91b5SGeorgi Djakov *sleep = 0; 168872f91b5SGeorgi Djakov else 169872f91b5SGeorgi Djakov *sleep = *active; 170872f91b5SGeorgi Djakov } 171872f91b5SGeorgi Djakov 172872f91b5SGeorgi Djakov static int clk_rpm_prepare(struct clk_hw *hw) 173872f91b5SGeorgi Djakov { 174872f91b5SGeorgi Djakov struct clk_rpm *r = to_clk_rpm(hw); 175872f91b5SGeorgi Djakov struct clk_rpm *peer = r->peer; 176872f91b5SGeorgi Djakov unsigned long this_rate = 0, this_sleep_rate = 0; 177872f91b5SGeorgi Djakov unsigned long peer_rate = 0, peer_sleep_rate = 0; 178872f91b5SGeorgi Djakov unsigned long active_rate, sleep_rate; 179872f91b5SGeorgi Djakov int ret = 0; 180872f91b5SGeorgi Djakov 181872f91b5SGeorgi Djakov mutex_lock(&rpm_clk_lock); 182872f91b5SGeorgi Djakov 183872f91b5SGeorgi Djakov /* Don't send requests to the RPM if the rate has not been set. */ 184872f91b5SGeorgi Djakov if (!r->rate) 185872f91b5SGeorgi Djakov goto out; 186872f91b5SGeorgi Djakov 187872f91b5SGeorgi Djakov to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); 188872f91b5SGeorgi Djakov 189872f91b5SGeorgi Djakov /* Take peer clock's rate into account only if it's enabled. */ 190872f91b5SGeorgi Djakov if (peer->enabled) 191872f91b5SGeorgi Djakov to_active_sleep(peer, peer->rate, 192872f91b5SGeorgi Djakov &peer_rate, &peer_sleep_rate); 193872f91b5SGeorgi Djakov 194872f91b5SGeorgi Djakov active_rate = max(this_rate, peer_rate); 195872f91b5SGeorgi Djakov 196872f91b5SGeorgi Djakov if (r->branch) 197872f91b5SGeorgi Djakov active_rate = !!active_rate; 198872f91b5SGeorgi Djakov 199872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_active(r, active_rate); 200872f91b5SGeorgi Djakov if (ret) 201872f91b5SGeorgi Djakov goto out; 202872f91b5SGeorgi Djakov 203872f91b5SGeorgi Djakov sleep_rate = max(this_sleep_rate, peer_sleep_rate); 204872f91b5SGeorgi Djakov if (r->branch) 205872f91b5SGeorgi Djakov sleep_rate = !!sleep_rate; 206872f91b5SGeorgi Djakov 207872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_sleep(r, sleep_rate); 208872f91b5SGeorgi Djakov if (ret) 209872f91b5SGeorgi Djakov /* Undo the active set vote and restore it */ 210872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_active(r, peer_rate); 211872f91b5SGeorgi Djakov 212872f91b5SGeorgi Djakov out: 213872f91b5SGeorgi Djakov if (!ret) 214872f91b5SGeorgi Djakov r->enabled = true; 215872f91b5SGeorgi Djakov 216872f91b5SGeorgi Djakov mutex_unlock(&rpm_clk_lock); 217872f91b5SGeorgi Djakov 218872f91b5SGeorgi Djakov return ret; 219872f91b5SGeorgi Djakov } 220872f91b5SGeorgi Djakov 221872f91b5SGeorgi Djakov static void clk_rpm_unprepare(struct clk_hw *hw) 222872f91b5SGeorgi Djakov { 223872f91b5SGeorgi Djakov struct clk_rpm *r = to_clk_rpm(hw); 224872f91b5SGeorgi Djakov struct clk_rpm *peer = r->peer; 225872f91b5SGeorgi Djakov unsigned long peer_rate = 0, peer_sleep_rate = 0; 226872f91b5SGeorgi Djakov unsigned long active_rate, sleep_rate; 227872f91b5SGeorgi Djakov int ret; 228872f91b5SGeorgi Djakov 229872f91b5SGeorgi Djakov mutex_lock(&rpm_clk_lock); 230872f91b5SGeorgi Djakov 231872f91b5SGeorgi Djakov if (!r->rate) 232872f91b5SGeorgi Djakov goto out; 233872f91b5SGeorgi Djakov 234872f91b5SGeorgi Djakov /* Take peer clock's rate into account only if it's enabled. */ 235872f91b5SGeorgi Djakov if (peer->enabled) 236872f91b5SGeorgi Djakov to_active_sleep(peer, peer->rate, &peer_rate, 237872f91b5SGeorgi Djakov &peer_sleep_rate); 238872f91b5SGeorgi Djakov 239872f91b5SGeorgi Djakov active_rate = r->branch ? !!peer_rate : peer_rate; 240872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_active(r, active_rate); 241872f91b5SGeorgi Djakov if (ret) 242872f91b5SGeorgi Djakov goto out; 243872f91b5SGeorgi Djakov 244872f91b5SGeorgi Djakov sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; 245872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_sleep(r, sleep_rate); 246872f91b5SGeorgi Djakov if (ret) 247872f91b5SGeorgi Djakov goto out; 248872f91b5SGeorgi Djakov 249872f91b5SGeorgi Djakov r->enabled = false; 250872f91b5SGeorgi Djakov 251872f91b5SGeorgi Djakov out: 252872f91b5SGeorgi Djakov mutex_unlock(&rpm_clk_lock); 253872f91b5SGeorgi Djakov } 254872f91b5SGeorgi Djakov 2558bcde658SSrinivas Kandagatla static int clk_rpm_xo_prepare(struct clk_hw *hw) 2568bcde658SSrinivas Kandagatla { 2578bcde658SSrinivas Kandagatla struct clk_rpm *r = to_clk_rpm(hw); 2588bcde658SSrinivas Kandagatla struct rpm_cc *rcc = r->rpm_cc; 2598bcde658SSrinivas Kandagatla int ret, clk_id = r->rpm_clk_id; 2608bcde658SSrinivas Kandagatla u32 value; 2618bcde658SSrinivas Kandagatla 2628bcde658SSrinivas Kandagatla mutex_lock(&rcc->xo_lock); 2638bcde658SSrinivas Kandagatla 2648bcde658SSrinivas Kandagatla value = rcc->xo_buffer_value | (QCOM_RPM_XO_MODE_ON << r->xo_offset); 2658bcde658SSrinivas Kandagatla ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, clk_id, &value, 1); 2668bcde658SSrinivas Kandagatla if (!ret) { 2678bcde658SSrinivas Kandagatla r->enabled = true; 2688bcde658SSrinivas Kandagatla rcc->xo_buffer_value = value; 2698bcde658SSrinivas Kandagatla } 2708bcde658SSrinivas Kandagatla 2718bcde658SSrinivas Kandagatla mutex_unlock(&rcc->xo_lock); 2728bcde658SSrinivas Kandagatla 2738bcde658SSrinivas Kandagatla return ret; 2748bcde658SSrinivas Kandagatla } 2758bcde658SSrinivas Kandagatla 2768bcde658SSrinivas Kandagatla static void clk_rpm_xo_unprepare(struct clk_hw *hw) 2778bcde658SSrinivas Kandagatla { 2788bcde658SSrinivas Kandagatla struct clk_rpm *r = to_clk_rpm(hw); 2798bcde658SSrinivas Kandagatla struct rpm_cc *rcc = r->rpm_cc; 2808bcde658SSrinivas Kandagatla int ret, clk_id = r->rpm_clk_id; 2818bcde658SSrinivas Kandagatla u32 value; 2828bcde658SSrinivas Kandagatla 2838bcde658SSrinivas Kandagatla mutex_lock(&rcc->xo_lock); 2848bcde658SSrinivas Kandagatla 2858bcde658SSrinivas Kandagatla value = rcc->xo_buffer_value & ~(QCOM_RPM_XO_MODE_ON << r->xo_offset); 2868bcde658SSrinivas Kandagatla ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, clk_id, &value, 1); 2878bcde658SSrinivas Kandagatla if (!ret) { 2888bcde658SSrinivas Kandagatla r->enabled = false; 2898bcde658SSrinivas Kandagatla rcc->xo_buffer_value = value; 2908bcde658SSrinivas Kandagatla } 2918bcde658SSrinivas Kandagatla 2928bcde658SSrinivas Kandagatla mutex_unlock(&rcc->xo_lock); 2938bcde658SSrinivas Kandagatla } 2948bcde658SSrinivas Kandagatla 295d4a69583SLinus Walleij static int clk_rpm_fixed_prepare(struct clk_hw *hw) 296d4a69583SLinus Walleij { 297d4a69583SLinus Walleij struct clk_rpm *r = to_clk_rpm(hw); 298d4a69583SLinus Walleij u32 value = 1; 299d4a69583SLinus Walleij int ret; 300d4a69583SLinus Walleij 301d4a69583SLinus Walleij ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 302d4a69583SLinus Walleij r->rpm_clk_id, &value, 1); 303d4a69583SLinus Walleij if (!ret) 304d4a69583SLinus Walleij r->enabled = true; 305d4a69583SLinus Walleij 306d4a69583SLinus Walleij return ret; 307d4a69583SLinus Walleij } 308d4a69583SLinus Walleij 309d4a69583SLinus Walleij static void clk_rpm_fixed_unprepare(struct clk_hw *hw) 310d4a69583SLinus Walleij { 311d4a69583SLinus Walleij struct clk_rpm *r = to_clk_rpm(hw); 312d4a69583SLinus Walleij u32 value = 0; 313d4a69583SLinus Walleij int ret; 314d4a69583SLinus Walleij 315d4a69583SLinus Walleij ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 316d4a69583SLinus Walleij r->rpm_clk_id, &value, 1); 317d4a69583SLinus Walleij if (!ret) 318d4a69583SLinus Walleij r->enabled = false; 319d4a69583SLinus Walleij } 320d4a69583SLinus Walleij 321872f91b5SGeorgi Djakov static int clk_rpm_set_rate(struct clk_hw *hw, 322872f91b5SGeorgi Djakov unsigned long rate, unsigned long parent_rate) 323872f91b5SGeorgi Djakov { 324872f91b5SGeorgi Djakov struct clk_rpm *r = to_clk_rpm(hw); 325872f91b5SGeorgi Djakov struct clk_rpm *peer = r->peer; 326872f91b5SGeorgi Djakov unsigned long active_rate, sleep_rate; 327872f91b5SGeorgi Djakov unsigned long this_rate = 0, this_sleep_rate = 0; 328872f91b5SGeorgi Djakov unsigned long peer_rate = 0, peer_sleep_rate = 0; 329872f91b5SGeorgi Djakov int ret = 0; 330872f91b5SGeorgi Djakov 331872f91b5SGeorgi Djakov mutex_lock(&rpm_clk_lock); 332872f91b5SGeorgi Djakov 333872f91b5SGeorgi Djakov if (!r->enabled) 334872f91b5SGeorgi Djakov goto out; 335872f91b5SGeorgi Djakov 336872f91b5SGeorgi Djakov to_active_sleep(r, rate, &this_rate, &this_sleep_rate); 337872f91b5SGeorgi Djakov 338872f91b5SGeorgi Djakov /* Take peer clock's rate into account only if it's enabled. */ 339872f91b5SGeorgi Djakov if (peer->enabled) 340872f91b5SGeorgi Djakov to_active_sleep(peer, peer->rate, 341872f91b5SGeorgi Djakov &peer_rate, &peer_sleep_rate); 342872f91b5SGeorgi Djakov 343872f91b5SGeorgi Djakov active_rate = max(this_rate, peer_rate); 344872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_active(r, active_rate); 345872f91b5SGeorgi Djakov if (ret) 346872f91b5SGeorgi Djakov goto out; 347872f91b5SGeorgi Djakov 348872f91b5SGeorgi Djakov sleep_rate = max(this_sleep_rate, peer_sleep_rate); 349872f91b5SGeorgi Djakov ret = clk_rpm_set_rate_sleep(r, sleep_rate); 350872f91b5SGeorgi Djakov if (ret) 351872f91b5SGeorgi Djakov goto out; 352872f91b5SGeorgi Djakov 353872f91b5SGeorgi Djakov r->rate = rate; 354872f91b5SGeorgi Djakov 355872f91b5SGeorgi Djakov out: 356872f91b5SGeorgi Djakov mutex_unlock(&rpm_clk_lock); 357872f91b5SGeorgi Djakov 358872f91b5SGeorgi Djakov return ret; 359872f91b5SGeorgi Djakov } 360872f91b5SGeorgi Djakov 361872f91b5SGeorgi Djakov static long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate, 362872f91b5SGeorgi Djakov unsigned long *parent_rate) 363872f91b5SGeorgi Djakov { 364872f91b5SGeorgi Djakov /* 365872f91b5SGeorgi Djakov * RPM handles rate rounding and we don't have a way to 366872f91b5SGeorgi Djakov * know what the rate will be, so just return whatever 367872f91b5SGeorgi Djakov * rate is requested. 368872f91b5SGeorgi Djakov */ 369872f91b5SGeorgi Djakov return rate; 370872f91b5SGeorgi Djakov } 371872f91b5SGeorgi Djakov 372872f91b5SGeorgi Djakov static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, 373872f91b5SGeorgi Djakov unsigned long parent_rate) 374872f91b5SGeorgi Djakov { 375872f91b5SGeorgi Djakov struct clk_rpm *r = to_clk_rpm(hw); 376872f91b5SGeorgi Djakov 377872f91b5SGeorgi Djakov /* 378872f91b5SGeorgi Djakov * RPM handles rate rounding and we don't have a way to 379872f91b5SGeorgi Djakov * know what the rate will be, so just return whatever 380872f91b5SGeorgi Djakov * rate was set. 381872f91b5SGeorgi Djakov */ 382872f91b5SGeorgi Djakov return r->rate; 383872f91b5SGeorgi Djakov } 384872f91b5SGeorgi Djakov 3858bcde658SSrinivas Kandagatla static const struct clk_ops clk_rpm_xo_ops = { 3868bcde658SSrinivas Kandagatla .prepare = clk_rpm_xo_prepare, 3878bcde658SSrinivas Kandagatla .unprepare = clk_rpm_xo_unprepare, 3888bcde658SSrinivas Kandagatla }; 3898bcde658SSrinivas Kandagatla 390d4a69583SLinus Walleij static const struct clk_ops clk_rpm_fixed_ops = { 391d4a69583SLinus Walleij .prepare = clk_rpm_fixed_prepare, 392d4a69583SLinus Walleij .unprepare = clk_rpm_fixed_unprepare, 393d4a69583SLinus Walleij .round_rate = clk_rpm_round_rate, 394d4a69583SLinus Walleij .recalc_rate = clk_rpm_recalc_rate, 395d4a69583SLinus Walleij }; 396d4a69583SLinus Walleij 397872f91b5SGeorgi Djakov static const struct clk_ops clk_rpm_ops = { 398872f91b5SGeorgi Djakov .prepare = clk_rpm_prepare, 399872f91b5SGeorgi Djakov .unprepare = clk_rpm_unprepare, 400872f91b5SGeorgi Djakov .set_rate = clk_rpm_set_rate, 401872f91b5SGeorgi Djakov .round_rate = clk_rpm_round_rate, 402872f91b5SGeorgi Djakov .recalc_rate = clk_rpm_recalc_rate, 403872f91b5SGeorgi Djakov }; 404872f91b5SGeorgi Djakov 405d4a69583SLinus Walleij /* MSM8660/APQ8060 */ 406d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); 407d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); 408d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); 409d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); 410d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); 411d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); 412d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); 413d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, smi_clk, smi_a_clk, QCOM_RPM_SMI_CLK); 414d4a69583SLinus Walleij DEFINE_CLK_RPM(msm8660, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); 415*fbd5a2cbSDmitry Baryshkov DEFINE_CLK_RPM_FIXED(msm8660, pll4_clk, QCOM_RPM_PLL_4, 540672000); 416d4a69583SLinus Walleij 417d4a69583SLinus Walleij static struct clk_rpm *msm8660_clks[] = { 418d4a69583SLinus Walleij [RPM_APPS_FABRIC_CLK] = &msm8660_afab_clk, 419d4a69583SLinus Walleij [RPM_APPS_FABRIC_A_CLK] = &msm8660_afab_a_clk, 420d4a69583SLinus Walleij [RPM_SYS_FABRIC_CLK] = &msm8660_sfab_clk, 421d4a69583SLinus Walleij [RPM_SYS_FABRIC_A_CLK] = &msm8660_sfab_a_clk, 422d4a69583SLinus Walleij [RPM_MM_FABRIC_CLK] = &msm8660_mmfab_clk, 423d4a69583SLinus Walleij [RPM_MM_FABRIC_A_CLK] = &msm8660_mmfab_a_clk, 424d4a69583SLinus Walleij [RPM_DAYTONA_FABRIC_CLK] = &msm8660_daytona_clk, 425d4a69583SLinus Walleij [RPM_DAYTONA_FABRIC_A_CLK] = &msm8660_daytona_a_clk, 426d4a69583SLinus Walleij [RPM_SFPB_CLK] = &msm8660_sfpb_clk, 427d4a69583SLinus Walleij [RPM_SFPB_A_CLK] = &msm8660_sfpb_a_clk, 428d4a69583SLinus Walleij [RPM_CFPB_CLK] = &msm8660_cfpb_clk, 429d4a69583SLinus Walleij [RPM_CFPB_A_CLK] = &msm8660_cfpb_a_clk, 430d4a69583SLinus Walleij [RPM_MMFPB_CLK] = &msm8660_mmfpb_clk, 431d4a69583SLinus Walleij [RPM_MMFPB_A_CLK] = &msm8660_mmfpb_a_clk, 432d4a69583SLinus Walleij [RPM_SMI_CLK] = &msm8660_smi_clk, 433d4a69583SLinus Walleij [RPM_SMI_A_CLK] = &msm8660_smi_a_clk, 434d4a69583SLinus Walleij [RPM_EBI1_CLK] = &msm8660_ebi1_clk, 435d4a69583SLinus Walleij [RPM_EBI1_A_CLK] = &msm8660_ebi1_a_clk, 436d4a69583SLinus Walleij [RPM_PLL4_CLK] = &msm8660_pll4_clk, 437d4a69583SLinus Walleij }; 438d4a69583SLinus Walleij 439d4a69583SLinus Walleij static const struct rpm_clk_desc rpm_clk_msm8660 = { 440d4a69583SLinus Walleij .clks = msm8660_clks, 441d4a69583SLinus Walleij .num_clks = ARRAY_SIZE(msm8660_clks), 442d4a69583SLinus Walleij }; 443d4a69583SLinus Walleij 444872f91b5SGeorgi Djakov /* apq8064 */ 445872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); 446872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); 447872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); 448872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); 449872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); 450872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); 451872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); 452872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); 453872f91b5SGeorgi Djakov DEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); 454*fbd5a2cbSDmitry Baryshkov DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_d0_clk, 0); 455*fbd5a2cbSDmitry Baryshkov DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_d1_clk, 8); 456*fbd5a2cbSDmitry Baryshkov DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_a0_clk, 16); 457*fbd5a2cbSDmitry Baryshkov DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_a1_clk, 24); 458*fbd5a2cbSDmitry Baryshkov DEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_a2_clk, 28); 459872f91b5SGeorgi Djakov 460872f91b5SGeorgi Djakov static struct clk_rpm *apq8064_clks[] = { 461872f91b5SGeorgi Djakov [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, 462872f91b5SGeorgi Djakov [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, 463872f91b5SGeorgi Djakov [RPM_CFPB_CLK] = &apq8064_cfpb_clk, 464872f91b5SGeorgi Djakov [RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk, 465872f91b5SGeorgi Djakov [RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk, 466872f91b5SGeorgi Djakov [RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk, 467872f91b5SGeorgi Djakov [RPM_EBI1_CLK] = &apq8064_ebi1_clk, 468872f91b5SGeorgi Djakov [RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk, 469872f91b5SGeorgi Djakov [RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk, 470872f91b5SGeorgi Djakov [RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk, 471872f91b5SGeorgi Djakov [RPM_MMFPB_CLK] = &apq8064_mmfpb_clk, 472872f91b5SGeorgi Djakov [RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk, 473872f91b5SGeorgi Djakov [RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk, 474872f91b5SGeorgi Djakov [RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk, 475872f91b5SGeorgi Djakov [RPM_SFPB_CLK] = &apq8064_sfpb_clk, 476872f91b5SGeorgi Djakov [RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk, 477872f91b5SGeorgi Djakov [RPM_QDSS_CLK] = &apq8064_qdss_clk, 478872f91b5SGeorgi Djakov [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, 4798bcde658SSrinivas Kandagatla [RPM_XO_D0] = &apq8064_xo_d0_clk, 4808bcde658SSrinivas Kandagatla [RPM_XO_D1] = &apq8064_xo_d1_clk, 4818bcde658SSrinivas Kandagatla [RPM_XO_A0] = &apq8064_xo_a0_clk, 4828bcde658SSrinivas Kandagatla [RPM_XO_A1] = &apq8064_xo_a1_clk, 4838bcde658SSrinivas Kandagatla [RPM_XO_A2] = &apq8064_xo_a2_clk, 484872f91b5SGeorgi Djakov }; 485872f91b5SGeorgi Djakov 486872f91b5SGeorgi Djakov static const struct rpm_clk_desc rpm_clk_apq8064 = { 487872f91b5SGeorgi Djakov .clks = apq8064_clks, 488872f91b5SGeorgi Djakov .num_clks = ARRAY_SIZE(apq8064_clks), 489872f91b5SGeorgi Djakov }; 490872f91b5SGeorgi Djakov 491eec15273SAnsuel Smith /* ipq806x */ 492eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); 493eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); 494eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); 495eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); 496eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); 497eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); 498eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK); 499eec15273SAnsuel Smith DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK); 500eec15273SAnsuel Smith 501eec15273SAnsuel Smith static struct clk_rpm *ipq806x_clks[] = { 502eec15273SAnsuel Smith [RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk, 503eec15273SAnsuel Smith [RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk, 504eec15273SAnsuel Smith [RPM_CFPB_CLK] = &ipq806x_cfpb_clk, 505eec15273SAnsuel Smith [RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk, 506eec15273SAnsuel Smith [RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk, 507eec15273SAnsuel Smith [RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk, 508eec15273SAnsuel Smith [RPM_EBI1_CLK] = &ipq806x_ebi1_clk, 509eec15273SAnsuel Smith [RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk, 510eec15273SAnsuel Smith [RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk, 511eec15273SAnsuel Smith [RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk, 512eec15273SAnsuel Smith [RPM_SFPB_CLK] = &ipq806x_sfpb_clk, 513eec15273SAnsuel Smith [RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk, 514eec15273SAnsuel Smith [RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk, 515eec15273SAnsuel Smith [RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk, 516eec15273SAnsuel Smith [RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk, 517eec15273SAnsuel Smith [RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk, 518eec15273SAnsuel Smith }; 519eec15273SAnsuel Smith 520eec15273SAnsuel Smith static const struct rpm_clk_desc rpm_clk_ipq806x = { 521eec15273SAnsuel Smith .clks = ipq806x_clks, 522eec15273SAnsuel Smith .num_clks = ARRAY_SIZE(ipq806x_clks), 523eec15273SAnsuel Smith }; 524eec15273SAnsuel Smith 525872f91b5SGeorgi Djakov static const struct of_device_id rpm_clk_match_table[] = { 526d4a69583SLinus Walleij { .compatible = "qcom,rpmcc-msm8660", .data = &rpm_clk_msm8660 }, 527d4a69583SLinus Walleij { .compatible = "qcom,rpmcc-apq8060", .data = &rpm_clk_msm8660 }, 528872f91b5SGeorgi Djakov { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, 529eec15273SAnsuel Smith { .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x }, 530872f91b5SGeorgi Djakov { } 531872f91b5SGeorgi Djakov }; 532872f91b5SGeorgi Djakov MODULE_DEVICE_TABLE(of, rpm_clk_match_table); 533872f91b5SGeorgi Djakov 534c260524aSGeorgi Djakov static struct clk_hw *qcom_rpm_clk_hw_get(struct of_phandle_args *clkspec, 535c260524aSGeorgi Djakov void *data) 536c260524aSGeorgi Djakov { 537c260524aSGeorgi Djakov struct rpm_cc *rcc = data; 538c260524aSGeorgi Djakov unsigned int idx = clkspec->args[0]; 539c260524aSGeorgi Djakov 540c260524aSGeorgi Djakov if (idx >= rcc->num_clks) { 541c260524aSGeorgi Djakov pr_err("%s: invalid index %u\n", __func__, idx); 542c260524aSGeorgi Djakov return ERR_PTR(-EINVAL); 543c260524aSGeorgi Djakov } 544c260524aSGeorgi Djakov 545c260524aSGeorgi Djakov return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT); 546c260524aSGeorgi Djakov } 547c260524aSGeorgi Djakov 548872f91b5SGeorgi Djakov static int rpm_clk_probe(struct platform_device *pdev) 549872f91b5SGeorgi Djakov { 550872f91b5SGeorgi Djakov struct rpm_cc *rcc; 551872f91b5SGeorgi Djakov int ret; 552872f91b5SGeorgi Djakov size_t num_clks, i; 553872f91b5SGeorgi Djakov struct qcom_rpm *rpm; 554872f91b5SGeorgi Djakov struct clk_rpm **rpm_clks; 555872f91b5SGeorgi Djakov const struct rpm_clk_desc *desc; 556872f91b5SGeorgi Djakov 557872f91b5SGeorgi Djakov rpm = dev_get_drvdata(pdev->dev.parent); 558872f91b5SGeorgi Djakov if (!rpm) { 559872f91b5SGeorgi Djakov dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); 560872f91b5SGeorgi Djakov return -ENODEV; 561872f91b5SGeorgi Djakov } 562872f91b5SGeorgi Djakov 563872f91b5SGeorgi Djakov desc = of_device_get_match_data(&pdev->dev); 564872f91b5SGeorgi Djakov if (!desc) 565872f91b5SGeorgi Djakov return -EINVAL; 566872f91b5SGeorgi Djakov 567872f91b5SGeorgi Djakov rpm_clks = desc->clks; 568872f91b5SGeorgi Djakov num_clks = desc->num_clks; 569872f91b5SGeorgi Djakov 570c260524aSGeorgi Djakov rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL); 571872f91b5SGeorgi Djakov if (!rcc) 572872f91b5SGeorgi Djakov return -ENOMEM; 573872f91b5SGeorgi Djakov 574c260524aSGeorgi Djakov rcc->clks = rpm_clks; 575c260524aSGeorgi Djakov rcc->num_clks = num_clks; 5768bcde658SSrinivas Kandagatla mutex_init(&rcc->xo_lock); 577872f91b5SGeorgi Djakov 578872f91b5SGeorgi Djakov for (i = 0; i < num_clks; i++) { 579872f91b5SGeorgi Djakov if (!rpm_clks[i]) 580872f91b5SGeorgi Djakov continue; 581872f91b5SGeorgi Djakov 582872f91b5SGeorgi Djakov rpm_clks[i]->rpm = rpm; 5838bcde658SSrinivas Kandagatla rpm_clks[i]->rpm_cc = rcc; 584872f91b5SGeorgi Djakov 585872f91b5SGeorgi Djakov ret = clk_rpm_handoff(rpm_clks[i]); 586872f91b5SGeorgi Djakov if (ret) 587872f91b5SGeorgi Djakov goto err; 588872f91b5SGeorgi Djakov } 589872f91b5SGeorgi Djakov 590872f91b5SGeorgi Djakov for (i = 0; i < num_clks; i++) { 591c260524aSGeorgi Djakov if (!rpm_clks[i]) 592872f91b5SGeorgi Djakov continue; 593872f91b5SGeorgi Djakov 594872f91b5SGeorgi Djakov ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); 595872f91b5SGeorgi Djakov if (ret) 596872f91b5SGeorgi Djakov goto err; 597872f91b5SGeorgi Djakov } 598872f91b5SGeorgi Djakov 599c260524aSGeorgi Djakov ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_rpm_clk_hw_get, 600c260524aSGeorgi Djakov rcc); 601872f91b5SGeorgi Djakov if (ret) 602872f91b5SGeorgi Djakov goto err; 603872f91b5SGeorgi Djakov 604872f91b5SGeorgi Djakov return 0; 605872f91b5SGeorgi Djakov err: 606872f91b5SGeorgi Djakov dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret); 607872f91b5SGeorgi Djakov return ret; 608872f91b5SGeorgi Djakov } 609872f91b5SGeorgi Djakov 610872f91b5SGeorgi Djakov static int rpm_clk_remove(struct platform_device *pdev) 611872f91b5SGeorgi Djakov { 612872f91b5SGeorgi Djakov of_clk_del_provider(pdev->dev.of_node); 613872f91b5SGeorgi Djakov return 0; 614872f91b5SGeorgi Djakov } 615872f91b5SGeorgi Djakov 616872f91b5SGeorgi Djakov static struct platform_driver rpm_clk_driver = { 617872f91b5SGeorgi Djakov .driver = { 618872f91b5SGeorgi Djakov .name = "qcom-clk-rpm", 619872f91b5SGeorgi Djakov .of_match_table = rpm_clk_match_table, 620872f91b5SGeorgi Djakov }, 621872f91b5SGeorgi Djakov .probe = rpm_clk_probe, 622872f91b5SGeorgi Djakov .remove = rpm_clk_remove, 623872f91b5SGeorgi Djakov }; 624872f91b5SGeorgi Djakov 625872f91b5SGeorgi Djakov static int __init rpm_clk_init(void) 626872f91b5SGeorgi Djakov { 627872f91b5SGeorgi Djakov return platform_driver_register(&rpm_clk_driver); 628872f91b5SGeorgi Djakov } 629872f91b5SGeorgi Djakov core_initcall(rpm_clk_init); 630872f91b5SGeorgi Djakov 631872f91b5SGeorgi Djakov static void __exit rpm_clk_exit(void) 632872f91b5SGeorgi Djakov { 633872f91b5SGeorgi Djakov platform_driver_unregister(&rpm_clk_driver); 634872f91b5SGeorgi Djakov } 635872f91b5SGeorgi Djakov module_exit(rpm_clk_exit); 636872f91b5SGeorgi Djakov 637872f91b5SGeorgi Djakov MODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver"); 638872f91b5SGeorgi Djakov MODULE_LICENSE("GPL v2"); 639872f91b5SGeorgi Djakov MODULE_ALIAS("platform:qcom-clk-rpm"); 640