162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Microchip Image Sensor Controller (ISC) common clock driver setup
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016 Microchip Technology, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Songjun Wu
862306a36Sopenharmony_ci * Author: Eugen Hristev <eugen.hristev@microchip.com>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/clk.h>
1262306a36Sopenharmony_ci#include <linux/clkdev.h>
1362306a36Sopenharmony_ci#include <linux/clk-provider.h>
1462306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1562306a36Sopenharmony_ci#include <linux/regmap.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "microchip-isc-regs.h"
1862306a36Sopenharmony_ci#include "microchip-isc.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic int isc_wait_clk_stable(struct clk_hw *hw)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	struct isc_clk *isc_clk = to_isc_clk(hw);
2362306a36Sopenharmony_ci	struct regmap *regmap = isc_clk->regmap;
2462306a36Sopenharmony_ci	unsigned long timeout = jiffies + usecs_to_jiffies(1000);
2562306a36Sopenharmony_ci	unsigned int status;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
2862306a36Sopenharmony_ci		regmap_read(regmap, ISC_CLKSR, &status);
2962306a36Sopenharmony_ci		if (!(status & ISC_CLKSR_SIP))
3062306a36Sopenharmony_ci			return 0;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci		usleep_range(10, 250);
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return -ETIMEDOUT;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int isc_clk_prepare(struct clk_hw *hw)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct isc_clk *isc_clk = to_isc_clk(hw);
4162306a36Sopenharmony_ci	int ret;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(isc_clk->dev);
4462306a36Sopenharmony_ci	if (ret < 0)
4562306a36Sopenharmony_ci		return ret;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return isc_wait_clk_stable(hw);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic void isc_clk_unprepare(struct clk_hw *hw)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct isc_clk *isc_clk = to_isc_clk(hw);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	isc_wait_clk_stable(hw);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	pm_runtime_put_sync(isc_clk->dev);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int isc_clk_enable(struct clk_hw *hw)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct isc_clk *isc_clk = to_isc_clk(hw);
6262306a36Sopenharmony_ci	u32 id = isc_clk->id;
6362306a36Sopenharmony_ci	struct regmap *regmap = isc_clk->regmap;
6462306a36Sopenharmony_ci	unsigned long flags;
6562306a36Sopenharmony_ci	unsigned int status;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n",
6862306a36Sopenharmony_ci		__func__, id, isc_clk->div, isc_clk->parent_id);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	spin_lock_irqsave(&isc_clk->lock, flags);
7162306a36Sopenharmony_ci	regmap_update_bits(regmap, ISC_CLKCFG,
7262306a36Sopenharmony_ci			   ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id),
7362306a36Sopenharmony_ci			   (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) |
7462306a36Sopenharmony_ci			   (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id)));
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	regmap_write(regmap, ISC_CLKEN, ISC_CLK(id));
7762306a36Sopenharmony_ci	spin_unlock_irqrestore(&isc_clk->lock, flags);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	regmap_read(regmap, ISC_CLKSR, &status);
8062306a36Sopenharmony_ci	if (status & ISC_CLK(id))
8162306a36Sopenharmony_ci		return 0;
8262306a36Sopenharmony_ci	else
8362306a36Sopenharmony_ci		return -EINVAL;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic void isc_clk_disable(struct clk_hw *hw)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	struct isc_clk *isc_clk = to_isc_clk(hw);
8962306a36Sopenharmony_ci	u32 id = isc_clk->id;
9062306a36Sopenharmony_ci	unsigned long flags;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	spin_lock_irqsave(&isc_clk->lock, flags);
9362306a36Sopenharmony_ci	regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id));
9462306a36Sopenharmony_ci	spin_unlock_irqrestore(&isc_clk->lock, flags);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int isc_clk_is_enabled(struct clk_hw *hw)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct isc_clk *isc_clk = to_isc_clk(hw);
10062306a36Sopenharmony_ci	u32 status;
10162306a36Sopenharmony_ci	int ret;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	ret = pm_runtime_resume_and_get(isc_clk->dev);
10462306a36Sopenharmony_ci	if (ret < 0)
10562306a36Sopenharmony_ci		return 0;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	regmap_read(isc_clk->regmap, ISC_CLKSR, &status);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	pm_runtime_put_sync(isc_clk->dev);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return status & ISC_CLK(isc_clk->id) ? 1 : 0;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic unsigned long
11562306a36Sopenharmony_ciisc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct isc_clk *isc_clk = to_isc_clk(hw);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic int isc_clk_determine_rate(struct clk_hw *hw,
12362306a36Sopenharmony_ci				  struct clk_rate_request *req)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct isc_clk *isc_clk = to_isc_clk(hw);
12662306a36Sopenharmony_ci	long best_rate = -EINVAL;
12762306a36Sopenharmony_ci	int best_diff = -1;
12862306a36Sopenharmony_ci	unsigned int i, div;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
13162306a36Sopenharmony_ci		struct clk_hw *parent;
13262306a36Sopenharmony_ci		unsigned long parent_rate;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		parent = clk_hw_get_parent_by_index(hw, i);
13562306a36Sopenharmony_ci		if (!parent)
13662306a36Sopenharmony_ci			continue;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		parent_rate = clk_hw_get_rate(parent);
13962306a36Sopenharmony_ci		if (!parent_rate)
14062306a36Sopenharmony_ci			continue;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) {
14362306a36Sopenharmony_ci			unsigned long rate;
14462306a36Sopenharmony_ci			int diff;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci			rate = DIV_ROUND_CLOSEST(parent_rate, div);
14762306a36Sopenharmony_ci			diff = abs(req->rate - rate);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci			if (best_diff < 0 || best_diff > diff) {
15062306a36Sopenharmony_ci				best_rate = rate;
15162306a36Sopenharmony_ci				best_diff = diff;
15262306a36Sopenharmony_ci				req->best_parent_rate = parent_rate;
15362306a36Sopenharmony_ci				req->best_parent_hw = parent;
15462306a36Sopenharmony_ci			}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci			if (!best_diff || rate < req->rate)
15762306a36Sopenharmony_ci				break;
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		if (!best_diff)
16162306a36Sopenharmony_ci			break;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	dev_dbg(isc_clk->dev,
16562306a36Sopenharmony_ci		"ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
16662306a36Sopenharmony_ci		__func__, best_rate,
16762306a36Sopenharmony_ci		__clk_get_name((req->best_parent_hw)->clk),
16862306a36Sopenharmony_ci		req->best_parent_rate);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (best_rate < 0)
17162306a36Sopenharmony_ci		return best_rate;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	req->rate = best_rate;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return 0;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic int isc_clk_set_parent(struct clk_hw *hw, u8 index)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct isc_clk *isc_clk = to_isc_clk(hw);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (index >= clk_hw_get_num_parents(hw))
18362306a36Sopenharmony_ci		return -EINVAL;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	isc_clk->parent_id = index;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return 0;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic u8 isc_clk_get_parent(struct clk_hw *hw)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct isc_clk *isc_clk = to_isc_clk(hw);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return isc_clk->parent_id;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int isc_clk_set_rate(struct clk_hw *hw,
19862306a36Sopenharmony_ci			    unsigned long rate,
19962306a36Sopenharmony_ci			    unsigned long parent_rate)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct isc_clk *isc_clk = to_isc_clk(hw);
20262306a36Sopenharmony_ci	u32 div;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (!rate)
20562306a36Sopenharmony_ci		return -EINVAL;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	div = DIV_ROUND_CLOSEST(parent_rate, rate);
20862306a36Sopenharmony_ci	if (div > (ISC_CLK_MAX_DIV + 1) || !div)
20962306a36Sopenharmony_ci		return -EINVAL;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	isc_clk->div = div - 1;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	return 0;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic const struct clk_ops isc_clk_ops = {
21762306a36Sopenharmony_ci	.prepare	= isc_clk_prepare,
21862306a36Sopenharmony_ci	.unprepare	= isc_clk_unprepare,
21962306a36Sopenharmony_ci	.enable		= isc_clk_enable,
22062306a36Sopenharmony_ci	.disable	= isc_clk_disable,
22162306a36Sopenharmony_ci	.is_enabled	= isc_clk_is_enabled,
22262306a36Sopenharmony_ci	.recalc_rate	= isc_clk_recalc_rate,
22362306a36Sopenharmony_ci	.determine_rate	= isc_clk_determine_rate,
22462306a36Sopenharmony_ci	.set_parent	= isc_clk_set_parent,
22562306a36Sopenharmony_ci	.get_parent	= isc_clk_get_parent,
22662306a36Sopenharmony_ci	.set_rate	= isc_clk_set_rate,
22762306a36Sopenharmony_ci};
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int isc_clk_register(struct isc_device *isc, unsigned int id)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct regmap *regmap = isc->regmap;
23262306a36Sopenharmony_ci	struct device_node *np = isc->dev->of_node;
23362306a36Sopenharmony_ci	struct isc_clk *isc_clk;
23462306a36Sopenharmony_ci	struct clk_init_data init;
23562306a36Sopenharmony_ci	const char *clk_name = np->name;
23662306a36Sopenharmony_ci	const char *parent_names[3];
23762306a36Sopenharmony_ci	int num_parents;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (id == ISC_ISPCK && !isc->ispck_required)
24062306a36Sopenharmony_ci		return 0;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	num_parents = of_clk_get_parent_count(np);
24362306a36Sopenharmony_ci	if (num_parents < 1 || num_parents > 3)
24462306a36Sopenharmony_ci		return -EINVAL;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (num_parents > 2 && id == ISC_ISPCK)
24762306a36Sopenharmony_ci		num_parents = 2;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	of_clk_parent_fill(np, parent_names, num_parents);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (id == ISC_MCK)
25262306a36Sopenharmony_ci		of_property_read_string(np, "clock-output-names", &clk_name);
25362306a36Sopenharmony_ci	else
25462306a36Sopenharmony_ci		clk_name = "isc-ispck";
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	init.parent_names	= parent_names;
25762306a36Sopenharmony_ci	init.num_parents	= num_parents;
25862306a36Sopenharmony_ci	init.name		= clk_name;
25962306a36Sopenharmony_ci	init.ops		= &isc_clk_ops;
26062306a36Sopenharmony_ci	init.flags		= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	isc_clk = &isc->isc_clks[id];
26362306a36Sopenharmony_ci	isc_clk->hw.init	= &init;
26462306a36Sopenharmony_ci	isc_clk->regmap		= regmap;
26562306a36Sopenharmony_ci	isc_clk->id		= id;
26662306a36Sopenharmony_ci	isc_clk->dev		= isc->dev;
26762306a36Sopenharmony_ci	spin_lock_init(&isc_clk->lock);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	isc_clk->clk = clk_register(isc->dev, &isc_clk->hw);
27062306a36Sopenharmony_ci	if (IS_ERR(isc_clk->clk)) {
27162306a36Sopenharmony_ci		dev_err(isc->dev, "%s: clock register fail\n", clk_name);
27262306a36Sopenharmony_ci		return PTR_ERR(isc_clk->clk);
27362306a36Sopenharmony_ci	} else if (id == ISC_MCK) {
27462306a36Sopenharmony_ci		of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk);
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return 0;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ciint microchip_isc_clk_init(struct isc_device *isc)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	unsigned int i;
28362306a36Sopenharmony_ci	int ret;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++)
28662306a36Sopenharmony_ci		isc->isc_clks[i].clk = ERR_PTR(-EINVAL);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
28962306a36Sopenharmony_ci		ret = isc_clk_register(isc, i);
29062306a36Sopenharmony_ci		if (ret)
29162306a36Sopenharmony_ci			return ret;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return 0;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microchip_isc_clk_init);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_civoid microchip_isc_clk_cleanup(struct isc_device *isc)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	unsigned int i;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	of_clk_del_provider(isc->dev->of_node);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
30562306a36Sopenharmony_ci		struct isc_clk *isc_clk = &isc->isc_clks[i];
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		if (!IS_ERR(isc_clk->clk))
30862306a36Sopenharmony_ci			clk_unregister(isc_clk->clk);
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microchip_isc_clk_cleanup);
312