18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2016, Linaro Limited 48c2ecf20Sopenharmony_ci * Copyright (c) 2014, The Linux Foundation. All rights reserved. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 88c2ecf20Sopenharmony_ci#include <linux/err.h> 98c2ecf20Sopenharmony_ci#include <linux/export.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/mutex.h> 148c2ecf20Sopenharmony_ci#include <linux/mfd/qcom_rpm.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/of_device.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <dt-bindings/mfd/qcom-rpm.h> 208c2ecf20Sopenharmony_ci#include <dt-bindings/clock/qcom,rpmcc.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define QCOM_RPM_MISC_CLK_TYPE 0x306b6c63 238c2ecf20Sopenharmony_ci#define QCOM_RPM_SCALING_ENABLE_ID 0x2 248c2ecf20Sopenharmony_ci#define QCOM_RPM_XO_MODE_ON 0x2 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \ 278c2ecf20Sopenharmony_ci static struct clk_rpm _platform##_##_active; \ 288c2ecf20Sopenharmony_ci static struct clk_rpm _platform##_##_name = { \ 298c2ecf20Sopenharmony_ci .rpm_clk_id = (r_id), \ 308c2ecf20Sopenharmony_ci .peer = &_platform##_##_active, \ 318c2ecf20Sopenharmony_ci .rate = INT_MAX, \ 328c2ecf20Sopenharmony_ci .hw.init = &(struct clk_init_data){ \ 338c2ecf20Sopenharmony_ci .ops = &clk_rpm_ops, \ 348c2ecf20Sopenharmony_ci .name = #_name, \ 358c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ "pxo_board" }, \ 368c2ecf20Sopenharmony_ci .num_parents = 1, \ 378c2ecf20Sopenharmony_ci }, \ 388c2ecf20Sopenharmony_ci }; \ 398c2ecf20Sopenharmony_ci static struct clk_rpm _platform##_##_active = { \ 408c2ecf20Sopenharmony_ci .rpm_clk_id = (r_id), \ 418c2ecf20Sopenharmony_ci .peer = &_platform##_##_name, \ 428c2ecf20Sopenharmony_ci .active_only = true, \ 438c2ecf20Sopenharmony_ci .rate = INT_MAX, \ 448c2ecf20Sopenharmony_ci .hw.init = &(struct clk_init_data){ \ 458c2ecf20Sopenharmony_ci .ops = &clk_rpm_ops, \ 468c2ecf20Sopenharmony_ci .name = #_active, \ 478c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ "pxo_board" }, \ 488c2ecf20Sopenharmony_ci .num_parents = 1, \ 498c2ecf20Sopenharmony_ci }, \ 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define DEFINE_CLK_RPM_XO_BUFFER(_platform, _name, _active, offset) \ 538c2ecf20Sopenharmony_ci static struct clk_rpm _platform##_##_name = { \ 548c2ecf20Sopenharmony_ci .rpm_clk_id = QCOM_RPM_CXO_BUFFERS, \ 558c2ecf20Sopenharmony_ci .xo_offset = (offset), \ 568c2ecf20Sopenharmony_ci .hw.init = &(struct clk_init_data){ \ 578c2ecf20Sopenharmony_ci .ops = &clk_rpm_xo_ops, \ 588c2ecf20Sopenharmony_ci .name = #_name, \ 598c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ "cxo_board" }, \ 608c2ecf20Sopenharmony_ci .num_parents = 1, \ 618c2ecf20Sopenharmony_ci }, \ 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define DEFINE_CLK_RPM_FIXED(_platform, _name, _active, r_id, r) \ 658c2ecf20Sopenharmony_ci static struct clk_rpm _platform##_##_name = { \ 668c2ecf20Sopenharmony_ci .rpm_clk_id = (r_id), \ 678c2ecf20Sopenharmony_ci .rate = (r), \ 688c2ecf20Sopenharmony_ci .hw.init = &(struct clk_init_data){ \ 698c2ecf20Sopenharmony_ci .ops = &clk_rpm_fixed_ops, \ 708c2ecf20Sopenharmony_ci .name = #_name, \ 718c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ "pxo" }, \ 728c2ecf20Sopenharmony_ci .num_parents = 1, \ 738c2ecf20Sopenharmony_ci }, \ 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define DEFINE_CLK_RPM_PXO_BRANCH(_platform, _name, _active, r_id, r) \ 778c2ecf20Sopenharmony_ci static struct clk_rpm _platform##_##_active; \ 788c2ecf20Sopenharmony_ci static struct clk_rpm _platform##_##_name = { \ 798c2ecf20Sopenharmony_ci .rpm_clk_id = (r_id), \ 808c2ecf20Sopenharmony_ci .active_only = true, \ 818c2ecf20Sopenharmony_ci .peer = &_platform##_##_active, \ 828c2ecf20Sopenharmony_ci .rate = (r), \ 838c2ecf20Sopenharmony_ci .branch = true, \ 848c2ecf20Sopenharmony_ci .hw.init = &(struct clk_init_data){ \ 858c2ecf20Sopenharmony_ci .ops = &clk_rpm_branch_ops, \ 868c2ecf20Sopenharmony_ci .name = #_name, \ 878c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ "pxo_board" }, \ 888c2ecf20Sopenharmony_ci .num_parents = 1, \ 898c2ecf20Sopenharmony_ci }, \ 908c2ecf20Sopenharmony_ci }; \ 918c2ecf20Sopenharmony_ci static struct clk_rpm _platform##_##_active = { \ 928c2ecf20Sopenharmony_ci .rpm_clk_id = (r_id), \ 938c2ecf20Sopenharmony_ci .peer = &_platform##_##_name, \ 948c2ecf20Sopenharmony_ci .rate = (r), \ 958c2ecf20Sopenharmony_ci .branch = true, \ 968c2ecf20Sopenharmony_ci .hw.init = &(struct clk_init_data){ \ 978c2ecf20Sopenharmony_ci .ops = &clk_rpm_branch_ops, \ 988c2ecf20Sopenharmony_ci .name = #_active, \ 998c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ "pxo_board" }, \ 1008c2ecf20Sopenharmony_ci .num_parents = 1, \ 1018c2ecf20Sopenharmony_ci }, \ 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define DEFINE_CLK_RPM_CXO_BRANCH(_platform, _name, _active, r_id, r) \ 1058c2ecf20Sopenharmony_ci static struct clk_rpm _platform##_##_active; \ 1068c2ecf20Sopenharmony_ci static struct clk_rpm _platform##_##_name = { \ 1078c2ecf20Sopenharmony_ci .rpm_clk_id = (r_id), \ 1088c2ecf20Sopenharmony_ci .peer = &_platform##_##_active, \ 1098c2ecf20Sopenharmony_ci .rate = (r), \ 1108c2ecf20Sopenharmony_ci .branch = true, \ 1118c2ecf20Sopenharmony_ci .hw.init = &(struct clk_init_data){ \ 1128c2ecf20Sopenharmony_ci .ops = &clk_rpm_branch_ops, \ 1138c2ecf20Sopenharmony_ci .name = #_name, \ 1148c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ "cxo_board" }, \ 1158c2ecf20Sopenharmony_ci .num_parents = 1, \ 1168c2ecf20Sopenharmony_ci }, \ 1178c2ecf20Sopenharmony_ci }; \ 1188c2ecf20Sopenharmony_ci static struct clk_rpm _platform##_##_active = { \ 1198c2ecf20Sopenharmony_ci .rpm_clk_id = (r_id), \ 1208c2ecf20Sopenharmony_ci .active_only = true, \ 1218c2ecf20Sopenharmony_ci .peer = &_platform##_##_name, \ 1228c2ecf20Sopenharmony_ci .rate = (r), \ 1238c2ecf20Sopenharmony_ci .branch = true, \ 1248c2ecf20Sopenharmony_ci .hw.init = &(struct clk_init_data){ \ 1258c2ecf20Sopenharmony_ci .ops = &clk_rpm_branch_ops, \ 1268c2ecf20Sopenharmony_ci .name = #_active, \ 1278c2ecf20Sopenharmony_ci .parent_names = (const char *[]){ "cxo_board" }, \ 1288c2ecf20Sopenharmony_ci .num_parents = 1, \ 1298c2ecf20Sopenharmony_ci }, \ 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci#define to_clk_rpm(_hw) container_of(_hw, struct clk_rpm, hw) 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistruct rpm_cc; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistruct clk_rpm { 1378c2ecf20Sopenharmony_ci const int rpm_clk_id; 1388c2ecf20Sopenharmony_ci const int xo_offset; 1398c2ecf20Sopenharmony_ci const bool active_only; 1408c2ecf20Sopenharmony_ci unsigned long rate; 1418c2ecf20Sopenharmony_ci bool enabled; 1428c2ecf20Sopenharmony_ci bool branch; 1438c2ecf20Sopenharmony_ci struct clk_rpm *peer; 1448c2ecf20Sopenharmony_ci struct clk_hw hw; 1458c2ecf20Sopenharmony_ci struct qcom_rpm *rpm; 1468c2ecf20Sopenharmony_ci struct rpm_cc *rpm_cc; 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistruct rpm_cc { 1508c2ecf20Sopenharmony_ci struct qcom_rpm *rpm; 1518c2ecf20Sopenharmony_ci struct clk_rpm **clks; 1528c2ecf20Sopenharmony_ci size_t num_clks; 1538c2ecf20Sopenharmony_ci u32 xo_buffer_value; 1548c2ecf20Sopenharmony_ci struct mutex xo_lock; 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistruct rpm_clk_desc { 1588c2ecf20Sopenharmony_ci struct clk_rpm **clks; 1598c2ecf20Sopenharmony_ci size_t num_clks; 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(rpm_clk_lock); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int clk_rpm_handoff(struct clk_rpm *r) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci int ret; 1678c2ecf20Sopenharmony_ci u32 value = INT_MAX; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * The vendor tree simply reads the status for this 1718c2ecf20Sopenharmony_ci * RPM clock. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci if (r->rpm_clk_id == QCOM_RPM_PLL_4 || 1748c2ecf20Sopenharmony_ci r->rpm_clk_id == QCOM_RPM_CXO_BUFFERS) 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 1788c2ecf20Sopenharmony_ci r->rpm_clk_id, &value, 1); 1798c2ecf20Sopenharmony_ci if (ret) 1808c2ecf20Sopenharmony_ci return ret; 1818c2ecf20Sopenharmony_ci ret = qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, 1828c2ecf20Sopenharmony_ci r->rpm_clk_id, &value, 1); 1838c2ecf20Sopenharmony_ci if (ret) 1848c2ecf20Sopenharmony_ci return ret; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int clk_rpm_set_rate_active(struct clk_rpm *r, unsigned long rate) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 1948c2ecf20Sopenharmony_ci r->rpm_clk_id, &value, 1); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int clk_rpm_set_rate_sleep(struct clk_rpm *r, unsigned long rate) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci u32 value = DIV_ROUND_UP(rate, 1000); /* to kHz */ 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return qcom_rpm_write(r->rpm, QCOM_RPM_SLEEP_STATE, 2028c2ecf20Sopenharmony_ci r->rpm_clk_id, &value, 1); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void to_active_sleep(struct clk_rpm *r, unsigned long rate, 2068c2ecf20Sopenharmony_ci unsigned long *active, unsigned long *sleep) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci *active = rate; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * Active-only clocks don't care what the rate is during sleep. So, 2128c2ecf20Sopenharmony_ci * they vote for zero. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci if (r->active_only) 2158c2ecf20Sopenharmony_ci *sleep = 0; 2168c2ecf20Sopenharmony_ci else 2178c2ecf20Sopenharmony_ci *sleep = *active; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic int clk_rpm_prepare(struct clk_hw *hw) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct clk_rpm *r = to_clk_rpm(hw); 2238c2ecf20Sopenharmony_ci struct clk_rpm *peer = r->peer; 2248c2ecf20Sopenharmony_ci unsigned long this_rate = 0, this_sleep_rate = 0; 2258c2ecf20Sopenharmony_ci unsigned long peer_rate = 0, peer_sleep_rate = 0; 2268c2ecf20Sopenharmony_ci unsigned long active_rate, sleep_rate; 2278c2ecf20Sopenharmony_ci int ret = 0; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci mutex_lock(&rpm_clk_lock); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Don't send requests to the RPM if the rate has not been set. */ 2328c2ecf20Sopenharmony_ci if (!r->rate) 2338c2ecf20Sopenharmony_ci goto out; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci to_active_sleep(r, r->rate, &this_rate, &this_sleep_rate); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* Take peer clock's rate into account only if it's enabled. */ 2388c2ecf20Sopenharmony_ci if (peer->enabled) 2398c2ecf20Sopenharmony_ci to_active_sleep(peer, peer->rate, 2408c2ecf20Sopenharmony_ci &peer_rate, &peer_sleep_rate); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci active_rate = max(this_rate, peer_rate); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (r->branch) 2458c2ecf20Sopenharmony_ci active_rate = !!active_rate; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci ret = clk_rpm_set_rate_active(r, active_rate); 2488c2ecf20Sopenharmony_ci if (ret) 2498c2ecf20Sopenharmony_ci goto out; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci sleep_rate = max(this_sleep_rate, peer_sleep_rate); 2528c2ecf20Sopenharmony_ci if (r->branch) 2538c2ecf20Sopenharmony_ci sleep_rate = !!sleep_rate; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci ret = clk_rpm_set_rate_sleep(r, sleep_rate); 2568c2ecf20Sopenharmony_ci if (ret) 2578c2ecf20Sopenharmony_ci /* Undo the active set vote and restore it */ 2588c2ecf20Sopenharmony_ci ret = clk_rpm_set_rate_active(r, peer_rate); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ciout: 2618c2ecf20Sopenharmony_ci if (!ret) 2628c2ecf20Sopenharmony_ci r->enabled = true; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci mutex_unlock(&rpm_clk_lock); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic void clk_rpm_unprepare(struct clk_hw *hw) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct clk_rpm *r = to_clk_rpm(hw); 2728c2ecf20Sopenharmony_ci struct clk_rpm *peer = r->peer; 2738c2ecf20Sopenharmony_ci unsigned long peer_rate = 0, peer_sleep_rate = 0; 2748c2ecf20Sopenharmony_ci unsigned long active_rate, sleep_rate; 2758c2ecf20Sopenharmony_ci int ret; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci mutex_lock(&rpm_clk_lock); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (!r->rate) 2808c2ecf20Sopenharmony_ci goto out; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* Take peer clock's rate into account only if it's enabled. */ 2838c2ecf20Sopenharmony_ci if (peer->enabled) 2848c2ecf20Sopenharmony_ci to_active_sleep(peer, peer->rate, &peer_rate, 2858c2ecf20Sopenharmony_ci &peer_sleep_rate); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci active_rate = r->branch ? !!peer_rate : peer_rate; 2888c2ecf20Sopenharmony_ci ret = clk_rpm_set_rate_active(r, active_rate); 2898c2ecf20Sopenharmony_ci if (ret) 2908c2ecf20Sopenharmony_ci goto out; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci sleep_rate = r->branch ? !!peer_sleep_rate : peer_sleep_rate; 2938c2ecf20Sopenharmony_ci ret = clk_rpm_set_rate_sleep(r, sleep_rate); 2948c2ecf20Sopenharmony_ci if (ret) 2958c2ecf20Sopenharmony_ci goto out; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci r->enabled = false; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ciout: 3008c2ecf20Sopenharmony_ci mutex_unlock(&rpm_clk_lock); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int clk_rpm_xo_prepare(struct clk_hw *hw) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct clk_rpm *r = to_clk_rpm(hw); 3068c2ecf20Sopenharmony_ci struct rpm_cc *rcc = r->rpm_cc; 3078c2ecf20Sopenharmony_ci int ret, clk_id = r->rpm_clk_id; 3088c2ecf20Sopenharmony_ci u32 value; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci mutex_lock(&rcc->xo_lock); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci value = rcc->xo_buffer_value | (QCOM_RPM_XO_MODE_ON << r->xo_offset); 3138c2ecf20Sopenharmony_ci ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, clk_id, &value, 1); 3148c2ecf20Sopenharmony_ci if (!ret) { 3158c2ecf20Sopenharmony_ci r->enabled = true; 3168c2ecf20Sopenharmony_ci rcc->xo_buffer_value = value; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci mutex_unlock(&rcc->xo_lock); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return ret; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void clk_rpm_xo_unprepare(struct clk_hw *hw) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct clk_rpm *r = to_clk_rpm(hw); 3278c2ecf20Sopenharmony_ci struct rpm_cc *rcc = r->rpm_cc; 3288c2ecf20Sopenharmony_ci int ret, clk_id = r->rpm_clk_id; 3298c2ecf20Sopenharmony_ci u32 value; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci mutex_lock(&rcc->xo_lock); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci value = rcc->xo_buffer_value & ~(QCOM_RPM_XO_MODE_ON << r->xo_offset); 3348c2ecf20Sopenharmony_ci ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, clk_id, &value, 1); 3358c2ecf20Sopenharmony_ci if (!ret) { 3368c2ecf20Sopenharmony_ci r->enabled = false; 3378c2ecf20Sopenharmony_ci rcc->xo_buffer_value = value; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci mutex_unlock(&rcc->xo_lock); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int clk_rpm_fixed_prepare(struct clk_hw *hw) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct clk_rpm *r = to_clk_rpm(hw); 3468c2ecf20Sopenharmony_ci u32 value = 1; 3478c2ecf20Sopenharmony_ci int ret; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 3508c2ecf20Sopenharmony_ci r->rpm_clk_id, &value, 1); 3518c2ecf20Sopenharmony_ci if (!ret) 3528c2ecf20Sopenharmony_ci r->enabled = true; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic void clk_rpm_fixed_unprepare(struct clk_hw *hw) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct clk_rpm *r = to_clk_rpm(hw); 3608c2ecf20Sopenharmony_ci u32 value = 0; 3618c2ecf20Sopenharmony_ci int ret; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci ret = qcom_rpm_write(r->rpm, QCOM_RPM_ACTIVE_STATE, 3648c2ecf20Sopenharmony_ci r->rpm_clk_id, &value, 1); 3658c2ecf20Sopenharmony_ci if (!ret) 3668c2ecf20Sopenharmony_ci r->enabled = false; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic int clk_rpm_set_rate(struct clk_hw *hw, 3708c2ecf20Sopenharmony_ci unsigned long rate, unsigned long parent_rate) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct clk_rpm *r = to_clk_rpm(hw); 3738c2ecf20Sopenharmony_ci struct clk_rpm *peer = r->peer; 3748c2ecf20Sopenharmony_ci unsigned long active_rate, sleep_rate; 3758c2ecf20Sopenharmony_ci unsigned long this_rate = 0, this_sleep_rate = 0; 3768c2ecf20Sopenharmony_ci unsigned long peer_rate = 0, peer_sleep_rate = 0; 3778c2ecf20Sopenharmony_ci int ret = 0; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci mutex_lock(&rpm_clk_lock); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (!r->enabled) 3828c2ecf20Sopenharmony_ci goto out; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci to_active_sleep(r, rate, &this_rate, &this_sleep_rate); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* Take peer clock's rate into account only if it's enabled. */ 3878c2ecf20Sopenharmony_ci if (peer->enabled) 3888c2ecf20Sopenharmony_ci to_active_sleep(peer, peer->rate, 3898c2ecf20Sopenharmony_ci &peer_rate, &peer_sleep_rate); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci active_rate = max(this_rate, peer_rate); 3928c2ecf20Sopenharmony_ci ret = clk_rpm_set_rate_active(r, active_rate); 3938c2ecf20Sopenharmony_ci if (ret) 3948c2ecf20Sopenharmony_ci goto out; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci sleep_rate = max(this_sleep_rate, peer_sleep_rate); 3978c2ecf20Sopenharmony_ci ret = clk_rpm_set_rate_sleep(r, sleep_rate); 3988c2ecf20Sopenharmony_ci if (ret) 3998c2ecf20Sopenharmony_ci goto out; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci r->rate = rate; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ciout: 4048c2ecf20Sopenharmony_ci mutex_unlock(&rpm_clk_lock); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return ret; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_cistatic long clk_rpm_round_rate(struct clk_hw *hw, unsigned long rate, 4108c2ecf20Sopenharmony_ci unsigned long *parent_rate) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci /* 4138c2ecf20Sopenharmony_ci * RPM handles rate rounding and we don't have a way to 4148c2ecf20Sopenharmony_ci * know what the rate will be, so just return whatever 4158c2ecf20Sopenharmony_ci * rate is requested. 4168c2ecf20Sopenharmony_ci */ 4178c2ecf20Sopenharmony_ci return rate; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, 4218c2ecf20Sopenharmony_ci unsigned long parent_rate) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct clk_rpm *r = to_clk_rpm(hw); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* 4268c2ecf20Sopenharmony_ci * RPM handles rate rounding and we don't have a way to 4278c2ecf20Sopenharmony_ci * know what the rate will be, so just return whatever 4288c2ecf20Sopenharmony_ci * rate was set. 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_ci return r->rate; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic const struct clk_ops clk_rpm_xo_ops = { 4348c2ecf20Sopenharmony_ci .prepare = clk_rpm_xo_prepare, 4358c2ecf20Sopenharmony_ci .unprepare = clk_rpm_xo_unprepare, 4368c2ecf20Sopenharmony_ci}; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic const struct clk_ops clk_rpm_fixed_ops = { 4398c2ecf20Sopenharmony_ci .prepare = clk_rpm_fixed_prepare, 4408c2ecf20Sopenharmony_ci .unprepare = clk_rpm_fixed_unprepare, 4418c2ecf20Sopenharmony_ci .round_rate = clk_rpm_round_rate, 4428c2ecf20Sopenharmony_ci .recalc_rate = clk_rpm_recalc_rate, 4438c2ecf20Sopenharmony_ci}; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic const struct clk_ops clk_rpm_ops = { 4468c2ecf20Sopenharmony_ci .prepare = clk_rpm_prepare, 4478c2ecf20Sopenharmony_ci .unprepare = clk_rpm_unprepare, 4488c2ecf20Sopenharmony_ci .set_rate = clk_rpm_set_rate, 4498c2ecf20Sopenharmony_ci .round_rate = clk_rpm_round_rate, 4508c2ecf20Sopenharmony_ci .recalc_rate = clk_rpm_recalc_rate, 4518c2ecf20Sopenharmony_ci}; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic const struct clk_ops clk_rpm_branch_ops = { 4548c2ecf20Sopenharmony_ci .prepare = clk_rpm_prepare, 4558c2ecf20Sopenharmony_ci .unprepare = clk_rpm_unprepare, 4568c2ecf20Sopenharmony_ci .round_rate = clk_rpm_round_rate, 4578c2ecf20Sopenharmony_ci .recalc_rate = clk_rpm_recalc_rate, 4588c2ecf20Sopenharmony_ci}; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci/* MSM8660/APQ8060 */ 4618c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(msm8660, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); 4628c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(msm8660, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); 4638c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(msm8660, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); 4648c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(msm8660, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); 4658c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(msm8660, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); 4668c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(msm8660, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); 4678c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(msm8660, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); 4688c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(msm8660, smi_clk, smi_a_clk, QCOM_RPM_SMI_CLK); 4698c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(msm8660, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); 4708c2ecf20Sopenharmony_ciDEFINE_CLK_RPM_FIXED(msm8660, pll4_clk, pll4_a_clk, QCOM_RPM_PLL_4, 540672000); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic struct clk_rpm *msm8660_clks[] = { 4738c2ecf20Sopenharmony_ci [RPM_APPS_FABRIC_CLK] = &msm8660_afab_clk, 4748c2ecf20Sopenharmony_ci [RPM_APPS_FABRIC_A_CLK] = &msm8660_afab_a_clk, 4758c2ecf20Sopenharmony_ci [RPM_SYS_FABRIC_CLK] = &msm8660_sfab_clk, 4768c2ecf20Sopenharmony_ci [RPM_SYS_FABRIC_A_CLK] = &msm8660_sfab_a_clk, 4778c2ecf20Sopenharmony_ci [RPM_MM_FABRIC_CLK] = &msm8660_mmfab_clk, 4788c2ecf20Sopenharmony_ci [RPM_MM_FABRIC_A_CLK] = &msm8660_mmfab_a_clk, 4798c2ecf20Sopenharmony_ci [RPM_DAYTONA_FABRIC_CLK] = &msm8660_daytona_clk, 4808c2ecf20Sopenharmony_ci [RPM_DAYTONA_FABRIC_A_CLK] = &msm8660_daytona_a_clk, 4818c2ecf20Sopenharmony_ci [RPM_SFPB_CLK] = &msm8660_sfpb_clk, 4828c2ecf20Sopenharmony_ci [RPM_SFPB_A_CLK] = &msm8660_sfpb_a_clk, 4838c2ecf20Sopenharmony_ci [RPM_CFPB_CLK] = &msm8660_cfpb_clk, 4848c2ecf20Sopenharmony_ci [RPM_CFPB_A_CLK] = &msm8660_cfpb_a_clk, 4858c2ecf20Sopenharmony_ci [RPM_MMFPB_CLK] = &msm8660_mmfpb_clk, 4868c2ecf20Sopenharmony_ci [RPM_MMFPB_A_CLK] = &msm8660_mmfpb_a_clk, 4878c2ecf20Sopenharmony_ci [RPM_SMI_CLK] = &msm8660_smi_clk, 4888c2ecf20Sopenharmony_ci [RPM_SMI_A_CLK] = &msm8660_smi_a_clk, 4898c2ecf20Sopenharmony_ci [RPM_EBI1_CLK] = &msm8660_ebi1_clk, 4908c2ecf20Sopenharmony_ci [RPM_EBI1_A_CLK] = &msm8660_ebi1_a_clk, 4918c2ecf20Sopenharmony_ci [RPM_PLL4_CLK] = &msm8660_pll4_clk, 4928c2ecf20Sopenharmony_ci}; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic const struct rpm_clk_desc rpm_clk_msm8660 = { 4958c2ecf20Sopenharmony_ci .clks = msm8660_clks, 4968c2ecf20Sopenharmony_ci .num_clks = ARRAY_SIZE(msm8660_clks), 4978c2ecf20Sopenharmony_ci}; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci/* apq8064 */ 5008c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(apq8064, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); 5018c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(apq8064, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); 5028c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(apq8064, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); 5038c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(apq8064, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); 5048c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(apq8064, mmfab_clk, mmfab_a_clk, QCOM_RPM_MM_FABRIC_CLK); 5058c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(apq8064, mmfpb_clk, mmfpb_a_clk, QCOM_RPM_MMFPB_CLK); 5068c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(apq8064, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); 5078c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(apq8064, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); 5088c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(apq8064, qdss_clk, qdss_a_clk, QCOM_RPM_QDSS_CLK); 5098c2ecf20Sopenharmony_ciDEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_d0_clk, xo_d0_a_clk, 0); 5108c2ecf20Sopenharmony_ciDEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_d1_clk, xo_d1_a_clk, 8); 5118c2ecf20Sopenharmony_ciDEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_a0_clk, xo_a0_a_clk, 16); 5128c2ecf20Sopenharmony_ciDEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_a1_clk, xo_a1_a_clk, 24); 5138c2ecf20Sopenharmony_ciDEFINE_CLK_RPM_XO_BUFFER(apq8064, xo_a2_clk, xo_a2_a_clk, 28); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic struct clk_rpm *apq8064_clks[] = { 5168c2ecf20Sopenharmony_ci [RPM_APPS_FABRIC_CLK] = &apq8064_afab_clk, 5178c2ecf20Sopenharmony_ci [RPM_APPS_FABRIC_A_CLK] = &apq8064_afab_a_clk, 5188c2ecf20Sopenharmony_ci [RPM_CFPB_CLK] = &apq8064_cfpb_clk, 5198c2ecf20Sopenharmony_ci [RPM_CFPB_A_CLK] = &apq8064_cfpb_a_clk, 5208c2ecf20Sopenharmony_ci [RPM_DAYTONA_FABRIC_CLK] = &apq8064_daytona_clk, 5218c2ecf20Sopenharmony_ci [RPM_DAYTONA_FABRIC_A_CLK] = &apq8064_daytona_a_clk, 5228c2ecf20Sopenharmony_ci [RPM_EBI1_CLK] = &apq8064_ebi1_clk, 5238c2ecf20Sopenharmony_ci [RPM_EBI1_A_CLK] = &apq8064_ebi1_a_clk, 5248c2ecf20Sopenharmony_ci [RPM_MM_FABRIC_CLK] = &apq8064_mmfab_clk, 5258c2ecf20Sopenharmony_ci [RPM_MM_FABRIC_A_CLK] = &apq8064_mmfab_a_clk, 5268c2ecf20Sopenharmony_ci [RPM_MMFPB_CLK] = &apq8064_mmfpb_clk, 5278c2ecf20Sopenharmony_ci [RPM_MMFPB_A_CLK] = &apq8064_mmfpb_a_clk, 5288c2ecf20Sopenharmony_ci [RPM_SYS_FABRIC_CLK] = &apq8064_sfab_clk, 5298c2ecf20Sopenharmony_ci [RPM_SYS_FABRIC_A_CLK] = &apq8064_sfab_a_clk, 5308c2ecf20Sopenharmony_ci [RPM_SFPB_CLK] = &apq8064_sfpb_clk, 5318c2ecf20Sopenharmony_ci [RPM_SFPB_A_CLK] = &apq8064_sfpb_a_clk, 5328c2ecf20Sopenharmony_ci [RPM_QDSS_CLK] = &apq8064_qdss_clk, 5338c2ecf20Sopenharmony_ci [RPM_QDSS_A_CLK] = &apq8064_qdss_a_clk, 5348c2ecf20Sopenharmony_ci [RPM_XO_D0] = &apq8064_xo_d0_clk, 5358c2ecf20Sopenharmony_ci [RPM_XO_D1] = &apq8064_xo_d1_clk, 5368c2ecf20Sopenharmony_ci [RPM_XO_A0] = &apq8064_xo_a0_clk, 5378c2ecf20Sopenharmony_ci [RPM_XO_A1] = &apq8064_xo_a1_clk, 5388c2ecf20Sopenharmony_ci [RPM_XO_A2] = &apq8064_xo_a2_clk, 5398c2ecf20Sopenharmony_ci}; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic const struct rpm_clk_desc rpm_clk_apq8064 = { 5428c2ecf20Sopenharmony_ci .clks = apq8064_clks, 5438c2ecf20Sopenharmony_ci .num_clks = ARRAY_SIZE(apq8064_clks), 5448c2ecf20Sopenharmony_ci}; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/* ipq806x */ 5478c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); 5488c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); 5498c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); 5508c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); 5518c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); 5528c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); 5538c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK); 5548c2ecf20Sopenharmony_ciDEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic struct clk_rpm *ipq806x_clks[] = { 5578c2ecf20Sopenharmony_ci [RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk, 5588c2ecf20Sopenharmony_ci [RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk, 5598c2ecf20Sopenharmony_ci [RPM_CFPB_CLK] = &ipq806x_cfpb_clk, 5608c2ecf20Sopenharmony_ci [RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk, 5618c2ecf20Sopenharmony_ci [RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk, 5628c2ecf20Sopenharmony_ci [RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk, 5638c2ecf20Sopenharmony_ci [RPM_EBI1_CLK] = &ipq806x_ebi1_clk, 5648c2ecf20Sopenharmony_ci [RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk, 5658c2ecf20Sopenharmony_ci [RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk, 5668c2ecf20Sopenharmony_ci [RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk, 5678c2ecf20Sopenharmony_ci [RPM_SFPB_CLK] = &ipq806x_sfpb_clk, 5688c2ecf20Sopenharmony_ci [RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk, 5698c2ecf20Sopenharmony_ci [RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk, 5708c2ecf20Sopenharmony_ci [RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk, 5718c2ecf20Sopenharmony_ci [RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk, 5728c2ecf20Sopenharmony_ci [RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk, 5738c2ecf20Sopenharmony_ci}; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic const struct rpm_clk_desc rpm_clk_ipq806x = { 5768c2ecf20Sopenharmony_ci .clks = ipq806x_clks, 5778c2ecf20Sopenharmony_ci .num_clks = ARRAY_SIZE(ipq806x_clks), 5788c2ecf20Sopenharmony_ci}; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic const struct of_device_id rpm_clk_match_table[] = { 5818c2ecf20Sopenharmony_ci { .compatible = "qcom,rpmcc-msm8660", .data = &rpm_clk_msm8660 }, 5828c2ecf20Sopenharmony_ci { .compatible = "qcom,rpmcc-apq8060", .data = &rpm_clk_msm8660 }, 5838c2ecf20Sopenharmony_ci { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, 5848c2ecf20Sopenharmony_ci { .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x }, 5858c2ecf20Sopenharmony_ci { } 5868c2ecf20Sopenharmony_ci}; 5878c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rpm_clk_match_table); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic struct clk_hw *qcom_rpm_clk_hw_get(struct of_phandle_args *clkspec, 5908c2ecf20Sopenharmony_ci void *data) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci struct rpm_cc *rcc = data; 5938c2ecf20Sopenharmony_ci unsigned int idx = clkspec->args[0]; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (idx >= rcc->num_clks) { 5968c2ecf20Sopenharmony_ci pr_err("%s: invalid index %u\n", __func__, idx); 5978c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci return rcc->clks[idx] ? &rcc->clks[idx]->hw : ERR_PTR(-ENOENT); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic int rpm_clk_probe(struct platform_device *pdev) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct rpm_cc *rcc; 6068c2ecf20Sopenharmony_ci int ret; 6078c2ecf20Sopenharmony_ci size_t num_clks, i; 6088c2ecf20Sopenharmony_ci struct qcom_rpm *rpm; 6098c2ecf20Sopenharmony_ci struct clk_rpm **rpm_clks; 6108c2ecf20Sopenharmony_ci const struct rpm_clk_desc *desc; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci rpm = dev_get_drvdata(pdev->dev.parent); 6138c2ecf20Sopenharmony_ci if (!rpm) { 6148c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); 6158c2ecf20Sopenharmony_ci return -ENODEV; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci desc = of_device_get_match_data(&pdev->dev); 6198c2ecf20Sopenharmony_ci if (!desc) 6208c2ecf20Sopenharmony_ci return -EINVAL; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci rpm_clks = desc->clks; 6238c2ecf20Sopenharmony_ci num_clks = desc->num_clks; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci rcc = devm_kzalloc(&pdev->dev, sizeof(*rcc), GFP_KERNEL); 6268c2ecf20Sopenharmony_ci if (!rcc) 6278c2ecf20Sopenharmony_ci return -ENOMEM; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci rcc->clks = rpm_clks; 6308c2ecf20Sopenharmony_ci rcc->num_clks = num_clks; 6318c2ecf20Sopenharmony_ci mutex_init(&rcc->xo_lock); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci for (i = 0; i < num_clks; i++) { 6348c2ecf20Sopenharmony_ci if (!rpm_clks[i]) 6358c2ecf20Sopenharmony_ci continue; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci rpm_clks[i]->rpm = rpm; 6388c2ecf20Sopenharmony_ci rpm_clks[i]->rpm_cc = rcc; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci ret = clk_rpm_handoff(rpm_clks[i]); 6418c2ecf20Sopenharmony_ci if (ret) 6428c2ecf20Sopenharmony_ci goto err; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci for (i = 0; i < num_clks; i++) { 6468c2ecf20Sopenharmony_ci if (!rpm_clks[i]) 6478c2ecf20Sopenharmony_ci continue; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(&pdev->dev, &rpm_clks[i]->hw); 6508c2ecf20Sopenharmony_ci if (ret) 6518c2ecf20Sopenharmony_ci goto err; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci ret = of_clk_add_hw_provider(pdev->dev.of_node, qcom_rpm_clk_hw_get, 6558c2ecf20Sopenharmony_ci rcc); 6568c2ecf20Sopenharmony_ci if (ret) 6578c2ecf20Sopenharmony_ci goto err; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci return 0; 6608c2ecf20Sopenharmony_cierr: 6618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Error registering RPM Clock driver (%d)\n", ret); 6628c2ecf20Sopenharmony_ci return ret; 6638c2ecf20Sopenharmony_ci} 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_cistatic int rpm_clk_remove(struct platform_device *pdev) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci of_clk_del_provider(pdev->dev.of_node); 6688c2ecf20Sopenharmony_ci return 0; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic struct platform_driver rpm_clk_driver = { 6728c2ecf20Sopenharmony_ci .driver = { 6738c2ecf20Sopenharmony_ci .name = "qcom-clk-rpm", 6748c2ecf20Sopenharmony_ci .of_match_table = rpm_clk_match_table, 6758c2ecf20Sopenharmony_ci }, 6768c2ecf20Sopenharmony_ci .probe = rpm_clk_probe, 6778c2ecf20Sopenharmony_ci .remove = rpm_clk_remove, 6788c2ecf20Sopenharmony_ci}; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic int __init rpm_clk_init(void) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci return platform_driver_register(&rpm_clk_driver); 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_cicore_initcall(rpm_clk_init); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic void __exit rpm_clk_exit(void) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci platform_driver_unregister(&rpm_clk_driver); 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_cimodule_exit(rpm_clk_exit); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm RPM Clock Controller Driver"); 6938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 6948c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:qcom-clk-rpm"); 695