18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
78c2ecf20Sopenharmony_ci#include <linux/err.h>
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/of.h>
118c2ecf20Sopenharmony_ci#include <linux/of_device.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <soc/qcom/cmd-db.h>
148c2ecf20Sopenharmony_ci#include <soc/qcom/rpmh.h>
158c2ecf20Sopenharmony_ci#include <soc/qcom/tcs.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <dt-bindings/clock/qcom,rpmh.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define CLK_RPMH_ARC_EN_OFFSET		0
208c2ecf20Sopenharmony_ci#define CLK_RPMH_VRM_EN_OFFSET		4
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/**
238c2ecf20Sopenharmony_ci * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager(BCM)
248c2ecf20Sopenharmony_ci * @unit: divisor used to convert Hz value to an RPMh msg
258c2ecf20Sopenharmony_ci * @width: multiplier used to convert Hz value to an RPMh msg
268c2ecf20Sopenharmony_ci * @vcd: virtual clock domain that this bcm belongs to
278c2ecf20Sopenharmony_ci * @reserved: reserved to pad the struct
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_cistruct bcm_db {
308c2ecf20Sopenharmony_ci	__le32 unit;
318c2ecf20Sopenharmony_ci	__le16 width;
328c2ecf20Sopenharmony_ci	u8 vcd;
338c2ecf20Sopenharmony_ci	u8 reserved;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/**
378c2ecf20Sopenharmony_ci * struct clk_rpmh - individual rpmh clock data structure
388c2ecf20Sopenharmony_ci * @hw:			handle between common and hardware-specific interfaces
398c2ecf20Sopenharmony_ci * @res_name:		resource name for the rpmh clock
408c2ecf20Sopenharmony_ci * @div:		clock divider to compute the clock rate
418c2ecf20Sopenharmony_ci * @res_addr:		base address of the rpmh resource within the RPMh
428c2ecf20Sopenharmony_ci * @res_on_val:		rpmh clock enable value
438c2ecf20Sopenharmony_ci * @state:		rpmh clock requested state
448c2ecf20Sopenharmony_ci * @aggr_state:		rpmh clock aggregated state
458c2ecf20Sopenharmony_ci * @last_sent_aggr_state: rpmh clock last aggr state sent to RPMh
468c2ecf20Sopenharmony_ci * @valid_state_mask:	mask to determine the state of the rpmh clock
478c2ecf20Sopenharmony_ci * @unit:		divisor to convert rate to rpmh msg in magnitudes of Khz
488c2ecf20Sopenharmony_ci * @dev:		device to which it is attached
498c2ecf20Sopenharmony_ci * @peer:		pointer to the clock rpmh sibling
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_cistruct clk_rpmh {
528c2ecf20Sopenharmony_ci	struct clk_hw hw;
538c2ecf20Sopenharmony_ci	const char *res_name;
548c2ecf20Sopenharmony_ci	u8 div;
558c2ecf20Sopenharmony_ci	u32 res_addr;
568c2ecf20Sopenharmony_ci	u32 res_on_val;
578c2ecf20Sopenharmony_ci	u32 state;
588c2ecf20Sopenharmony_ci	u32 aggr_state;
598c2ecf20Sopenharmony_ci	u32 last_sent_aggr_state;
608c2ecf20Sopenharmony_ci	u32 valid_state_mask;
618c2ecf20Sopenharmony_ci	u32 unit;
628c2ecf20Sopenharmony_ci	struct device *dev;
638c2ecf20Sopenharmony_ci	struct clk_rpmh *peer;
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistruct clk_rpmh_desc {
678c2ecf20Sopenharmony_ci	struct clk_hw **clks;
688c2ecf20Sopenharmony_ci	size_t num_clks;
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(rpmh_clk_lock);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define __DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name,	\
748c2ecf20Sopenharmony_ci			  _res_en_offset, _res_on, _div)		\
758c2ecf20Sopenharmony_ci	static struct clk_rpmh _platform##_##_name_active;		\
768c2ecf20Sopenharmony_ci	static struct clk_rpmh _platform##_##_name = {			\
778c2ecf20Sopenharmony_ci		.res_name = _res_name,					\
788c2ecf20Sopenharmony_ci		.res_addr = _res_en_offset,				\
798c2ecf20Sopenharmony_ci		.res_on_val = _res_on,					\
808c2ecf20Sopenharmony_ci		.div = _div,						\
818c2ecf20Sopenharmony_ci		.peer = &_platform##_##_name_active,			\
828c2ecf20Sopenharmony_ci		.valid_state_mask = (BIT(RPMH_WAKE_ONLY_STATE) |	\
838c2ecf20Sopenharmony_ci				      BIT(RPMH_ACTIVE_ONLY_STATE) |	\
848c2ecf20Sopenharmony_ci				      BIT(RPMH_SLEEP_STATE)),		\
858c2ecf20Sopenharmony_ci		.hw.init = &(struct clk_init_data){			\
868c2ecf20Sopenharmony_ci			.ops = &clk_rpmh_ops,				\
878c2ecf20Sopenharmony_ci			.name = #_name,					\
888c2ecf20Sopenharmony_ci			.parent_data =  &(const struct clk_parent_data){ \
898c2ecf20Sopenharmony_ci					.fw_name = "xo",		\
908c2ecf20Sopenharmony_ci					.name = "xo_board",		\
918c2ecf20Sopenharmony_ci			},						\
928c2ecf20Sopenharmony_ci			.num_parents = 1,				\
938c2ecf20Sopenharmony_ci		},							\
948c2ecf20Sopenharmony_ci	};								\
958c2ecf20Sopenharmony_ci	static struct clk_rpmh _platform##_##_name_active = {		\
968c2ecf20Sopenharmony_ci		.res_name = _res_name,					\
978c2ecf20Sopenharmony_ci		.res_addr = _res_en_offset,				\
988c2ecf20Sopenharmony_ci		.res_on_val = _res_on,					\
998c2ecf20Sopenharmony_ci		.div = _div,						\
1008c2ecf20Sopenharmony_ci		.peer = &_platform##_##_name,				\
1018c2ecf20Sopenharmony_ci		.valid_state_mask = (BIT(RPMH_WAKE_ONLY_STATE) |	\
1028c2ecf20Sopenharmony_ci					BIT(RPMH_ACTIVE_ONLY_STATE)),	\
1038c2ecf20Sopenharmony_ci		.hw.init = &(struct clk_init_data){			\
1048c2ecf20Sopenharmony_ci			.ops = &clk_rpmh_ops,				\
1058c2ecf20Sopenharmony_ci			.name = #_name_active,				\
1068c2ecf20Sopenharmony_ci			.parent_data =  &(const struct clk_parent_data){ \
1078c2ecf20Sopenharmony_ci					.fw_name = "xo",		\
1088c2ecf20Sopenharmony_ci					.name = "xo_board",		\
1098c2ecf20Sopenharmony_ci			},						\
1108c2ecf20Sopenharmony_ci			.num_parents = 1,				\
1118c2ecf20Sopenharmony_ci		},							\
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci#define DEFINE_CLK_RPMH_ARC(_platform, _name, _name_active, _res_name,	\
1158c2ecf20Sopenharmony_ci			    _res_on, _div)				\
1168c2ecf20Sopenharmony_ci	__DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name,	\
1178c2ecf20Sopenharmony_ci			  CLK_RPMH_ARC_EN_OFFSET, _res_on, _div)
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci#define DEFINE_CLK_RPMH_VRM(_platform, _name, _name_active, _res_name,	\
1208c2ecf20Sopenharmony_ci				_div)					\
1218c2ecf20Sopenharmony_ci	__DEFINE_CLK_RPMH(_platform, _name, _name_active, _res_name,	\
1228c2ecf20Sopenharmony_ci			  CLK_RPMH_VRM_EN_OFFSET, 1, _div)
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci#define DEFINE_CLK_RPMH_BCM(_platform, _name, _res_name)		\
1258c2ecf20Sopenharmony_ci	static struct clk_rpmh _platform##_##_name = {			\
1268c2ecf20Sopenharmony_ci		.res_name = _res_name,					\
1278c2ecf20Sopenharmony_ci		.valid_state_mask = BIT(RPMH_ACTIVE_ONLY_STATE),	\
1288c2ecf20Sopenharmony_ci		.div = 1,						\
1298c2ecf20Sopenharmony_ci		.hw.init = &(struct clk_init_data){			\
1308c2ecf20Sopenharmony_ci			.ops = &clk_rpmh_bcm_ops,			\
1318c2ecf20Sopenharmony_ci			.name = #_name,					\
1328c2ecf20Sopenharmony_ci		},							\
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic inline struct clk_rpmh *to_clk_rpmh(struct clk_hw *_hw)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	return container_of(_hw, struct clk_rpmh, hw);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic inline bool has_state_changed(struct clk_rpmh *c, u32 state)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	return (c->last_sent_aggr_state & BIT(state))
1438c2ecf20Sopenharmony_ci		!= (c->aggr_state & BIT(state));
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int clk_rpmh_send(struct clk_rpmh *c, enum rpmh_state state,
1478c2ecf20Sopenharmony_ci			 struct tcs_cmd *cmd, bool wait)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	if (wait)
1508c2ecf20Sopenharmony_ci		return rpmh_write(c->dev, state, cmd, 1);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return rpmh_write_async(c->dev, state, cmd, 1);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int clk_rpmh_send_aggregate_command(struct clk_rpmh *c)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct tcs_cmd cmd = { 0 };
1588c2ecf20Sopenharmony_ci	u32 cmd_state, on_val;
1598c2ecf20Sopenharmony_ci	enum rpmh_state state = RPMH_SLEEP_STATE;
1608c2ecf20Sopenharmony_ci	int ret;
1618c2ecf20Sopenharmony_ci	bool wait;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	cmd.addr = c->res_addr;
1648c2ecf20Sopenharmony_ci	cmd_state = c->aggr_state;
1658c2ecf20Sopenharmony_ci	on_val = c->res_on_val;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	for (; state <= RPMH_ACTIVE_ONLY_STATE; state++) {
1688c2ecf20Sopenharmony_ci		if (has_state_changed(c, state)) {
1698c2ecf20Sopenharmony_ci			if (cmd_state & BIT(state))
1708c2ecf20Sopenharmony_ci				cmd.data = on_val;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci			wait = cmd_state && state == RPMH_ACTIVE_ONLY_STATE;
1738c2ecf20Sopenharmony_ci			ret = clk_rpmh_send(c, state, &cmd, wait);
1748c2ecf20Sopenharmony_ci			if (ret) {
1758c2ecf20Sopenharmony_ci				dev_err(c->dev, "set %s state of %s failed: (%d)\n",
1768c2ecf20Sopenharmony_ci					!state ? "sleep" :
1778c2ecf20Sopenharmony_ci					state == RPMH_WAKE_ONLY_STATE	?
1788c2ecf20Sopenharmony_ci					"wake" : "active", c->res_name, ret);
1798c2ecf20Sopenharmony_ci				return ret;
1808c2ecf20Sopenharmony_ci			}
1818c2ecf20Sopenharmony_ci		}
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	c->last_sent_aggr_state = c->aggr_state;
1858c2ecf20Sopenharmony_ci	c->peer->last_sent_aggr_state =  c->last_sent_aggr_state;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/*
1918c2ecf20Sopenharmony_ci * Update state and aggregate state values based on enable value.
1928c2ecf20Sopenharmony_ci */
1938c2ecf20Sopenharmony_cistatic int clk_rpmh_aggregate_state_send_command(struct clk_rpmh *c,
1948c2ecf20Sopenharmony_ci						bool enable)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	int ret;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/* Nothing required to be done if already off or on */
1998c2ecf20Sopenharmony_ci	if (enable == c->state)
2008c2ecf20Sopenharmony_ci		return 0;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	c->state = enable ? c->valid_state_mask : 0;
2038c2ecf20Sopenharmony_ci	c->aggr_state = c->state | c->peer->state;
2048c2ecf20Sopenharmony_ci	c->peer->aggr_state = c->aggr_state;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	ret = clk_rpmh_send_aggregate_command(c);
2078c2ecf20Sopenharmony_ci	if (!ret)
2088c2ecf20Sopenharmony_ci		return 0;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (ret && enable)
2118c2ecf20Sopenharmony_ci		c->state = 0;
2128c2ecf20Sopenharmony_ci	else if (ret)
2138c2ecf20Sopenharmony_ci		c->state = c->valid_state_mask;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	WARN(1, "clk: %s failed to %s\n", c->res_name,
2168c2ecf20Sopenharmony_ci	     enable ? "enable" : "disable");
2178c2ecf20Sopenharmony_ci	return ret;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic int clk_rpmh_prepare(struct clk_hw *hw)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct clk_rpmh *c = to_clk_rpmh(hw);
2238c2ecf20Sopenharmony_ci	int ret = 0;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	mutex_lock(&rpmh_clk_lock);
2268c2ecf20Sopenharmony_ci	ret = clk_rpmh_aggregate_state_send_command(c, true);
2278c2ecf20Sopenharmony_ci	mutex_unlock(&rpmh_clk_lock);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	return ret;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic void clk_rpmh_unprepare(struct clk_hw *hw)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct clk_rpmh *c = to_clk_rpmh(hw);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	mutex_lock(&rpmh_clk_lock);
2378c2ecf20Sopenharmony_ci	clk_rpmh_aggregate_state_send_command(c, false);
2388c2ecf20Sopenharmony_ci	mutex_unlock(&rpmh_clk_lock);
2398c2ecf20Sopenharmony_ci};
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic unsigned long clk_rpmh_recalc_rate(struct clk_hw *hw,
2428c2ecf20Sopenharmony_ci					unsigned long prate)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct clk_rpmh *r = to_clk_rpmh(hw);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/*
2478c2ecf20Sopenharmony_ci	 * RPMh clocks have a fixed rate. Return static rate.
2488c2ecf20Sopenharmony_ci	 */
2498c2ecf20Sopenharmony_ci	return prate / r->div;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic const struct clk_ops clk_rpmh_ops = {
2538c2ecf20Sopenharmony_ci	.prepare	= clk_rpmh_prepare,
2548c2ecf20Sopenharmony_ci	.unprepare	= clk_rpmh_unprepare,
2558c2ecf20Sopenharmony_ci	.recalc_rate	= clk_rpmh_recalc_rate,
2568c2ecf20Sopenharmony_ci};
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int clk_rpmh_bcm_send_cmd(struct clk_rpmh *c, bool enable)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct tcs_cmd cmd = { 0 };
2618c2ecf20Sopenharmony_ci	u32 cmd_state;
2628c2ecf20Sopenharmony_ci	int ret = 0;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	mutex_lock(&rpmh_clk_lock);
2658c2ecf20Sopenharmony_ci	if (enable) {
2668c2ecf20Sopenharmony_ci		cmd_state = 1;
2678c2ecf20Sopenharmony_ci		if (c->aggr_state)
2688c2ecf20Sopenharmony_ci			cmd_state = c->aggr_state;
2698c2ecf20Sopenharmony_ci	} else {
2708c2ecf20Sopenharmony_ci		cmd_state = 0;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (c->last_sent_aggr_state != cmd_state) {
2748c2ecf20Sopenharmony_ci		cmd.addr = c->res_addr;
2758c2ecf20Sopenharmony_ci		cmd.data = BCM_TCS_CMD(1, enable, 0, cmd_state);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		ret = clk_rpmh_send(c, RPMH_ACTIVE_ONLY_STATE, &cmd, enable);
2788c2ecf20Sopenharmony_ci		if (ret) {
2798c2ecf20Sopenharmony_ci			dev_err(c->dev, "set active state of %s failed: (%d)\n",
2808c2ecf20Sopenharmony_ci				c->res_name, ret);
2818c2ecf20Sopenharmony_ci		} else {
2828c2ecf20Sopenharmony_ci			c->last_sent_aggr_state = cmd_state;
2838c2ecf20Sopenharmony_ci		}
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	mutex_unlock(&rpmh_clk_lock);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return ret;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic int clk_rpmh_bcm_prepare(struct clk_hw *hw)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct clk_rpmh *c = to_clk_rpmh(hw);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return clk_rpmh_bcm_send_cmd(c, true);
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic void clk_rpmh_bcm_unprepare(struct clk_hw *hw)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct clk_rpmh *c = to_clk_rpmh(hw);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	clk_rpmh_bcm_send_cmd(c, false);
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic int clk_rpmh_bcm_set_rate(struct clk_hw *hw, unsigned long rate,
3068c2ecf20Sopenharmony_ci				 unsigned long parent_rate)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct clk_rpmh *c = to_clk_rpmh(hw);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	c->aggr_state = rate / c->unit;
3118c2ecf20Sopenharmony_ci	/*
3128c2ecf20Sopenharmony_ci	 * Since any non-zero value sent to hw would result in enabling the
3138c2ecf20Sopenharmony_ci	 * clock, only send the value if the clock has already been prepared.
3148c2ecf20Sopenharmony_ci	 */
3158c2ecf20Sopenharmony_ci	if (clk_hw_is_prepared(hw))
3168c2ecf20Sopenharmony_ci		clk_rpmh_bcm_send_cmd(c, true);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic long clk_rpmh_round_rate(struct clk_hw *hw, unsigned long rate,
3228c2ecf20Sopenharmony_ci				unsigned long *parent_rate)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	return rate;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic unsigned long clk_rpmh_bcm_recalc_rate(struct clk_hw *hw,
3288c2ecf20Sopenharmony_ci					unsigned long prate)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct clk_rpmh *c = to_clk_rpmh(hw);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return c->aggr_state * c->unit;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic const struct clk_ops clk_rpmh_bcm_ops = {
3368c2ecf20Sopenharmony_ci	.prepare	= clk_rpmh_bcm_prepare,
3378c2ecf20Sopenharmony_ci	.unprepare	= clk_rpmh_bcm_unprepare,
3388c2ecf20Sopenharmony_ci	.set_rate	= clk_rpmh_bcm_set_rate,
3398c2ecf20Sopenharmony_ci	.round_rate	= clk_rpmh_round_rate,
3408c2ecf20Sopenharmony_ci	.recalc_rate	= clk_rpmh_bcm_recalc_rate,
3418c2ecf20Sopenharmony_ci};
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci/* Resource name must match resource id present in cmd-db */
3448c2ecf20Sopenharmony_ciDEFINE_CLK_RPMH_ARC(sdm845, bi_tcxo, bi_tcxo_ao, "xo.lvl", 0x3, 2);
3458c2ecf20Sopenharmony_ciDEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk2, ln_bb_clk2_ao, "lnbclka2", 2);
3468c2ecf20Sopenharmony_ciDEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk3, ln_bb_clk3_ao, "lnbclka3", 2);
3478c2ecf20Sopenharmony_ciDEFINE_CLK_RPMH_VRM(sdm845, rf_clk1, rf_clk1_ao, "rfclka1", 1);
3488c2ecf20Sopenharmony_ciDEFINE_CLK_RPMH_VRM(sdm845, rf_clk2, rf_clk2_ao, "rfclka2", 1);
3498c2ecf20Sopenharmony_ciDEFINE_CLK_RPMH_VRM(sdm845, rf_clk3, rf_clk3_ao, "rfclka3", 1);
3508c2ecf20Sopenharmony_ciDEFINE_CLK_RPMH_VRM(sm8150, rf_clk3, rf_clk3_ao, "rfclka3", 1);
3518c2ecf20Sopenharmony_ciDEFINE_CLK_RPMH_BCM(sdm845, ipa, "IP0");
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic struct clk_hw *sdm845_rpmh_clocks[] = {
3548c2ecf20Sopenharmony_ci	[RPMH_CXO_CLK]		= &sdm845_bi_tcxo.hw,
3558c2ecf20Sopenharmony_ci	[RPMH_CXO_CLK_A]	= &sdm845_bi_tcxo_ao.hw,
3568c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK2]	= &sdm845_ln_bb_clk2.hw,
3578c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK2_A]	= &sdm845_ln_bb_clk2_ao.hw,
3588c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK3]	= &sdm845_ln_bb_clk3.hw,
3598c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK3_A]	= &sdm845_ln_bb_clk3_ao.hw,
3608c2ecf20Sopenharmony_ci	[RPMH_RF_CLK1]		= &sdm845_rf_clk1.hw,
3618c2ecf20Sopenharmony_ci	[RPMH_RF_CLK1_A]	= &sdm845_rf_clk1_ao.hw,
3628c2ecf20Sopenharmony_ci	[RPMH_RF_CLK2]		= &sdm845_rf_clk2.hw,
3638c2ecf20Sopenharmony_ci	[RPMH_RF_CLK2_A]	= &sdm845_rf_clk2_ao.hw,
3648c2ecf20Sopenharmony_ci	[RPMH_RF_CLK3]		= &sdm845_rf_clk3.hw,
3658c2ecf20Sopenharmony_ci	[RPMH_RF_CLK3_A]	= &sdm845_rf_clk3_ao.hw,
3668c2ecf20Sopenharmony_ci	[RPMH_IPA_CLK]		= &sdm845_ipa.hw,
3678c2ecf20Sopenharmony_ci};
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic const struct clk_rpmh_desc clk_rpmh_sdm845 = {
3708c2ecf20Sopenharmony_ci	.clks = sdm845_rpmh_clocks,
3718c2ecf20Sopenharmony_ci	.num_clks = ARRAY_SIZE(sdm845_rpmh_clocks),
3728c2ecf20Sopenharmony_ci};
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic struct clk_hw *sm8150_rpmh_clocks[] = {
3758c2ecf20Sopenharmony_ci	[RPMH_CXO_CLK]		= &sdm845_bi_tcxo.hw,
3768c2ecf20Sopenharmony_ci	[RPMH_CXO_CLK_A]	= &sdm845_bi_tcxo_ao.hw,
3778c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK2]	= &sdm845_ln_bb_clk2.hw,
3788c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK2_A]	= &sdm845_ln_bb_clk2_ao.hw,
3798c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK3]	= &sdm845_ln_bb_clk3.hw,
3808c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK3_A]	= &sdm845_ln_bb_clk3_ao.hw,
3818c2ecf20Sopenharmony_ci	[RPMH_RF_CLK1]		= &sdm845_rf_clk1.hw,
3828c2ecf20Sopenharmony_ci	[RPMH_RF_CLK1_A]	= &sdm845_rf_clk1_ao.hw,
3838c2ecf20Sopenharmony_ci	[RPMH_RF_CLK2]		= &sdm845_rf_clk2.hw,
3848c2ecf20Sopenharmony_ci	[RPMH_RF_CLK2_A]	= &sdm845_rf_clk2_ao.hw,
3858c2ecf20Sopenharmony_ci	[RPMH_RF_CLK3]		= &sdm845_rf_clk3.hw,
3868c2ecf20Sopenharmony_ci	[RPMH_RF_CLK3_A]	= &sdm845_rf_clk3_ao.hw,
3878c2ecf20Sopenharmony_ci};
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic const struct clk_rpmh_desc clk_rpmh_sm8150 = {
3908c2ecf20Sopenharmony_ci	.clks = sm8150_rpmh_clocks,
3918c2ecf20Sopenharmony_ci	.num_clks = ARRAY_SIZE(sm8150_rpmh_clocks),
3928c2ecf20Sopenharmony_ci};
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic struct clk_hw *sc7180_rpmh_clocks[] = {
3958c2ecf20Sopenharmony_ci	[RPMH_CXO_CLK]		= &sdm845_bi_tcxo.hw,
3968c2ecf20Sopenharmony_ci	[RPMH_CXO_CLK_A]	= &sdm845_bi_tcxo_ao.hw,
3978c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK2]	= &sdm845_ln_bb_clk2.hw,
3988c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK2_A]	= &sdm845_ln_bb_clk2_ao.hw,
3998c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK3]	= &sdm845_ln_bb_clk3.hw,
4008c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK3_A]	= &sdm845_ln_bb_clk3_ao.hw,
4018c2ecf20Sopenharmony_ci	[RPMH_RF_CLK1]		= &sdm845_rf_clk1.hw,
4028c2ecf20Sopenharmony_ci	[RPMH_RF_CLK1_A]	= &sdm845_rf_clk1_ao.hw,
4038c2ecf20Sopenharmony_ci	[RPMH_RF_CLK2]		= &sdm845_rf_clk2.hw,
4048c2ecf20Sopenharmony_ci	[RPMH_RF_CLK2_A]	= &sdm845_rf_clk2_ao.hw,
4058c2ecf20Sopenharmony_ci	[RPMH_IPA_CLK]		= &sdm845_ipa.hw,
4068c2ecf20Sopenharmony_ci};
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic const struct clk_rpmh_desc clk_rpmh_sc7180 = {
4098c2ecf20Sopenharmony_ci	.clks = sc7180_rpmh_clocks,
4108c2ecf20Sopenharmony_ci	.num_clks = ARRAY_SIZE(sc7180_rpmh_clocks),
4118c2ecf20Sopenharmony_ci};
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ciDEFINE_CLK_RPMH_VRM(sm8250, ln_bb_clk1, ln_bb_clk1_ao, "lnbclka1", 2);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cistatic struct clk_hw *sm8250_rpmh_clocks[] = {
4168c2ecf20Sopenharmony_ci	[RPMH_CXO_CLK]		= &sdm845_bi_tcxo.hw,
4178c2ecf20Sopenharmony_ci	[RPMH_CXO_CLK_A]	= &sdm845_bi_tcxo_ao.hw,
4188c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK1]	= &sm8250_ln_bb_clk1.hw,
4198c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK1_A]	= &sm8250_ln_bb_clk1_ao.hw,
4208c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK2]	= &sdm845_ln_bb_clk2.hw,
4218c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK2_A]	= &sdm845_ln_bb_clk2_ao.hw,
4228c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK3]	= &sdm845_ln_bb_clk3.hw,
4238c2ecf20Sopenharmony_ci	[RPMH_LN_BB_CLK3_A]	= &sdm845_ln_bb_clk3_ao.hw,
4248c2ecf20Sopenharmony_ci	[RPMH_RF_CLK1]		= &sdm845_rf_clk1.hw,
4258c2ecf20Sopenharmony_ci	[RPMH_RF_CLK1_A]	= &sdm845_rf_clk1_ao.hw,
4268c2ecf20Sopenharmony_ci	[RPMH_RF_CLK3]		= &sdm845_rf_clk3.hw,
4278c2ecf20Sopenharmony_ci	[RPMH_RF_CLK3_A]	= &sdm845_rf_clk3_ao.hw,
4288c2ecf20Sopenharmony_ci};
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic const struct clk_rpmh_desc clk_rpmh_sm8250 = {
4318c2ecf20Sopenharmony_ci	.clks = sm8250_rpmh_clocks,
4328c2ecf20Sopenharmony_ci	.num_clks = ARRAY_SIZE(sm8250_rpmh_clocks),
4338c2ecf20Sopenharmony_ci};
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic struct clk_hw *of_clk_rpmh_hw_get(struct of_phandle_args *clkspec,
4368c2ecf20Sopenharmony_ci					 void *data)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct clk_rpmh_desc *rpmh = data;
4398c2ecf20Sopenharmony_ci	unsigned int idx = clkspec->args[0];
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (idx >= rpmh->num_clks) {
4428c2ecf20Sopenharmony_ci		pr_err("%s: invalid index %u\n", __func__, idx);
4438c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	return rpmh->clks[idx];
4478c2ecf20Sopenharmony_ci}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_cistatic int clk_rpmh_probe(struct platform_device *pdev)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	struct clk_hw **hw_clks;
4528c2ecf20Sopenharmony_ci	struct clk_rpmh *rpmh_clk;
4538c2ecf20Sopenharmony_ci	const struct clk_rpmh_desc *desc;
4548c2ecf20Sopenharmony_ci	int ret, i;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	desc = of_device_get_match_data(&pdev->dev);
4578c2ecf20Sopenharmony_ci	if (!desc)
4588c2ecf20Sopenharmony_ci		return -ENODEV;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	hw_clks = desc->clks;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	for (i = 0; i < desc->num_clks; i++) {
4638c2ecf20Sopenharmony_ci		const char *name;
4648c2ecf20Sopenharmony_ci		u32 res_addr;
4658c2ecf20Sopenharmony_ci		size_t aux_data_len;
4668c2ecf20Sopenharmony_ci		const struct bcm_db *data;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci		if (!hw_clks[i])
4698c2ecf20Sopenharmony_ci			continue;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		name = hw_clks[i]->init->name;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		rpmh_clk = to_clk_rpmh(hw_clks[i]);
4748c2ecf20Sopenharmony_ci		res_addr = cmd_db_read_addr(rpmh_clk->res_name);
4758c2ecf20Sopenharmony_ci		if (!res_addr) {
4768c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "missing RPMh resource address for %s\n",
4778c2ecf20Sopenharmony_ci				rpmh_clk->res_name);
4788c2ecf20Sopenharmony_ci			return -ENODEV;
4798c2ecf20Sopenharmony_ci		}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci		data = cmd_db_read_aux_data(rpmh_clk->res_name, &aux_data_len);
4828c2ecf20Sopenharmony_ci		if (IS_ERR(data)) {
4838c2ecf20Sopenharmony_ci			ret = PTR_ERR(data);
4848c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
4858c2ecf20Sopenharmony_ci				"error reading RPMh aux data for %s (%d)\n",
4868c2ecf20Sopenharmony_ci				rpmh_clk->res_name, ret);
4878c2ecf20Sopenharmony_ci			return ret;
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		/* Convert unit from Khz to Hz */
4918c2ecf20Sopenharmony_ci		if (aux_data_len == sizeof(*data))
4928c2ecf20Sopenharmony_ci			rpmh_clk->unit = le32_to_cpu(data->unit) * 1000ULL;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		rpmh_clk->res_addr += res_addr;
4958c2ecf20Sopenharmony_ci		rpmh_clk->dev = &pdev->dev;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci		ret = devm_clk_hw_register(&pdev->dev, hw_clks[i]);
4988c2ecf20Sopenharmony_ci		if (ret) {
4998c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "failed to register %s\n", name);
5008c2ecf20Sopenharmony_ci			return ret;
5018c2ecf20Sopenharmony_ci		}
5028c2ecf20Sopenharmony_ci	}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/* typecast to silence compiler warning */
5058c2ecf20Sopenharmony_ci	ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_rpmh_hw_get,
5068c2ecf20Sopenharmony_ci					  (void *)desc);
5078c2ecf20Sopenharmony_ci	if (ret) {
5088c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to add clock provider\n");
5098c2ecf20Sopenharmony_ci		return ret;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "Registered RPMh clocks\n");
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	return 0;
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cistatic const struct of_device_id clk_rpmh_match_table[] = {
5188c2ecf20Sopenharmony_ci	{ .compatible = "qcom,sc7180-rpmh-clk", .data = &clk_rpmh_sc7180},
5198c2ecf20Sopenharmony_ci	{ .compatible = "qcom,sdm845-rpmh-clk", .data = &clk_rpmh_sdm845},
5208c2ecf20Sopenharmony_ci	{ .compatible = "qcom,sm8150-rpmh-clk", .data = &clk_rpmh_sm8150},
5218c2ecf20Sopenharmony_ci	{ .compatible = "qcom,sm8250-rpmh-clk", .data = &clk_rpmh_sm8250},
5228c2ecf20Sopenharmony_ci	{ }
5238c2ecf20Sopenharmony_ci};
5248c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, clk_rpmh_match_table);
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_cistatic struct platform_driver clk_rpmh_driver = {
5278c2ecf20Sopenharmony_ci	.probe		= clk_rpmh_probe,
5288c2ecf20Sopenharmony_ci	.driver		= {
5298c2ecf20Sopenharmony_ci		.name	= "clk-rpmh",
5308c2ecf20Sopenharmony_ci		.of_match_table = clk_rpmh_match_table,
5318c2ecf20Sopenharmony_ci	},
5328c2ecf20Sopenharmony_ci};
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic int __init clk_rpmh_init(void)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	return platform_driver_register(&clk_rpmh_driver);
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_cicore_initcall(clk_rpmh_init);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic void __exit clk_rpmh_exit(void)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	platform_driver_unregister(&clk_rpmh_driver);
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_cimodule_exit(clk_rpmh_exit);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QCOM RPMh Clock Driver");
5478c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
548