162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Ingenic SoC CGU driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2013-2015 Imagination Technologies 662306a36Sopenharmony_ci * Author: Paul Burton <paul.burton@mips.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/clk-provider.h> 1262306a36Sopenharmony_ci#include <linux/clkdev.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/iopoll.h> 1662306a36Sopenharmony_ci#include <linux/math64.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/of_address.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/spinlock.h> 2162306a36Sopenharmony_ci#include <linux/time.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "cgu.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define MHZ (1000 * 1000) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic inline const struct ingenic_cgu_clk_info * 2862306a36Sopenharmony_cito_clk_info(struct ingenic_clk *clk) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci return &clk->cgu->clock_info[clk->idx]; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/** 3462306a36Sopenharmony_ci * ingenic_cgu_gate_get() - get the value of clock gate register bit 3562306a36Sopenharmony_ci * @cgu: reference to the CGU whose registers should be read 3662306a36Sopenharmony_ci * @info: info struct describing the gate bit 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * Retrieves the state of the clock gate bit described by info. The 3962306a36Sopenharmony_ci * caller must hold cgu->lock. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * Return: true if the gate bit is set, else false. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_cistatic inline bool 4462306a36Sopenharmony_ciingenic_cgu_gate_get(struct ingenic_cgu *cgu, 4562306a36Sopenharmony_ci const struct ingenic_cgu_gate_info *info) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci return !!(readl(cgu->base + info->reg) & BIT(info->bit)) 4862306a36Sopenharmony_ci ^ info->clear_to_gate; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/** 5262306a36Sopenharmony_ci * ingenic_cgu_gate_set() - set the value of clock gate register bit 5362306a36Sopenharmony_ci * @cgu: reference to the CGU whose registers should be modified 5462306a36Sopenharmony_ci * @info: info struct describing the gate bit 5562306a36Sopenharmony_ci * @val: non-zero to gate a clock, otherwise zero 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * Sets the given gate bit in order to gate or ungate a clock. 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * The caller must hold cgu->lock. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_cistatic inline void 6262306a36Sopenharmony_ciingenic_cgu_gate_set(struct ingenic_cgu *cgu, 6362306a36Sopenharmony_ci const struct ingenic_cgu_gate_info *info, bool val) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci u32 clkgr = readl(cgu->base + info->reg); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (val ^ info->clear_to_gate) 6862306a36Sopenharmony_ci clkgr |= BIT(info->bit); 6962306a36Sopenharmony_ci else 7062306a36Sopenharmony_ci clkgr &= ~BIT(info->bit); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci writel(clkgr, cgu->base + info->reg); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * PLL operations 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic unsigned long 8062306a36Sopenharmony_ciingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 8362306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 8462306a36Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 8562306a36Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info; 8662306a36Sopenharmony_ci unsigned m, n, od, od_enc = 0; 8762306a36Sopenharmony_ci bool bypass; 8862306a36Sopenharmony_ci u32 ctl; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci BUG_ON(clk_info->type != CGU_CLK_PLL); 9162306a36Sopenharmony_ci pll_info = &clk_info->pll; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci ctl = readl(cgu->base + pll_info->reg); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci m = (ctl >> pll_info->m_shift) & GENMASK(pll_info->m_bits - 1, 0); 9662306a36Sopenharmony_ci m += pll_info->m_offset; 9762306a36Sopenharmony_ci n = (ctl >> pll_info->n_shift) & GENMASK(pll_info->n_bits - 1, 0); 9862306a36Sopenharmony_ci n += pll_info->n_offset; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (pll_info->od_bits > 0) { 10162306a36Sopenharmony_ci od_enc = ctl >> pll_info->od_shift; 10262306a36Sopenharmony_ci od_enc &= GENMASK(pll_info->od_bits - 1, 0); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (pll_info->bypass_bit >= 0) { 10662306a36Sopenharmony_ci ctl = readl(cgu->base + pll_info->bypass_reg); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci bypass = !!(ctl & BIT(pll_info->bypass_bit)); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (bypass) 11162306a36Sopenharmony_ci return parent_rate; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci for (od = 0; od < pll_info->od_max; od++) 11562306a36Sopenharmony_ci if (pll_info->od_encoding[od] == od_enc) 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* if od_max = 0, od_bits should be 0 and od is fixed to 1. */ 11962306a36Sopenharmony_ci if (pll_info->od_max == 0) 12062306a36Sopenharmony_ci BUG_ON(pll_info->od_bits != 0); 12162306a36Sopenharmony_ci else 12262306a36Sopenharmony_ci BUG_ON(od == pll_info->od_max); 12362306a36Sopenharmony_ci od++; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return div_u64((u64)parent_rate * m * pll_info->rate_multiplier, 12662306a36Sopenharmony_ci n * od); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void 13062306a36Sopenharmony_ciingenic_pll_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info, 13162306a36Sopenharmony_ci unsigned long rate, unsigned long parent_rate, 13262306a36Sopenharmony_ci unsigned int *pm, unsigned int *pn, unsigned int *pod) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci unsigned int m, n, od = 1; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * The frequency after the input divider must be between 10 and 50 MHz. 13862306a36Sopenharmony_ci * The highest divider yields the best resolution. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci n = parent_rate / (10 * MHZ); 14162306a36Sopenharmony_ci n = min_t(unsigned int, n, 1 << pll_info->n_bits); 14262306a36Sopenharmony_ci n = max_t(unsigned int, n, pll_info->n_offset); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci m = (rate / MHZ) * od * n / (parent_rate / MHZ); 14562306a36Sopenharmony_ci m = min_t(unsigned int, m, 1 << pll_info->m_bits); 14662306a36Sopenharmony_ci m = max_t(unsigned int, m, pll_info->m_offset); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci *pm = m; 14962306a36Sopenharmony_ci *pn = n; 15062306a36Sopenharmony_ci *pod = od; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic unsigned long 15462306a36Sopenharmony_ciingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info, 15562306a36Sopenharmony_ci unsigned long rate, unsigned long parent_rate, 15662306a36Sopenharmony_ci unsigned int *pm, unsigned int *pn, unsigned int *pod) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; 15962306a36Sopenharmony_ci unsigned int m, n, od; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (pll_info->calc_m_n_od) 16262306a36Sopenharmony_ci (*pll_info->calc_m_n_od)(pll_info, rate, parent_rate, &m, &n, &od); 16362306a36Sopenharmony_ci else 16462306a36Sopenharmony_ci ingenic_pll_calc_m_n_od(pll_info, rate, parent_rate, &m, &n, &od); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (pm) 16762306a36Sopenharmony_ci *pm = m; 16862306a36Sopenharmony_ci if (pn) 16962306a36Sopenharmony_ci *pn = n; 17062306a36Sopenharmony_ci if (pod) 17162306a36Sopenharmony_ci *pod = od; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return div_u64((u64)parent_rate * m * pll_info->rate_multiplier, 17462306a36Sopenharmony_ci n * od); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic long 17862306a36Sopenharmony_ciingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate, 17962306a36Sopenharmony_ci unsigned long *prate) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 18262306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return ingenic_pll_calc(clk_info, req_rate, *prate, NULL, NULL, NULL); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic inline int ingenic_pll_check_stable(struct ingenic_cgu *cgu, 18862306a36Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci u32 ctl; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (pll_info->stable_bit < 0) 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return readl_poll_timeout(cgu->base + pll_info->reg, ctl, 19662306a36Sopenharmony_ci ctl & BIT(pll_info->stable_bit), 19762306a36Sopenharmony_ci 0, 100 * USEC_PER_MSEC); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic int 20162306a36Sopenharmony_ciingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate, 20262306a36Sopenharmony_ci unsigned long parent_rate) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 20562306a36Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 20662306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 20762306a36Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; 20862306a36Sopenharmony_ci unsigned long rate, flags; 20962306a36Sopenharmony_ci unsigned int m, n, od; 21062306a36Sopenharmony_ci int ret = 0; 21162306a36Sopenharmony_ci u32 ctl; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci rate = ingenic_pll_calc(clk_info, req_rate, parent_rate, 21462306a36Sopenharmony_ci &m, &n, &od); 21562306a36Sopenharmony_ci if (rate != req_rate) 21662306a36Sopenharmony_ci pr_info("ingenic-cgu: request '%s' rate %luHz, actual %luHz\n", 21762306a36Sopenharmony_ci clk_info->name, req_rate, rate); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 22062306a36Sopenharmony_ci ctl = readl(cgu->base + pll_info->reg); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ctl &= ~(GENMASK(pll_info->m_bits - 1, 0) << pll_info->m_shift); 22362306a36Sopenharmony_ci ctl |= (m - pll_info->m_offset) << pll_info->m_shift; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci ctl &= ~(GENMASK(pll_info->n_bits - 1, 0) << pll_info->n_shift); 22662306a36Sopenharmony_ci ctl |= (n - pll_info->n_offset) << pll_info->n_shift; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (pll_info->od_bits > 0) { 22962306a36Sopenharmony_ci ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift); 23062306a36Sopenharmony_ci ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci writel(ctl, cgu->base + pll_info->reg); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (pll_info->set_rate_hook) 23662306a36Sopenharmony_ci pll_info->set_rate_hook(pll_info, rate, parent_rate); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* If the PLL is enabled, verify that it's stable */ 23962306a36Sopenharmony_ci if (pll_info->enable_bit >= 0 && (ctl & BIT(pll_info->enable_bit))) 24062306a36Sopenharmony_ci ret = ingenic_pll_check_stable(cgu, pll_info); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return ret; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int ingenic_pll_enable(struct clk_hw *hw) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 25062306a36Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 25162306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 25262306a36Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; 25362306a36Sopenharmony_ci unsigned long flags; 25462306a36Sopenharmony_ci int ret; 25562306a36Sopenharmony_ci u32 ctl; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (pll_info->enable_bit < 0) 25862306a36Sopenharmony_ci return 0; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 26162306a36Sopenharmony_ci if (pll_info->bypass_bit >= 0) { 26262306a36Sopenharmony_ci ctl = readl(cgu->base + pll_info->bypass_reg); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ctl &= ~BIT(pll_info->bypass_bit); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci writel(ctl, cgu->base + pll_info->bypass_reg); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci ctl = readl(cgu->base + pll_info->reg); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci ctl |= BIT(pll_info->enable_bit); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci writel(ctl, cgu->base + pll_info->reg); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci ret = ingenic_pll_check_stable(cgu, pll_info); 27662306a36Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return ret; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic void ingenic_pll_disable(struct clk_hw *hw) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 28462306a36Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 28562306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 28662306a36Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; 28762306a36Sopenharmony_ci unsigned long flags; 28862306a36Sopenharmony_ci u32 ctl; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (pll_info->enable_bit < 0) 29162306a36Sopenharmony_ci return; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 29462306a36Sopenharmony_ci ctl = readl(cgu->base + pll_info->reg); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci ctl &= ~BIT(pll_info->enable_bit); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci writel(ctl, cgu->base + pll_info->reg); 29962306a36Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int ingenic_pll_is_enabled(struct clk_hw *hw) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 30562306a36Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 30662306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 30762306a36Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; 30862306a36Sopenharmony_ci u32 ctl; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (pll_info->enable_bit < 0) 31162306a36Sopenharmony_ci return true; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci ctl = readl(cgu->base + pll_info->reg); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return !!(ctl & BIT(pll_info->enable_bit)); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic const struct clk_ops ingenic_pll_ops = { 31962306a36Sopenharmony_ci .recalc_rate = ingenic_pll_recalc_rate, 32062306a36Sopenharmony_ci .round_rate = ingenic_pll_round_rate, 32162306a36Sopenharmony_ci .set_rate = ingenic_pll_set_rate, 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci .enable = ingenic_pll_enable, 32462306a36Sopenharmony_ci .disable = ingenic_pll_disable, 32562306a36Sopenharmony_ci .is_enabled = ingenic_pll_is_enabled, 32662306a36Sopenharmony_ci}; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/* 32962306a36Sopenharmony_ci * Operations for all non-PLL clocks 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic u8 ingenic_clk_get_parent(struct clk_hw *hw) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 33562306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 33662306a36Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 33762306a36Sopenharmony_ci u32 reg; 33862306a36Sopenharmony_ci u8 i, hw_idx, idx = 0; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (clk_info->type & CGU_CLK_MUX) { 34162306a36Sopenharmony_ci reg = readl(cgu->base + clk_info->mux.reg); 34262306a36Sopenharmony_ci hw_idx = (reg >> clk_info->mux.shift) & 34362306a36Sopenharmony_ci GENMASK(clk_info->mux.bits - 1, 0); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* 34662306a36Sopenharmony_ci * Convert the hardware index to the parent index by skipping 34762306a36Sopenharmony_ci * over any -1's in the parents array. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci for (i = 0; i < hw_idx; i++) { 35062306a36Sopenharmony_ci if (clk_info->parents[i] != -1) 35162306a36Sopenharmony_ci idx++; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci return idx; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int ingenic_clk_set_parent(struct clk_hw *hw, u8 idx) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 36162306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 36262306a36Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 36362306a36Sopenharmony_ci unsigned long flags; 36462306a36Sopenharmony_ci u8 curr_idx, hw_idx, num_poss; 36562306a36Sopenharmony_ci u32 reg, mask; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (clk_info->type & CGU_CLK_MUX) { 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * Convert the parent index to the hardware index by adding 37062306a36Sopenharmony_ci * 1 for any -1 in the parents array preceding the given 37162306a36Sopenharmony_ci * index. That is, we want the index of idx'th entry in 37262306a36Sopenharmony_ci * clk_info->parents which does not equal -1. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci hw_idx = curr_idx = 0; 37562306a36Sopenharmony_ci num_poss = 1 << clk_info->mux.bits; 37662306a36Sopenharmony_ci for (; hw_idx < num_poss; hw_idx++) { 37762306a36Sopenharmony_ci if (clk_info->parents[hw_idx] == -1) 37862306a36Sopenharmony_ci continue; 37962306a36Sopenharmony_ci if (curr_idx == idx) 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci curr_idx++; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* idx should always be a valid parent */ 38562306a36Sopenharmony_ci BUG_ON(curr_idx != idx); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci mask = GENMASK(clk_info->mux.bits - 1, 0); 38862306a36Sopenharmony_ci mask <<= clk_info->mux.shift; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* write the register */ 39362306a36Sopenharmony_ci reg = readl(cgu->base + clk_info->mux.reg); 39462306a36Sopenharmony_ci reg &= ~mask; 39562306a36Sopenharmony_ci reg |= hw_idx << clk_info->mux.shift; 39662306a36Sopenharmony_ci writel(reg, cgu->base + clk_info->mux.reg); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 39962306a36Sopenharmony_ci return 0; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return idx ? -EINVAL : 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic unsigned long 40662306a36Sopenharmony_ciingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 40962306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 41062306a36Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 41162306a36Sopenharmony_ci unsigned long rate = parent_rate; 41262306a36Sopenharmony_ci u32 div_reg, div; 41362306a36Sopenharmony_ci u8 parent; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (clk_info->type & CGU_CLK_DIV) { 41662306a36Sopenharmony_ci parent = ingenic_clk_get_parent(hw); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (!(clk_info->div.bypass_mask & BIT(parent))) { 41962306a36Sopenharmony_ci div_reg = readl(cgu->base + clk_info->div.reg); 42062306a36Sopenharmony_ci div = (div_reg >> clk_info->div.shift) & 42162306a36Sopenharmony_ci GENMASK(clk_info->div.bits - 1, 0); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (clk_info->div.div_table) 42462306a36Sopenharmony_ci div = clk_info->div.div_table[div]; 42562306a36Sopenharmony_ci else 42662306a36Sopenharmony_ci div = (div + 1) * clk_info->div.div; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci rate /= div; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci } else if (clk_info->type & CGU_CLK_FIXDIV) { 43162306a36Sopenharmony_ci rate /= clk_info->fixdiv.div; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return rate; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic unsigned int 43862306a36Sopenharmony_ciingenic_clk_calc_hw_div(const struct ingenic_cgu_clk_info *clk_info, 43962306a36Sopenharmony_ci unsigned int div) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci unsigned int i, best_i = 0, best = (unsigned int)-1; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci for (i = 0; i < (1 << clk_info->div.bits) 44462306a36Sopenharmony_ci && clk_info->div.div_table[i]; i++) { 44562306a36Sopenharmony_ci if (clk_info->div.div_table[i] >= div && 44662306a36Sopenharmony_ci clk_info->div.div_table[i] < best) { 44762306a36Sopenharmony_ci best = clk_info->div.div_table[i]; 44862306a36Sopenharmony_ci best_i = i; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (div == best) 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return best_i; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic unsigned 45962306a36Sopenharmony_ciingenic_clk_calc_div(struct clk_hw *hw, 46062306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info, 46162306a36Sopenharmony_ci unsigned long parent_rate, unsigned long req_rate) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci unsigned int div, hw_div; 46462306a36Sopenharmony_ci u8 parent; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci parent = ingenic_clk_get_parent(hw); 46762306a36Sopenharmony_ci if (clk_info->div.bypass_mask & BIT(parent)) 46862306a36Sopenharmony_ci return 1; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* calculate the divide */ 47162306a36Sopenharmony_ci div = DIV_ROUND_UP(parent_rate, req_rate); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (clk_info->div.div_table) { 47462306a36Sopenharmony_ci hw_div = ingenic_clk_calc_hw_div(clk_info, div); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return clk_info->div.div_table[hw_div]; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Impose hardware constraints */ 48062306a36Sopenharmony_ci div = clamp_t(unsigned int, div, clk_info->div.div, 48162306a36Sopenharmony_ci clk_info->div.div << clk_info->div.bits); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* 48462306a36Sopenharmony_ci * If the divider value itself must be divided before being written to 48562306a36Sopenharmony_ci * the divider register, we must ensure we don't have any bits set that 48662306a36Sopenharmony_ci * would be lost as a result of doing so. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci div = DIV_ROUND_UP(div, clk_info->div.div); 48962306a36Sopenharmony_ci div *= clk_info->div.div; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci return div; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic int ingenic_clk_determine_rate(struct clk_hw *hw, 49562306a36Sopenharmony_ci struct clk_rate_request *req) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 49862306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 49962306a36Sopenharmony_ci unsigned int div = 1; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (clk_info->type & CGU_CLK_DIV) 50262306a36Sopenharmony_ci div = ingenic_clk_calc_div(hw, clk_info, req->best_parent_rate, 50362306a36Sopenharmony_ci req->rate); 50462306a36Sopenharmony_ci else if (clk_info->type & CGU_CLK_FIXDIV) 50562306a36Sopenharmony_ci div = clk_info->fixdiv.div; 50662306a36Sopenharmony_ci else if (clk_hw_can_set_rate_parent(hw)) 50762306a36Sopenharmony_ci req->best_parent_rate = req->rate; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci req->rate = DIV_ROUND_UP(req->best_parent_rate, div); 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic inline int ingenic_clk_check_stable(struct ingenic_cgu *cgu, 51462306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci u32 reg; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return readl_poll_timeout(cgu->base + clk_info->div.reg, reg, 51962306a36Sopenharmony_ci !(reg & BIT(clk_info->div.busy_bit)), 52062306a36Sopenharmony_ci 0, 100 * USEC_PER_MSEC); 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic int 52462306a36Sopenharmony_ciingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate, 52562306a36Sopenharmony_ci unsigned long parent_rate) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 52862306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 52962306a36Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 53062306a36Sopenharmony_ci unsigned long rate, flags; 53162306a36Sopenharmony_ci unsigned int hw_div, div; 53262306a36Sopenharmony_ci u32 reg, mask; 53362306a36Sopenharmony_ci int ret = 0; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (clk_info->type & CGU_CLK_DIV) { 53662306a36Sopenharmony_ci div = ingenic_clk_calc_div(hw, clk_info, parent_rate, req_rate); 53762306a36Sopenharmony_ci rate = DIV_ROUND_UP(parent_rate, div); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (rate != req_rate) 54062306a36Sopenharmony_ci return -EINVAL; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (clk_info->div.div_table) 54362306a36Sopenharmony_ci hw_div = ingenic_clk_calc_hw_div(clk_info, div); 54462306a36Sopenharmony_ci else 54562306a36Sopenharmony_ci hw_div = ((div / clk_info->div.div) - 1); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 54862306a36Sopenharmony_ci reg = readl(cgu->base + clk_info->div.reg); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* update the divide */ 55162306a36Sopenharmony_ci mask = GENMASK(clk_info->div.bits - 1, 0); 55262306a36Sopenharmony_ci reg &= ~(mask << clk_info->div.shift); 55362306a36Sopenharmony_ci reg |= hw_div << clk_info->div.shift; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* clear the stop bit */ 55662306a36Sopenharmony_ci if (clk_info->div.stop_bit != -1) 55762306a36Sopenharmony_ci reg &= ~BIT(clk_info->div.stop_bit); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* set the change enable bit */ 56062306a36Sopenharmony_ci if (clk_info->div.ce_bit != -1) 56162306a36Sopenharmony_ci reg |= BIT(clk_info->div.ce_bit); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* update the hardware */ 56462306a36Sopenharmony_ci writel(reg, cgu->base + clk_info->div.reg); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* wait for the change to take effect */ 56762306a36Sopenharmony_ci if (clk_info->div.busy_bit != -1) 56862306a36Sopenharmony_ci ret = ingenic_clk_check_stable(cgu, clk_info); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return -EINVAL; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic int ingenic_clk_enable(struct clk_hw *hw) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 58062306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 58162306a36Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 58262306a36Sopenharmony_ci unsigned long flags; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (clk_info->type & CGU_CLK_GATE) { 58562306a36Sopenharmony_ci /* ungate the clock */ 58662306a36Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 58762306a36Sopenharmony_ci ingenic_cgu_gate_set(cgu, &clk_info->gate, false); 58862306a36Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (clk_info->gate.delay_us) 59162306a36Sopenharmony_ci udelay(clk_info->gate.delay_us); 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic void ingenic_clk_disable(struct clk_hw *hw) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 60062306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 60162306a36Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 60262306a36Sopenharmony_ci unsigned long flags; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (clk_info->type & CGU_CLK_GATE) { 60562306a36Sopenharmony_ci /* gate the clock */ 60662306a36Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 60762306a36Sopenharmony_ci ingenic_cgu_gate_set(cgu, &clk_info->gate, true); 60862306a36Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int ingenic_clk_is_enabled(struct clk_hw *hw) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 61562306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 61662306a36Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 61762306a36Sopenharmony_ci int enabled = 1; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (clk_info->type & CGU_CLK_GATE) 62062306a36Sopenharmony_ci enabled = !ingenic_cgu_gate_get(cgu, &clk_info->gate); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return enabled; 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic const struct clk_ops ingenic_clk_ops = { 62662306a36Sopenharmony_ci .get_parent = ingenic_clk_get_parent, 62762306a36Sopenharmony_ci .set_parent = ingenic_clk_set_parent, 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci .recalc_rate = ingenic_clk_recalc_rate, 63062306a36Sopenharmony_ci .determine_rate = ingenic_clk_determine_rate, 63162306a36Sopenharmony_ci .set_rate = ingenic_clk_set_rate, 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci .enable = ingenic_clk_enable, 63462306a36Sopenharmony_ci .disable = ingenic_clk_disable, 63562306a36Sopenharmony_ci .is_enabled = ingenic_clk_is_enabled, 63662306a36Sopenharmony_ci}; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/* 63962306a36Sopenharmony_ci * Setup functions. 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic int ingenic_register_clock(struct ingenic_cgu *cgu, unsigned idx) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = &cgu->clock_info[idx]; 64562306a36Sopenharmony_ci struct clk_init_data clk_init; 64662306a36Sopenharmony_ci struct ingenic_clk *ingenic_clk = NULL; 64762306a36Sopenharmony_ci struct clk *clk, *parent; 64862306a36Sopenharmony_ci const char *parent_names[4]; 64962306a36Sopenharmony_ci unsigned caps, i, num_possible; 65062306a36Sopenharmony_ci int err = -EINVAL; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(clk_info->parents) > ARRAY_SIZE(parent_names)); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (clk_info->type == CGU_CLK_EXT) { 65562306a36Sopenharmony_ci clk = of_clk_get_by_name(cgu->np, clk_info->name); 65662306a36Sopenharmony_ci if (IS_ERR(clk)) { 65762306a36Sopenharmony_ci pr_err("%s: no external clock '%s' provided\n", 65862306a36Sopenharmony_ci __func__, clk_info->name); 65962306a36Sopenharmony_ci err = -ENODEV; 66062306a36Sopenharmony_ci goto out; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci err = clk_register_clkdev(clk, clk_info->name, NULL); 66362306a36Sopenharmony_ci if (err) { 66462306a36Sopenharmony_ci clk_put(clk); 66562306a36Sopenharmony_ci goto out; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci cgu->clocks.clks[idx] = clk; 66862306a36Sopenharmony_ci return 0; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (!clk_info->type) { 67262306a36Sopenharmony_ci pr_err("%s: no clock type specified for '%s'\n", __func__, 67362306a36Sopenharmony_ci clk_info->name); 67462306a36Sopenharmony_ci goto out; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci ingenic_clk = kzalloc(sizeof(*ingenic_clk), GFP_KERNEL); 67862306a36Sopenharmony_ci if (!ingenic_clk) { 67962306a36Sopenharmony_ci err = -ENOMEM; 68062306a36Sopenharmony_ci goto out; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci ingenic_clk->hw.init = &clk_init; 68462306a36Sopenharmony_ci ingenic_clk->cgu = cgu; 68562306a36Sopenharmony_ci ingenic_clk->idx = idx; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci clk_init.name = clk_info->name; 68862306a36Sopenharmony_ci clk_init.flags = clk_info->flags; 68962306a36Sopenharmony_ci clk_init.parent_names = parent_names; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci caps = clk_info->type; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (caps & CGU_CLK_DIV) { 69462306a36Sopenharmony_ci caps &= ~CGU_CLK_DIV; 69562306a36Sopenharmony_ci } else if (!(caps & CGU_CLK_CUSTOM)) { 69662306a36Sopenharmony_ci /* pass rate changes to the parent clock */ 69762306a36Sopenharmony_ci clk_init.flags |= CLK_SET_RATE_PARENT; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (caps & (CGU_CLK_MUX | CGU_CLK_CUSTOM)) { 70162306a36Sopenharmony_ci clk_init.num_parents = 0; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (caps & CGU_CLK_MUX) 70462306a36Sopenharmony_ci num_possible = 1 << clk_info->mux.bits; 70562306a36Sopenharmony_ci else 70662306a36Sopenharmony_ci num_possible = ARRAY_SIZE(clk_info->parents); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci for (i = 0; i < num_possible; i++) { 70962306a36Sopenharmony_ci if (clk_info->parents[i] == -1) 71062306a36Sopenharmony_ci continue; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci parent = cgu->clocks.clks[clk_info->parents[i]]; 71362306a36Sopenharmony_ci parent_names[clk_init.num_parents] = 71462306a36Sopenharmony_ci __clk_get_name(parent); 71562306a36Sopenharmony_ci clk_init.num_parents++; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci BUG_ON(!clk_init.num_parents); 71962306a36Sopenharmony_ci BUG_ON(clk_init.num_parents > ARRAY_SIZE(parent_names)); 72062306a36Sopenharmony_ci } else { 72162306a36Sopenharmony_ci BUG_ON(clk_info->parents[0] == -1); 72262306a36Sopenharmony_ci clk_init.num_parents = 1; 72362306a36Sopenharmony_ci parent = cgu->clocks.clks[clk_info->parents[0]]; 72462306a36Sopenharmony_ci parent_names[0] = __clk_get_name(parent); 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (caps & CGU_CLK_CUSTOM) { 72862306a36Sopenharmony_ci clk_init.ops = clk_info->custom.clk_ops; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci caps &= ~CGU_CLK_CUSTOM; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (caps) { 73362306a36Sopenharmony_ci pr_err("%s: custom clock may not be combined with type 0x%x\n", 73462306a36Sopenharmony_ci __func__, caps); 73562306a36Sopenharmony_ci goto out; 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci } else if (caps & CGU_CLK_PLL) { 73862306a36Sopenharmony_ci clk_init.ops = &ingenic_pll_ops; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci caps &= ~CGU_CLK_PLL; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (caps) { 74362306a36Sopenharmony_ci pr_err("%s: PLL may not be combined with type 0x%x\n", 74462306a36Sopenharmony_ci __func__, caps); 74562306a36Sopenharmony_ci goto out; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci } else { 74862306a36Sopenharmony_ci clk_init.ops = &ingenic_clk_ops; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* nothing to do for gates or fixed dividers */ 75262306a36Sopenharmony_ci caps &= ~(CGU_CLK_GATE | CGU_CLK_FIXDIV); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (caps & CGU_CLK_MUX) { 75562306a36Sopenharmony_ci if (!(caps & CGU_CLK_MUX_GLITCHFREE)) 75662306a36Sopenharmony_ci clk_init.flags |= CLK_SET_PARENT_GATE; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci caps &= ~(CGU_CLK_MUX | CGU_CLK_MUX_GLITCHFREE); 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (caps) { 76262306a36Sopenharmony_ci pr_err("%s: unknown clock type 0x%x\n", __func__, caps); 76362306a36Sopenharmony_ci goto out; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci clk = clk_register(NULL, &ingenic_clk->hw); 76762306a36Sopenharmony_ci if (IS_ERR(clk)) { 76862306a36Sopenharmony_ci pr_err("%s: failed to register clock '%s'\n", __func__, 76962306a36Sopenharmony_ci clk_info->name); 77062306a36Sopenharmony_ci err = PTR_ERR(clk); 77162306a36Sopenharmony_ci goto out; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci err = clk_register_clkdev(clk, clk_info->name, NULL); 77562306a36Sopenharmony_ci if (err) 77662306a36Sopenharmony_ci goto out; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci cgu->clocks.clks[idx] = clk; 77962306a36Sopenharmony_ciout: 78062306a36Sopenharmony_ci if (err) 78162306a36Sopenharmony_ci kfree(ingenic_clk); 78262306a36Sopenharmony_ci return err; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistruct ingenic_cgu * 78662306a36Sopenharmony_ciingenic_cgu_new(const struct ingenic_cgu_clk_info *clock_info, 78762306a36Sopenharmony_ci unsigned num_clocks, struct device_node *np) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct ingenic_cgu *cgu; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci cgu = kzalloc(sizeof(*cgu), GFP_KERNEL); 79262306a36Sopenharmony_ci if (!cgu) 79362306a36Sopenharmony_ci goto err_out; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci cgu->base = of_iomap(np, 0); 79662306a36Sopenharmony_ci if (!cgu->base) { 79762306a36Sopenharmony_ci pr_err("%s: failed to map CGU registers\n", __func__); 79862306a36Sopenharmony_ci goto err_out_free; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci cgu->np = np; 80262306a36Sopenharmony_ci cgu->clock_info = clock_info; 80362306a36Sopenharmony_ci cgu->clocks.clk_num = num_clocks; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci spin_lock_init(&cgu->lock); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci return cgu; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cierr_out_free: 81062306a36Sopenharmony_ci kfree(cgu); 81162306a36Sopenharmony_cierr_out: 81262306a36Sopenharmony_ci return NULL; 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ciint ingenic_cgu_register_clocks(struct ingenic_cgu *cgu) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci unsigned i; 81862306a36Sopenharmony_ci int err; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci cgu->clocks.clks = kcalloc(cgu->clocks.clk_num, sizeof(struct clk *), 82162306a36Sopenharmony_ci GFP_KERNEL); 82262306a36Sopenharmony_ci if (!cgu->clocks.clks) { 82362306a36Sopenharmony_ci err = -ENOMEM; 82462306a36Sopenharmony_ci goto err_out; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci for (i = 0; i < cgu->clocks.clk_num; i++) { 82862306a36Sopenharmony_ci err = ingenic_register_clock(cgu, i); 82962306a36Sopenharmony_ci if (err) 83062306a36Sopenharmony_ci goto err_out_unregister; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci err = of_clk_add_provider(cgu->np, of_clk_src_onecell_get, 83462306a36Sopenharmony_ci &cgu->clocks); 83562306a36Sopenharmony_ci if (err) 83662306a36Sopenharmony_ci goto err_out_unregister; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cierr_out_unregister: 84162306a36Sopenharmony_ci for (i = 0; i < cgu->clocks.clk_num; i++) { 84262306a36Sopenharmony_ci if (!cgu->clocks.clks[i]) 84362306a36Sopenharmony_ci continue; 84462306a36Sopenharmony_ci if (cgu->clock_info[i].type & CGU_CLK_EXT) 84562306a36Sopenharmony_ci clk_put(cgu->clocks.clks[i]); 84662306a36Sopenharmony_ci else 84762306a36Sopenharmony_ci clk_unregister(cgu->clocks.clks[i]); 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci kfree(cgu->clocks.clks); 85062306a36Sopenharmony_cierr_out: 85162306a36Sopenharmony_ci return err; 85262306a36Sopenharmony_ci} 853