162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SCI Clock driver for keystone based devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/
662306a36Sopenharmony_ci *	Tero Kristo <t-kristo@ti.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/clk-provider.h>
962306a36Sopenharmony_ci#include <linux/err.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/soc/ti/ti_sci_protocol.h>
1662306a36Sopenharmony_ci#include <linux/bsearch.h>
1762306a36Sopenharmony_ci#include <linux/list_sort.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define SCI_CLK_SSC_ENABLE		BIT(0)
2062306a36Sopenharmony_ci#define SCI_CLK_ALLOW_FREQ_CHANGE	BIT(1)
2162306a36Sopenharmony_ci#define SCI_CLK_INPUT_TERMINATION	BIT(2)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/**
2462306a36Sopenharmony_ci * struct sci_clk_provider - TI SCI clock provider representation
2562306a36Sopenharmony_ci * @sci: Handle to the System Control Interface protocol handler
2662306a36Sopenharmony_ci * @ops: Pointer to the SCI ops to be used by the clocks
2762306a36Sopenharmony_ci * @dev: Device pointer for the clock provider
2862306a36Sopenharmony_ci * @clocks: Clocks array for this device
2962306a36Sopenharmony_ci * @num_clocks: Total number of clocks for this provider
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_cistruct sci_clk_provider {
3262306a36Sopenharmony_ci	const struct ti_sci_handle *sci;
3362306a36Sopenharmony_ci	const struct ti_sci_clk_ops *ops;
3462306a36Sopenharmony_ci	struct device *dev;
3562306a36Sopenharmony_ci	struct sci_clk **clocks;
3662306a36Sopenharmony_ci	int num_clocks;
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/**
4062306a36Sopenharmony_ci * struct sci_clk - TI SCI clock representation
4162306a36Sopenharmony_ci * @hw:		 Hardware clock cookie for common clock framework
4262306a36Sopenharmony_ci * @dev_id:	 Device index
4362306a36Sopenharmony_ci * @clk_id:	 Clock index
4462306a36Sopenharmony_ci * @num_parents: Number of parents for this clock
4562306a36Sopenharmony_ci * @provider:	 Master clock provider
4662306a36Sopenharmony_ci * @flags:	 Flags for the clock
4762306a36Sopenharmony_ci * @node:	 Link for handling clocks probed via DT
4862306a36Sopenharmony_ci * @cached_req:	 Cached requested freq for determine rate calls
4962306a36Sopenharmony_ci * @cached_res:	 Cached result freq for determine rate calls
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_cistruct sci_clk {
5262306a36Sopenharmony_ci	struct clk_hw hw;
5362306a36Sopenharmony_ci	u16 dev_id;
5462306a36Sopenharmony_ci	u32 clk_id;
5562306a36Sopenharmony_ci	u32 num_parents;
5662306a36Sopenharmony_ci	struct sci_clk_provider *provider;
5762306a36Sopenharmony_ci	u8 flags;
5862306a36Sopenharmony_ci	struct list_head node;
5962306a36Sopenharmony_ci	unsigned long cached_req;
6062306a36Sopenharmony_ci	unsigned long cached_res;
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define to_sci_clk(_hw) container_of(_hw, struct sci_clk, hw)
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/**
6662306a36Sopenharmony_ci * sci_clk_prepare - Prepare (enable) a TI SCI clock
6762306a36Sopenharmony_ci * @hw: clock to prepare
6862306a36Sopenharmony_ci *
6962306a36Sopenharmony_ci * Prepares a clock to be actively used. Returns the SCI protocol status.
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_cistatic int sci_clk_prepare(struct clk_hw *hw)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct sci_clk *clk = to_sci_clk(hw);
7462306a36Sopenharmony_ci	bool enable_ssc = clk->flags & SCI_CLK_SSC_ENABLE;
7562306a36Sopenharmony_ci	bool allow_freq_change = clk->flags & SCI_CLK_ALLOW_FREQ_CHANGE;
7662306a36Sopenharmony_ci	bool input_termination = clk->flags & SCI_CLK_INPUT_TERMINATION;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return clk->provider->ops->get_clock(clk->provider->sci, clk->dev_id,
7962306a36Sopenharmony_ci					     clk->clk_id, enable_ssc,
8062306a36Sopenharmony_ci					     allow_freq_change,
8162306a36Sopenharmony_ci					     input_termination);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/**
8562306a36Sopenharmony_ci * sci_clk_unprepare - Un-prepares (disables) a TI SCI clock
8662306a36Sopenharmony_ci * @hw: clock to unprepare
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * Un-prepares a clock from active state.
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistatic void sci_clk_unprepare(struct clk_hw *hw)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct sci_clk *clk = to_sci_clk(hw);
9362306a36Sopenharmony_ci	int ret;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	ret = clk->provider->ops->put_clock(clk->provider->sci, clk->dev_id,
9662306a36Sopenharmony_ci					    clk->clk_id);
9762306a36Sopenharmony_ci	if (ret)
9862306a36Sopenharmony_ci		dev_err(clk->provider->dev,
9962306a36Sopenharmony_ci			"unprepare failed for dev=%d, clk=%d, ret=%d\n",
10062306a36Sopenharmony_ci			clk->dev_id, clk->clk_id, ret);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/**
10462306a36Sopenharmony_ci * sci_clk_is_prepared - Check if a TI SCI clock is prepared or not
10562306a36Sopenharmony_ci * @hw: clock to check status for
10662306a36Sopenharmony_ci *
10762306a36Sopenharmony_ci * Checks if a clock is prepared (enabled) in hardware. Returns non-zero
10862306a36Sopenharmony_ci * value if clock is enabled, zero otherwise.
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_cistatic int sci_clk_is_prepared(struct clk_hw *hw)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct sci_clk *clk = to_sci_clk(hw);
11362306a36Sopenharmony_ci	bool req_state, current_state;
11462306a36Sopenharmony_ci	int ret;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	ret = clk->provider->ops->is_on(clk->provider->sci, clk->dev_id,
11762306a36Sopenharmony_ci					clk->clk_id, &req_state,
11862306a36Sopenharmony_ci					&current_state);
11962306a36Sopenharmony_ci	if (ret) {
12062306a36Sopenharmony_ci		dev_err(clk->provider->dev,
12162306a36Sopenharmony_ci			"is_prepared failed for dev=%d, clk=%d, ret=%d\n",
12262306a36Sopenharmony_ci			clk->dev_id, clk->clk_id, ret);
12362306a36Sopenharmony_ci		return 0;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return req_state;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/**
13062306a36Sopenharmony_ci * sci_clk_recalc_rate - Get clock rate for a TI SCI clock
13162306a36Sopenharmony_ci * @hw: clock to get rate for
13262306a36Sopenharmony_ci * @parent_rate: parent rate provided by common clock framework, not used
13362306a36Sopenharmony_ci *
13462306a36Sopenharmony_ci * Gets the current clock rate of a TI SCI clock. Returns the current
13562306a36Sopenharmony_ci * clock rate, or zero in failure.
13662306a36Sopenharmony_ci */
13762306a36Sopenharmony_cistatic unsigned long sci_clk_recalc_rate(struct clk_hw *hw,
13862306a36Sopenharmony_ci					 unsigned long parent_rate)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct sci_clk *clk = to_sci_clk(hw);
14162306a36Sopenharmony_ci	u64 freq;
14262306a36Sopenharmony_ci	int ret;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	ret = clk->provider->ops->get_freq(clk->provider->sci, clk->dev_id,
14562306a36Sopenharmony_ci					   clk->clk_id, &freq);
14662306a36Sopenharmony_ci	if (ret) {
14762306a36Sopenharmony_ci		dev_err(clk->provider->dev,
14862306a36Sopenharmony_ci			"recalc-rate failed for dev=%d, clk=%d, ret=%d\n",
14962306a36Sopenharmony_ci			clk->dev_id, clk->clk_id, ret);
15062306a36Sopenharmony_ci		return 0;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return freq;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/**
15762306a36Sopenharmony_ci * sci_clk_determine_rate - Determines a clock rate a clock can be set to
15862306a36Sopenharmony_ci * @hw: clock to change rate for
15962306a36Sopenharmony_ci * @req: requested rate configuration for the clock
16062306a36Sopenharmony_ci *
16162306a36Sopenharmony_ci * Determines a suitable clock rate and parent for a TI SCI clock.
16262306a36Sopenharmony_ci * The parent handling is un-used, as generally the parent clock rates
16362306a36Sopenharmony_ci * are not known by the kernel; instead these are internally handled
16462306a36Sopenharmony_ci * by the firmware. Returns 0 on success, negative error value on failure.
16562306a36Sopenharmony_ci */
16662306a36Sopenharmony_cistatic int sci_clk_determine_rate(struct clk_hw *hw,
16762306a36Sopenharmony_ci				  struct clk_rate_request *req)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct sci_clk *clk = to_sci_clk(hw);
17062306a36Sopenharmony_ci	int ret;
17162306a36Sopenharmony_ci	u64 new_rate;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (clk->cached_req && clk->cached_req == req->rate) {
17462306a36Sopenharmony_ci		req->rate = clk->cached_res;
17562306a36Sopenharmony_ci		return 0;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	ret = clk->provider->ops->get_best_match_freq(clk->provider->sci,
17962306a36Sopenharmony_ci						      clk->dev_id,
18062306a36Sopenharmony_ci						      clk->clk_id,
18162306a36Sopenharmony_ci						      req->min_rate,
18262306a36Sopenharmony_ci						      req->rate,
18362306a36Sopenharmony_ci						      req->max_rate,
18462306a36Sopenharmony_ci						      &new_rate);
18562306a36Sopenharmony_ci	if (ret) {
18662306a36Sopenharmony_ci		dev_err(clk->provider->dev,
18762306a36Sopenharmony_ci			"determine-rate failed for dev=%d, clk=%d, ret=%d\n",
18862306a36Sopenharmony_ci			clk->dev_id, clk->clk_id, ret);
18962306a36Sopenharmony_ci		return ret;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	clk->cached_req = req->rate;
19362306a36Sopenharmony_ci	clk->cached_res = new_rate;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	req->rate = new_rate;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci/**
20162306a36Sopenharmony_ci * sci_clk_set_rate - Set rate for a TI SCI clock
20262306a36Sopenharmony_ci * @hw: clock to change rate for
20362306a36Sopenharmony_ci * @rate: target rate for the clock
20462306a36Sopenharmony_ci * @parent_rate: rate of the clock parent, not used for TI SCI clocks
20562306a36Sopenharmony_ci *
20662306a36Sopenharmony_ci * Sets a clock frequency for a TI SCI clock. Returns the TI SCI
20762306a36Sopenharmony_ci * protocol status.
20862306a36Sopenharmony_ci */
20962306a36Sopenharmony_cistatic int sci_clk_set_rate(struct clk_hw *hw, unsigned long rate,
21062306a36Sopenharmony_ci			    unsigned long parent_rate)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct sci_clk *clk = to_sci_clk(hw);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return clk->provider->ops->set_freq(clk->provider->sci, clk->dev_id,
21562306a36Sopenharmony_ci					    clk->clk_id, rate / 10 * 9, rate,
21662306a36Sopenharmony_ci					    rate / 10 * 11);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci/**
22062306a36Sopenharmony_ci * sci_clk_get_parent - Get the current parent of a TI SCI clock
22162306a36Sopenharmony_ci * @hw: clock to get parent for
22262306a36Sopenharmony_ci *
22362306a36Sopenharmony_ci * Returns the index of the currently selected parent for a TI SCI clock.
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_cistatic u8 sci_clk_get_parent(struct clk_hw *hw)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct sci_clk *clk = to_sci_clk(hw);
22862306a36Sopenharmony_ci	u32 parent_id = 0;
22962306a36Sopenharmony_ci	int ret;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	ret = clk->provider->ops->get_parent(clk->provider->sci, clk->dev_id,
23262306a36Sopenharmony_ci					     clk->clk_id, (void *)&parent_id);
23362306a36Sopenharmony_ci	if (ret) {
23462306a36Sopenharmony_ci		dev_err(clk->provider->dev,
23562306a36Sopenharmony_ci			"get-parent failed for dev=%d, clk=%d, ret=%d\n",
23662306a36Sopenharmony_ci			clk->dev_id, clk->clk_id, ret);
23762306a36Sopenharmony_ci		return 0;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	parent_id = parent_id - clk->clk_id - 1;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return (u8)parent_id;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci/**
24662306a36Sopenharmony_ci * sci_clk_set_parent - Set the parent of a TI SCI clock
24762306a36Sopenharmony_ci * @hw: clock to set parent for
24862306a36Sopenharmony_ci * @index: new parent index for the clock
24962306a36Sopenharmony_ci *
25062306a36Sopenharmony_ci * Sets the parent of a TI SCI clock. Return TI SCI protocol status.
25162306a36Sopenharmony_ci */
25262306a36Sopenharmony_cistatic int sci_clk_set_parent(struct clk_hw *hw, u8 index)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct sci_clk *clk = to_sci_clk(hw);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	clk->cached_req = 0;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	return clk->provider->ops->set_parent(clk->provider->sci, clk->dev_id,
25962306a36Sopenharmony_ci					      clk->clk_id,
26062306a36Sopenharmony_ci					      index + 1 + clk->clk_id);
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic const struct clk_ops sci_clk_ops = {
26462306a36Sopenharmony_ci	.prepare = sci_clk_prepare,
26562306a36Sopenharmony_ci	.unprepare = sci_clk_unprepare,
26662306a36Sopenharmony_ci	.is_prepared = sci_clk_is_prepared,
26762306a36Sopenharmony_ci	.recalc_rate = sci_clk_recalc_rate,
26862306a36Sopenharmony_ci	.determine_rate = sci_clk_determine_rate,
26962306a36Sopenharmony_ci	.set_rate = sci_clk_set_rate,
27062306a36Sopenharmony_ci	.get_parent = sci_clk_get_parent,
27162306a36Sopenharmony_ci	.set_parent = sci_clk_set_parent,
27262306a36Sopenharmony_ci};
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/**
27562306a36Sopenharmony_ci * _sci_clk_get - Gets a handle for an SCI clock
27662306a36Sopenharmony_ci * @provider: Handle to SCI clock provider
27762306a36Sopenharmony_ci * @sci_clk: Handle to the SCI clock to populate
27862306a36Sopenharmony_ci *
27962306a36Sopenharmony_ci * Gets a handle to an existing TI SCI hw clock, or builds a new clock
28062306a36Sopenharmony_ci * entry and registers it with the common clock framework. Called from
28162306a36Sopenharmony_ci * the common clock framework, when a corresponding of_clk_get call is
28262306a36Sopenharmony_ci * executed, or recursively from itself when parsing parent clocks.
28362306a36Sopenharmony_ci * Returns 0 on success, negative error code on failure.
28462306a36Sopenharmony_ci */
28562306a36Sopenharmony_cistatic int _sci_clk_build(struct sci_clk_provider *provider,
28662306a36Sopenharmony_ci			  struct sci_clk *sci_clk)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	struct clk_init_data init = { NULL };
28962306a36Sopenharmony_ci	char *name = NULL;
29062306a36Sopenharmony_ci	char **parent_names = NULL;
29162306a36Sopenharmony_ci	int i;
29262306a36Sopenharmony_ci	int ret = 0;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	name = kasprintf(GFP_KERNEL, "clk:%d:%d", sci_clk->dev_id,
29562306a36Sopenharmony_ci			 sci_clk->clk_id);
29662306a36Sopenharmony_ci	if (!name)
29762306a36Sopenharmony_ci		return -ENOMEM;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	init.name = name;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/*
30262306a36Sopenharmony_ci	 * From kernel point of view, we only care about a clocks parents,
30362306a36Sopenharmony_ci	 * if it has more than 1 possible parent. In this case, it is going
30462306a36Sopenharmony_ci	 * to have mux functionality. Otherwise it is going to act as a root
30562306a36Sopenharmony_ci	 * clock.
30662306a36Sopenharmony_ci	 */
30762306a36Sopenharmony_ci	if (sci_clk->num_parents < 2)
30862306a36Sopenharmony_ci		sci_clk->num_parents = 0;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (sci_clk->num_parents) {
31162306a36Sopenharmony_ci		parent_names = kcalloc(sci_clk->num_parents, sizeof(char *),
31262306a36Sopenharmony_ci				       GFP_KERNEL);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci		if (!parent_names) {
31562306a36Sopenharmony_ci			ret = -ENOMEM;
31662306a36Sopenharmony_ci			goto err;
31762306a36Sopenharmony_ci		}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		for (i = 0; i < sci_clk->num_parents; i++) {
32062306a36Sopenharmony_ci			char *parent_name;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci			parent_name = kasprintf(GFP_KERNEL, "clk:%d:%d",
32362306a36Sopenharmony_ci						sci_clk->dev_id,
32462306a36Sopenharmony_ci						sci_clk->clk_id + 1 + i);
32562306a36Sopenharmony_ci			if (!parent_name) {
32662306a36Sopenharmony_ci				ret = -ENOMEM;
32762306a36Sopenharmony_ci				goto err;
32862306a36Sopenharmony_ci			}
32962306a36Sopenharmony_ci			parent_names[i] = parent_name;
33062306a36Sopenharmony_ci		}
33162306a36Sopenharmony_ci		init.parent_names = (void *)parent_names;
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	init.ops = &sci_clk_ops;
33562306a36Sopenharmony_ci	init.num_parents = sci_clk->num_parents;
33662306a36Sopenharmony_ci	sci_clk->hw.init = &init;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	ret = devm_clk_hw_register(provider->dev, &sci_clk->hw);
33962306a36Sopenharmony_ci	if (ret)
34062306a36Sopenharmony_ci		dev_err(provider->dev, "failed clk register with %d\n", ret);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cierr:
34362306a36Sopenharmony_ci	if (parent_names) {
34462306a36Sopenharmony_ci		for (i = 0; i < sci_clk->num_parents; i++)
34562306a36Sopenharmony_ci			kfree(parent_names[i]);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		kfree(parent_names);
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	kfree(name);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return ret;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic int _cmp_sci_clk(const void *a, const void *b)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	const struct sci_clk *ca = a;
35862306a36Sopenharmony_ci	const struct sci_clk *cb = *(struct sci_clk **)b;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (ca->dev_id == cb->dev_id && ca->clk_id == cb->clk_id)
36162306a36Sopenharmony_ci		return 0;
36262306a36Sopenharmony_ci	if (ca->dev_id > cb->dev_id ||
36362306a36Sopenharmony_ci	    (ca->dev_id == cb->dev_id && ca->clk_id > cb->clk_id))
36462306a36Sopenharmony_ci		return 1;
36562306a36Sopenharmony_ci	return -1;
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/**
36962306a36Sopenharmony_ci * sci_clk_get - Xlate function for getting clock handles
37062306a36Sopenharmony_ci * @clkspec: device tree clock specifier
37162306a36Sopenharmony_ci * @data: pointer to the clock provider
37262306a36Sopenharmony_ci *
37362306a36Sopenharmony_ci * Xlate function for retrieving clock TI SCI hw clock handles based on
37462306a36Sopenharmony_ci * device tree clock specifier. Called from the common clock framework,
37562306a36Sopenharmony_ci * when a corresponding of_clk_get call is executed. Returns a pointer
37662306a36Sopenharmony_ci * to the TI SCI hw clock struct, or ERR_PTR value in failure.
37762306a36Sopenharmony_ci */
37862306a36Sopenharmony_cistatic struct clk_hw *sci_clk_get(struct of_phandle_args *clkspec, void *data)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	struct sci_clk_provider *provider = data;
38162306a36Sopenharmony_ci	struct sci_clk **clk;
38262306a36Sopenharmony_ci	struct sci_clk key;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (clkspec->args_count != 2)
38562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	key.dev_id = clkspec->args[0];
38862306a36Sopenharmony_ci	key.clk_id = clkspec->args[1];
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	clk = bsearch(&key, provider->clocks, provider->num_clocks,
39162306a36Sopenharmony_ci		      sizeof(clk), _cmp_sci_clk);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (!clk)
39462306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return &(*clk)->hw;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int ti_sci_init_clocks(struct sci_clk_provider *p)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	int i;
40262306a36Sopenharmony_ci	int ret;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	for (i = 0; i < p->num_clocks; i++) {
40562306a36Sopenharmony_ci		ret = _sci_clk_build(p, p->clocks[i]);
40662306a36Sopenharmony_ci		if (ret)
40762306a36Sopenharmony_ci			return ret;
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic const struct of_device_id ti_sci_clk_of_match[] = {
41462306a36Sopenharmony_ci	{ .compatible = "ti,k2g-sci-clk" },
41562306a36Sopenharmony_ci	{ /* Sentinel */ },
41662306a36Sopenharmony_ci};
41762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ti_sci_clk_of_match);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci#ifdef CONFIG_TI_SCI_CLK_PROBE_FROM_FW
42062306a36Sopenharmony_cistatic int ti_sci_scan_clocks_from_fw(struct sci_clk_provider *provider)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	int ret;
42362306a36Sopenharmony_ci	int num_clks = 0;
42462306a36Sopenharmony_ci	struct sci_clk **clks = NULL;
42562306a36Sopenharmony_ci	struct sci_clk **tmp_clks;
42662306a36Sopenharmony_ci	struct sci_clk *sci_clk;
42762306a36Sopenharmony_ci	int max_clks = 0;
42862306a36Sopenharmony_ci	int clk_id = 0;
42962306a36Sopenharmony_ci	int dev_id = 0;
43062306a36Sopenharmony_ci	u32 num_parents = 0;
43162306a36Sopenharmony_ci	int gap_size = 0;
43262306a36Sopenharmony_ci	struct device *dev = provider->dev;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	while (1) {
43562306a36Sopenharmony_ci		ret = provider->ops->get_num_parents(provider->sci, dev_id,
43662306a36Sopenharmony_ci						     clk_id,
43762306a36Sopenharmony_ci						     (void *)&num_parents);
43862306a36Sopenharmony_ci		if (ret) {
43962306a36Sopenharmony_ci			gap_size++;
44062306a36Sopenharmony_ci			if (!clk_id) {
44162306a36Sopenharmony_ci				if (gap_size >= 5)
44262306a36Sopenharmony_ci					break;
44362306a36Sopenharmony_ci				dev_id++;
44462306a36Sopenharmony_ci			} else {
44562306a36Sopenharmony_ci				if (gap_size >= 2) {
44662306a36Sopenharmony_ci					dev_id++;
44762306a36Sopenharmony_ci					clk_id = 0;
44862306a36Sopenharmony_ci					gap_size = 0;
44962306a36Sopenharmony_ci				} else {
45062306a36Sopenharmony_ci					clk_id++;
45162306a36Sopenharmony_ci				}
45262306a36Sopenharmony_ci			}
45362306a36Sopenharmony_ci			continue;
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		gap_size = 0;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		if (num_clks == max_clks) {
45962306a36Sopenharmony_ci			tmp_clks = devm_kmalloc_array(dev, max_clks + 64,
46062306a36Sopenharmony_ci						      sizeof(sci_clk),
46162306a36Sopenharmony_ci						      GFP_KERNEL);
46262306a36Sopenharmony_ci			memcpy(tmp_clks, clks, max_clks * sizeof(sci_clk));
46362306a36Sopenharmony_ci			if (max_clks)
46462306a36Sopenharmony_ci				devm_kfree(dev, clks);
46562306a36Sopenharmony_ci			max_clks += 64;
46662306a36Sopenharmony_ci			clks = tmp_clks;
46762306a36Sopenharmony_ci		}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		sci_clk = devm_kzalloc(dev, sizeof(*sci_clk), GFP_KERNEL);
47062306a36Sopenharmony_ci		if (!sci_clk)
47162306a36Sopenharmony_ci			return -ENOMEM;
47262306a36Sopenharmony_ci		sci_clk->dev_id = dev_id;
47362306a36Sopenharmony_ci		sci_clk->clk_id = clk_id;
47462306a36Sopenharmony_ci		sci_clk->provider = provider;
47562306a36Sopenharmony_ci		sci_clk->num_parents = num_parents;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		clks[num_clks] = sci_clk;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci		clk_id++;
48062306a36Sopenharmony_ci		num_clks++;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	provider->clocks = devm_kmalloc_array(dev, num_clks, sizeof(sci_clk),
48462306a36Sopenharmony_ci					      GFP_KERNEL);
48562306a36Sopenharmony_ci	if (!provider->clocks)
48662306a36Sopenharmony_ci		return -ENOMEM;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	memcpy(provider->clocks, clks, num_clks * sizeof(sci_clk));
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	provider->num_clocks = num_clks;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	devm_kfree(dev, clks);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	return 0;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci#else
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic int _cmp_sci_clk_list(void *priv, const struct list_head *a,
50062306a36Sopenharmony_ci			     const struct list_head *b)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct sci_clk *ca = container_of(a, struct sci_clk, node);
50362306a36Sopenharmony_ci	struct sci_clk *cb = container_of(b, struct sci_clk, node);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	return _cmp_sci_clk(ca, &cb);
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cistatic int ti_sci_scan_clocks_from_dt(struct sci_clk_provider *provider)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	struct device *dev = provider->dev;
51162306a36Sopenharmony_ci	struct device_node *np = NULL;
51262306a36Sopenharmony_ci	int ret;
51362306a36Sopenharmony_ci	int index;
51462306a36Sopenharmony_ci	struct of_phandle_args args;
51562306a36Sopenharmony_ci	struct list_head clks;
51662306a36Sopenharmony_ci	struct sci_clk *sci_clk, *prev;
51762306a36Sopenharmony_ci	int num_clks = 0;
51862306a36Sopenharmony_ci	int num_parents;
51962306a36Sopenharmony_ci	int clk_id;
52062306a36Sopenharmony_ci	const char * const clk_names[] = {
52162306a36Sopenharmony_ci		"clocks", "assigned-clocks", "assigned-clock-parents", NULL
52262306a36Sopenharmony_ci	};
52362306a36Sopenharmony_ci	const char * const *clk_name;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	INIT_LIST_HEAD(&clks);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	clk_name = clk_names;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	while (*clk_name) {
53062306a36Sopenharmony_ci		np = of_find_node_with_property(np, *clk_name);
53162306a36Sopenharmony_ci		if (!np) {
53262306a36Sopenharmony_ci			clk_name++;
53362306a36Sopenharmony_ci			continue;
53462306a36Sopenharmony_ci		}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci		if (!of_device_is_available(np))
53762306a36Sopenharmony_ci			continue;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		index = 0;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		do {
54262306a36Sopenharmony_ci			ret = of_parse_phandle_with_args(np, *clk_name,
54362306a36Sopenharmony_ci							 "#clock-cells", index,
54462306a36Sopenharmony_ci							 &args);
54562306a36Sopenharmony_ci			if (ret)
54662306a36Sopenharmony_ci				break;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci			if (args.args_count == 2 && args.np == dev->of_node) {
54962306a36Sopenharmony_ci				sci_clk = devm_kzalloc(dev, sizeof(*sci_clk),
55062306a36Sopenharmony_ci						       GFP_KERNEL);
55162306a36Sopenharmony_ci				if (!sci_clk)
55262306a36Sopenharmony_ci					return -ENOMEM;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci				sci_clk->dev_id = args.args[0];
55562306a36Sopenharmony_ci				sci_clk->clk_id = args.args[1];
55662306a36Sopenharmony_ci				sci_clk->provider = provider;
55762306a36Sopenharmony_ci				provider->ops->get_num_parents(provider->sci,
55862306a36Sopenharmony_ci							       sci_clk->dev_id,
55962306a36Sopenharmony_ci							       sci_clk->clk_id,
56062306a36Sopenharmony_ci							       (void *)&sci_clk->num_parents);
56162306a36Sopenharmony_ci				list_add_tail(&sci_clk->node, &clks);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci				num_clks++;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci				num_parents = sci_clk->num_parents;
56662306a36Sopenharmony_ci				if (num_parents == 1)
56762306a36Sopenharmony_ci					num_parents = 0;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci				/*
57062306a36Sopenharmony_ci				 * Linux kernel has inherent limitation
57162306a36Sopenharmony_ci				 * of 255 clock parents at the moment.
57262306a36Sopenharmony_ci				 * Right now, it is not expected that
57362306a36Sopenharmony_ci				 * any mux clock from sci-clk driver
57462306a36Sopenharmony_ci				 * would exceed that limit either, but
57562306a36Sopenharmony_ci				 * the ABI basically provides that
57662306a36Sopenharmony_ci				 * possibility. Print out a warning if
57762306a36Sopenharmony_ci				 * this happens for any clock.
57862306a36Sopenharmony_ci				 */
57962306a36Sopenharmony_ci				if (num_parents >= 255) {
58062306a36Sopenharmony_ci					dev_warn(dev, "too many parents for dev=%d, clk=%d (%d), cropping to 255.\n",
58162306a36Sopenharmony_ci						 sci_clk->dev_id,
58262306a36Sopenharmony_ci						 sci_clk->clk_id, num_parents);
58362306a36Sopenharmony_ci					num_parents = 255;
58462306a36Sopenharmony_ci				}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci				clk_id = args.args[1] + 1;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci				while (num_parents--) {
58962306a36Sopenharmony_ci					sci_clk = devm_kzalloc(dev,
59062306a36Sopenharmony_ci							       sizeof(*sci_clk),
59162306a36Sopenharmony_ci							       GFP_KERNEL);
59262306a36Sopenharmony_ci					if (!sci_clk)
59362306a36Sopenharmony_ci						return -ENOMEM;
59462306a36Sopenharmony_ci					sci_clk->dev_id = args.args[0];
59562306a36Sopenharmony_ci					sci_clk->clk_id = clk_id++;
59662306a36Sopenharmony_ci					sci_clk->provider = provider;
59762306a36Sopenharmony_ci					list_add_tail(&sci_clk->node, &clks);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci					num_clks++;
60062306a36Sopenharmony_ci				}
60162306a36Sopenharmony_ci			}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci			index++;
60462306a36Sopenharmony_ci		} while (args.np);
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	list_sort(NULL, &clks, _cmp_sci_clk_list);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	provider->clocks = devm_kmalloc_array(dev, num_clks, sizeof(sci_clk),
61062306a36Sopenharmony_ci					      GFP_KERNEL);
61162306a36Sopenharmony_ci	if (!provider->clocks)
61262306a36Sopenharmony_ci		return -ENOMEM;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	num_clks = 0;
61562306a36Sopenharmony_ci	prev = NULL;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	list_for_each_entry(sci_clk, &clks, node) {
61862306a36Sopenharmony_ci		if (prev && prev->dev_id == sci_clk->dev_id &&
61962306a36Sopenharmony_ci		    prev->clk_id == sci_clk->clk_id)
62062306a36Sopenharmony_ci			continue;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci		provider->clocks[num_clks++] = sci_clk;
62362306a36Sopenharmony_ci		prev = sci_clk;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	provider->num_clocks = num_clks;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	return 0;
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci#endif
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci/**
63362306a36Sopenharmony_ci * ti_sci_clk_probe - Probe function for the TI SCI clock driver
63462306a36Sopenharmony_ci * @pdev: platform device pointer to be probed
63562306a36Sopenharmony_ci *
63662306a36Sopenharmony_ci * Probes the TI SCI clock device. Allocates a new clock provider
63762306a36Sopenharmony_ci * and registers this to the common clock framework. Also applies
63862306a36Sopenharmony_ci * any required flags to the identified clocks via clock lists
63962306a36Sopenharmony_ci * supplied from DT. Returns 0 for success, negative error value
64062306a36Sopenharmony_ci * for failure.
64162306a36Sopenharmony_ci */
64262306a36Sopenharmony_cistatic int ti_sci_clk_probe(struct platform_device *pdev)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
64562306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
64662306a36Sopenharmony_ci	struct sci_clk_provider *provider;
64762306a36Sopenharmony_ci	const struct ti_sci_handle *handle;
64862306a36Sopenharmony_ci	int ret;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	handle = devm_ti_sci_get_handle(dev);
65162306a36Sopenharmony_ci	if (IS_ERR(handle))
65262306a36Sopenharmony_ci		return PTR_ERR(handle);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL);
65562306a36Sopenharmony_ci	if (!provider)
65662306a36Sopenharmony_ci		return -ENOMEM;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	provider->sci = handle;
65962306a36Sopenharmony_ci	provider->ops = &handle->ops.clk_ops;
66062306a36Sopenharmony_ci	provider->dev = dev;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci#ifdef CONFIG_TI_SCI_CLK_PROBE_FROM_FW
66362306a36Sopenharmony_ci	ret = ti_sci_scan_clocks_from_fw(provider);
66462306a36Sopenharmony_ci	if (ret) {
66562306a36Sopenharmony_ci		dev_err(dev, "scan clocks from FW failed: %d\n", ret);
66662306a36Sopenharmony_ci		return ret;
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci#else
66962306a36Sopenharmony_ci	ret = ti_sci_scan_clocks_from_dt(provider);
67062306a36Sopenharmony_ci	if (ret) {
67162306a36Sopenharmony_ci		dev_err(dev, "scan clocks from DT failed: %d\n", ret);
67262306a36Sopenharmony_ci		return ret;
67362306a36Sopenharmony_ci	}
67462306a36Sopenharmony_ci#endif
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	ret = ti_sci_init_clocks(provider);
67762306a36Sopenharmony_ci	if (ret) {
67862306a36Sopenharmony_ci		pr_err("ti-sci-init-clocks failed.\n");
67962306a36Sopenharmony_ci		return ret;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	return of_clk_add_hw_provider(np, sci_clk_get, provider);
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci/**
68662306a36Sopenharmony_ci * ti_sci_clk_remove - Remove TI SCI clock device
68762306a36Sopenharmony_ci * @pdev: platform device pointer for the device to be removed
68862306a36Sopenharmony_ci *
68962306a36Sopenharmony_ci * Removes the TI SCI device. Unregisters the clock provider registered
69062306a36Sopenharmony_ci * via common clock framework. Any memory allocated for the device will
69162306a36Sopenharmony_ci * be free'd silently via the devm framework. Returns 0 always.
69262306a36Sopenharmony_ci */
69362306a36Sopenharmony_cistatic void ti_sci_clk_remove(struct platform_device *pdev)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	of_clk_del_provider(pdev->dev.of_node);
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic struct platform_driver ti_sci_clk_driver = {
69962306a36Sopenharmony_ci	.probe = ti_sci_clk_probe,
70062306a36Sopenharmony_ci	.remove_new = ti_sci_clk_remove,
70162306a36Sopenharmony_ci	.driver = {
70262306a36Sopenharmony_ci		.name = "ti-sci-clk",
70362306a36Sopenharmony_ci		.of_match_table = of_match_ptr(ti_sci_clk_of_match),
70462306a36Sopenharmony_ci	},
70562306a36Sopenharmony_ci};
70662306a36Sopenharmony_cimodule_platform_driver(ti_sci_clk_driver);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
70962306a36Sopenharmony_ciMODULE_DESCRIPTION("TI System Control Interface(SCI) Clock driver");
71062306a36Sopenharmony_ciMODULE_AUTHOR("Tero Kristo");
71162306a36Sopenharmony_ciMODULE_ALIAS("platform:ti-sci-clk");
712