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