brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · 24358d8 Raw
312 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Microchip Image Sensor Controller (ISC) common clock driver setup4 *5 * Copyright (C) 2016 Microchip Technology, Inc.6 *7 * Author: Songjun Wu8 * Author: Eugen Hristev <eugen.hristev@microchip.com>9 *10 */11#include <linux/clk.h>12#include <linux/clkdev.h>13#include <linux/clk-provider.h>14#include <linux/pm_runtime.h>15#include <linux/regmap.h>16 17#include "microchip-isc-regs.h"18#include "microchip-isc.h"19 20static int isc_wait_clk_stable(struct clk_hw *hw)21{22	struct isc_clk *isc_clk = to_isc_clk(hw);23	struct regmap *regmap = isc_clk->regmap;24	unsigned long timeout = jiffies + usecs_to_jiffies(1000);25	unsigned int status;26 27	while (time_before(jiffies, timeout)) {28		regmap_read(regmap, ISC_CLKSR, &status);29		if (!(status & ISC_CLKSR_SIP))30			return 0;31 32		usleep_range(10, 250);33	}34 35	return -ETIMEDOUT;36}37 38static int isc_clk_prepare(struct clk_hw *hw)39{40	struct isc_clk *isc_clk = to_isc_clk(hw);41	int ret;42 43	ret = pm_runtime_resume_and_get(isc_clk->dev);44	if (ret < 0)45		return ret;46 47	return isc_wait_clk_stable(hw);48}49 50static void isc_clk_unprepare(struct clk_hw *hw)51{52	struct isc_clk *isc_clk = to_isc_clk(hw);53 54	isc_wait_clk_stable(hw);55 56	pm_runtime_put_sync(isc_clk->dev);57}58 59static int isc_clk_enable(struct clk_hw *hw)60{61	struct isc_clk *isc_clk = to_isc_clk(hw);62	u32 id = isc_clk->id;63	struct regmap *regmap = isc_clk->regmap;64	unsigned long flags;65	unsigned int status;66 67	dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n",68		__func__, id, isc_clk->div, isc_clk->parent_id);69 70	spin_lock_irqsave(&isc_clk->lock, flags);71	regmap_update_bits(regmap, ISC_CLKCFG,72			   ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id),73			   (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) |74			   (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id)));75 76	regmap_write(regmap, ISC_CLKEN, ISC_CLK(id));77	spin_unlock_irqrestore(&isc_clk->lock, flags);78 79	regmap_read(regmap, ISC_CLKSR, &status);80	if (status & ISC_CLK(id))81		return 0;82	else83		return -EINVAL;84}85 86static void isc_clk_disable(struct clk_hw *hw)87{88	struct isc_clk *isc_clk = to_isc_clk(hw);89	u32 id = isc_clk->id;90	unsigned long flags;91 92	spin_lock_irqsave(&isc_clk->lock, flags);93	regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id));94	spin_unlock_irqrestore(&isc_clk->lock, flags);95}96 97static int isc_clk_is_enabled(struct clk_hw *hw)98{99	struct isc_clk *isc_clk = to_isc_clk(hw);100	u32 status;101	int ret;102 103	ret = pm_runtime_resume_and_get(isc_clk->dev);104	if (ret < 0)105		return 0;106 107	regmap_read(isc_clk->regmap, ISC_CLKSR, &status);108 109	pm_runtime_put_sync(isc_clk->dev);110 111	return status & ISC_CLK(isc_clk->id) ? 1 : 0;112}113 114static unsigned long115isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)116{117	struct isc_clk *isc_clk = to_isc_clk(hw);118 119	return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1);120}121 122static int isc_clk_determine_rate(struct clk_hw *hw,123				  struct clk_rate_request *req)124{125	struct isc_clk *isc_clk = to_isc_clk(hw);126	long best_rate = -EINVAL;127	int best_diff = -1;128	unsigned int i, div;129 130	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {131		struct clk_hw *parent;132		unsigned long parent_rate;133 134		parent = clk_hw_get_parent_by_index(hw, i);135		if (!parent)136			continue;137 138		parent_rate = clk_hw_get_rate(parent);139		if (!parent_rate)140			continue;141 142		for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) {143			unsigned long rate;144			int diff;145 146			rate = DIV_ROUND_CLOSEST(parent_rate, div);147			diff = abs(req->rate - rate);148 149			if (best_diff < 0 || best_diff > diff) {150				best_rate = rate;151				best_diff = diff;152				req->best_parent_rate = parent_rate;153				req->best_parent_hw = parent;154			}155 156			if (!best_diff || rate < req->rate)157				break;158		}159 160		if (!best_diff)161			break;162	}163 164	dev_dbg(isc_clk->dev,165		"ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",166		__func__, best_rate,167		__clk_get_name((req->best_parent_hw)->clk),168		req->best_parent_rate);169 170	if (best_rate < 0)171		return best_rate;172 173	req->rate = best_rate;174 175	return 0;176}177 178static int isc_clk_set_parent(struct clk_hw *hw, u8 index)179{180	struct isc_clk *isc_clk = to_isc_clk(hw);181 182	if (index >= clk_hw_get_num_parents(hw))183		return -EINVAL;184 185	isc_clk->parent_id = index;186 187	return 0;188}189 190static u8 isc_clk_get_parent(struct clk_hw *hw)191{192	struct isc_clk *isc_clk = to_isc_clk(hw);193 194	return isc_clk->parent_id;195}196 197static int isc_clk_set_rate(struct clk_hw *hw,198			    unsigned long rate,199			    unsigned long parent_rate)200{201	struct isc_clk *isc_clk = to_isc_clk(hw);202	u32 div;203 204	if (!rate)205		return -EINVAL;206 207	div = DIV_ROUND_CLOSEST(parent_rate, rate);208	if (div > (ISC_CLK_MAX_DIV + 1) || !div)209		return -EINVAL;210 211	isc_clk->div = div - 1;212 213	return 0;214}215 216static const struct clk_ops isc_clk_ops = {217	.prepare	= isc_clk_prepare,218	.unprepare	= isc_clk_unprepare,219	.enable		= isc_clk_enable,220	.disable	= isc_clk_disable,221	.is_enabled	= isc_clk_is_enabled,222	.recalc_rate	= isc_clk_recalc_rate,223	.determine_rate	= isc_clk_determine_rate,224	.set_parent	= isc_clk_set_parent,225	.get_parent	= isc_clk_get_parent,226	.set_rate	= isc_clk_set_rate,227};228 229static int isc_clk_register(struct isc_device *isc, unsigned int id)230{231	struct regmap *regmap = isc->regmap;232	struct device_node *np = isc->dev->of_node;233	struct isc_clk *isc_clk;234	struct clk_init_data init;235	const char *clk_name = np->name;236	const char *parent_names[3];237	int num_parents;238 239	if (id == ISC_ISPCK && !isc->ispck_required)240		return 0;241 242	num_parents = of_clk_get_parent_count(np);243	if (num_parents < 1 || num_parents > 3)244		return -EINVAL;245 246	if (num_parents > 2 && id == ISC_ISPCK)247		num_parents = 2;248 249	of_clk_parent_fill(np, parent_names, num_parents);250 251	if (id == ISC_MCK)252		of_property_read_string(np, "clock-output-names", &clk_name);253	else254		clk_name = "isc-ispck";255 256	init.parent_names	= parent_names;257	init.num_parents	= num_parents;258	init.name		= clk_name;259	init.ops		= &isc_clk_ops;260	init.flags		= CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;261 262	isc_clk = &isc->isc_clks[id];263	isc_clk->hw.init	= &init;264	isc_clk->regmap		= regmap;265	isc_clk->id		= id;266	isc_clk->dev		= isc->dev;267	spin_lock_init(&isc_clk->lock);268 269	isc_clk->clk = clk_register(isc->dev, &isc_clk->hw);270	if (IS_ERR(isc_clk->clk)) {271		dev_err(isc->dev, "%s: clock register fail\n", clk_name);272		return PTR_ERR(isc_clk->clk);273	} else if (id == ISC_MCK) {274		of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk);275	}276 277	return 0;278}279 280int microchip_isc_clk_init(struct isc_device *isc)281{282	unsigned int i;283	int ret;284 285	for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++)286		isc->isc_clks[i].clk = ERR_PTR(-EINVAL);287 288	for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {289		ret = isc_clk_register(isc, i);290		if (ret)291			return ret;292	}293 294	return 0;295}296EXPORT_SYMBOL_GPL(microchip_isc_clk_init);297 298void microchip_isc_clk_cleanup(struct isc_device *isc)299{300	unsigned int i;301 302	of_clk_del_provider(isc->dev->of_node);303 304	for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {305		struct isc_clk *isc_clk = &isc->isc_clks[i];306 307		if (!IS_ERR(isc_clk->clk))308			clk_unregister(isc_clk->clk);309	}310}311EXPORT_SYMBOL_GPL(microchip_isc_clk_cleanup);312