18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Ingenic SoC CGU driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2013-2015 Imagination Technologies 68c2ecf20Sopenharmony_ci * Author: Paul Burton <paul.burton@mips.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/bitops.h> 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 128c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 168c2ecf20Sopenharmony_ci#include <linux/math64.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_address.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 218c2ecf20Sopenharmony_ci#include <linux/time.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "cgu.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define MHZ (1000 * 1000) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic inline const struct ingenic_cgu_clk_info * 288c2ecf20Sopenharmony_cito_clk_info(struct ingenic_clk *clk) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return &clk->cgu->clock_info[clk->idx]; 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/** 348c2ecf20Sopenharmony_ci * ingenic_cgu_gate_get() - get the value of clock gate register bit 358c2ecf20Sopenharmony_ci * @cgu: reference to the CGU whose registers should be read 368c2ecf20Sopenharmony_ci * @info: info struct describing the gate bit 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * Retrieves the state of the clock gate bit described by info. The 398c2ecf20Sopenharmony_ci * caller must hold cgu->lock. 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * Return: true if the gate bit is set, else false. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_cistatic inline bool 448c2ecf20Sopenharmony_ciingenic_cgu_gate_get(struct ingenic_cgu *cgu, 458c2ecf20Sopenharmony_ci const struct ingenic_cgu_gate_info *info) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci return !!(readl(cgu->base + info->reg) & BIT(info->bit)) 488c2ecf20Sopenharmony_ci ^ info->clear_to_gate; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/** 528c2ecf20Sopenharmony_ci * ingenic_cgu_gate_set() - set the value of clock gate register bit 538c2ecf20Sopenharmony_ci * @cgu: reference to the CGU whose registers should be modified 548c2ecf20Sopenharmony_ci * @info: info struct describing the gate bit 558c2ecf20Sopenharmony_ci * @val: non-zero to gate a clock, otherwise zero 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * Sets the given gate bit in order to gate or ungate a clock. 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * The caller must hold cgu->lock. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistatic inline void 628c2ecf20Sopenharmony_ciingenic_cgu_gate_set(struct ingenic_cgu *cgu, 638c2ecf20Sopenharmony_ci const struct ingenic_cgu_gate_info *info, bool val) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci u32 clkgr = readl(cgu->base + info->reg); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (val ^ info->clear_to_gate) 688c2ecf20Sopenharmony_ci clkgr |= BIT(info->bit); 698c2ecf20Sopenharmony_ci else 708c2ecf20Sopenharmony_ci clkgr &= ~BIT(info->bit); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci writel(clkgr, cgu->base + info->reg); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci * PLL operations 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic unsigned long 808c2ecf20Sopenharmony_ciingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 838c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 848c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 858c2ecf20Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info; 868c2ecf20Sopenharmony_ci unsigned m, n, od_enc, od; 878c2ecf20Sopenharmony_ci bool bypass; 888c2ecf20Sopenharmony_ci u32 ctl; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci BUG_ON(clk_info->type != CGU_CLK_PLL); 918c2ecf20Sopenharmony_ci pll_info = &clk_info->pll; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ctl = readl(cgu->base + pll_info->reg); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci m = (ctl >> pll_info->m_shift) & GENMASK(pll_info->m_bits - 1, 0); 968c2ecf20Sopenharmony_ci m += pll_info->m_offset; 978c2ecf20Sopenharmony_ci n = (ctl >> pll_info->n_shift) & GENMASK(pll_info->n_bits - 1, 0); 988c2ecf20Sopenharmony_ci n += pll_info->n_offset; 998c2ecf20Sopenharmony_ci od_enc = ctl >> pll_info->od_shift; 1008c2ecf20Sopenharmony_ci od_enc &= GENMASK(pll_info->od_bits - 1, 0); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci ctl = readl(cgu->base + pll_info->bypass_reg); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci bypass = !pll_info->no_bypass_bit && 1058c2ecf20Sopenharmony_ci !!(ctl & BIT(pll_info->bypass_bit)); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (bypass) 1088c2ecf20Sopenharmony_ci return parent_rate; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci for (od = 0; od < pll_info->od_max; od++) { 1118c2ecf20Sopenharmony_ci if (pll_info->od_encoding[od] == od_enc) 1128c2ecf20Sopenharmony_ci break; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci BUG_ON(od == pll_info->od_max); 1158c2ecf20Sopenharmony_ci od++; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return div_u64((u64)parent_rate * m * pll_info->rate_multiplier, 1188c2ecf20Sopenharmony_ci n * od); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic unsigned long 1228c2ecf20Sopenharmony_ciingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info, 1238c2ecf20Sopenharmony_ci unsigned long rate, unsigned long parent_rate, 1248c2ecf20Sopenharmony_ci unsigned *pm, unsigned *pn, unsigned *pod) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info; 1278c2ecf20Sopenharmony_ci unsigned m, n, od; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci pll_info = &clk_info->pll; 1308c2ecf20Sopenharmony_ci od = 1; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* 1338c2ecf20Sopenharmony_ci * The frequency after the input divider must be between 10 and 50 MHz. 1348c2ecf20Sopenharmony_ci * The highest divider yields the best resolution. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci n = parent_rate / (10 * MHZ); 1378c2ecf20Sopenharmony_ci n = min_t(unsigned, n, 1 << clk_info->pll.n_bits); 1388c2ecf20Sopenharmony_ci n = max_t(unsigned, n, pll_info->n_offset); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci m = (rate / MHZ) * od * n / (parent_rate / MHZ); 1418c2ecf20Sopenharmony_ci m = min_t(unsigned, m, 1 << clk_info->pll.m_bits); 1428c2ecf20Sopenharmony_ci m = max_t(unsigned, m, pll_info->m_offset); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (pm) 1458c2ecf20Sopenharmony_ci *pm = m; 1468c2ecf20Sopenharmony_ci if (pn) 1478c2ecf20Sopenharmony_ci *pn = n; 1488c2ecf20Sopenharmony_ci if (pod) 1498c2ecf20Sopenharmony_ci *pod = od; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return div_u64((u64)parent_rate * m * pll_info->rate_multiplier, 1528c2ecf20Sopenharmony_ci n * od); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic long 1568c2ecf20Sopenharmony_ciingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate, 1578c2ecf20Sopenharmony_ci unsigned long *prate) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 1608c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return ingenic_pll_calc(clk_info, req_rate, *prate, NULL, NULL, NULL); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic inline int ingenic_pll_check_stable(struct ingenic_cgu *cgu, 1668c2ecf20Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci u32 ctl; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return readl_poll_timeout(cgu->base + pll_info->reg, ctl, 1718c2ecf20Sopenharmony_ci ctl & BIT(pll_info->stable_bit), 1728c2ecf20Sopenharmony_ci 0, 100 * USEC_PER_MSEC); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int 1768c2ecf20Sopenharmony_ciingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate, 1778c2ecf20Sopenharmony_ci unsigned long parent_rate) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 1808c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 1818c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 1828c2ecf20Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; 1838c2ecf20Sopenharmony_ci unsigned long rate, flags; 1848c2ecf20Sopenharmony_ci unsigned int m, n, od; 1858c2ecf20Sopenharmony_ci int ret = 0; 1868c2ecf20Sopenharmony_ci u32 ctl; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci rate = ingenic_pll_calc(clk_info, req_rate, parent_rate, 1898c2ecf20Sopenharmony_ci &m, &n, &od); 1908c2ecf20Sopenharmony_ci if (rate != req_rate) 1918c2ecf20Sopenharmony_ci pr_info("ingenic-cgu: request '%s' rate %luHz, actual %luHz\n", 1928c2ecf20Sopenharmony_ci clk_info->name, req_rate, rate); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 1958c2ecf20Sopenharmony_ci ctl = readl(cgu->base + pll_info->reg); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ctl &= ~(GENMASK(pll_info->m_bits - 1, 0) << pll_info->m_shift); 1988c2ecf20Sopenharmony_ci ctl |= (m - pll_info->m_offset) << pll_info->m_shift; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci ctl &= ~(GENMASK(pll_info->n_bits - 1, 0) << pll_info->n_shift); 2018c2ecf20Sopenharmony_ci ctl |= (n - pll_info->n_offset) << pll_info->n_shift; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift); 2048c2ecf20Sopenharmony_ci ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci writel(ctl, cgu->base + pll_info->reg); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* If the PLL is enabled, verify that it's stable */ 2098c2ecf20Sopenharmony_ci if (ctl & BIT(pll_info->enable_bit)) 2108c2ecf20Sopenharmony_ci ret = ingenic_pll_check_stable(cgu, pll_info); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int ingenic_pll_enable(struct clk_hw *hw) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 2208c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 2218c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 2228c2ecf20Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; 2238c2ecf20Sopenharmony_ci unsigned long flags; 2248c2ecf20Sopenharmony_ci int ret; 2258c2ecf20Sopenharmony_ci u32 ctl; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 2288c2ecf20Sopenharmony_ci ctl = readl(cgu->base + pll_info->bypass_reg); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci ctl &= ~BIT(pll_info->bypass_bit); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci writel(ctl, cgu->base + pll_info->bypass_reg); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci ctl = readl(cgu->base + pll_info->reg); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci ctl |= BIT(pll_info->enable_bit); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci writel(ctl, cgu->base + pll_info->reg); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci ret = ingenic_pll_check_stable(cgu, pll_info); 2418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return ret; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic void ingenic_pll_disable(struct clk_hw *hw) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 2498c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 2508c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 2518c2ecf20Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; 2528c2ecf20Sopenharmony_ci unsigned long flags; 2538c2ecf20Sopenharmony_ci u32 ctl; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 2568c2ecf20Sopenharmony_ci ctl = readl(cgu->base + pll_info->reg); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci ctl &= ~BIT(pll_info->enable_bit); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci writel(ctl, cgu->base + pll_info->reg); 2618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int ingenic_pll_is_enabled(struct clk_hw *hw) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 2678c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 2688c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 2698c2ecf20Sopenharmony_ci const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; 2708c2ecf20Sopenharmony_ci u32 ctl; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ctl = readl(cgu->base + pll_info->reg); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return !!(ctl & BIT(pll_info->enable_bit)); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic const struct clk_ops ingenic_pll_ops = { 2788c2ecf20Sopenharmony_ci .recalc_rate = ingenic_pll_recalc_rate, 2798c2ecf20Sopenharmony_ci .round_rate = ingenic_pll_round_rate, 2808c2ecf20Sopenharmony_ci .set_rate = ingenic_pll_set_rate, 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci .enable = ingenic_pll_enable, 2838c2ecf20Sopenharmony_ci .disable = ingenic_pll_disable, 2848c2ecf20Sopenharmony_ci .is_enabled = ingenic_pll_is_enabled, 2858c2ecf20Sopenharmony_ci}; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* 2888c2ecf20Sopenharmony_ci * Operations for all non-PLL clocks 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic u8 ingenic_clk_get_parent(struct clk_hw *hw) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 2948c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 2958c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 2968c2ecf20Sopenharmony_ci u32 reg; 2978c2ecf20Sopenharmony_ci u8 i, hw_idx, idx = 0; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (clk_info->type & CGU_CLK_MUX) { 3008c2ecf20Sopenharmony_ci reg = readl(cgu->base + clk_info->mux.reg); 3018c2ecf20Sopenharmony_ci hw_idx = (reg >> clk_info->mux.shift) & 3028c2ecf20Sopenharmony_ci GENMASK(clk_info->mux.bits - 1, 0); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* 3058c2ecf20Sopenharmony_ci * Convert the hardware index to the parent index by skipping 3068c2ecf20Sopenharmony_ci * over any -1's in the parents array. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci for (i = 0; i < hw_idx; i++) { 3098c2ecf20Sopenharmony_ci if (clk_info->parents[i] != -1) 3108c2ecf20Sopenharmony_ci idx++; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return idx; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int ingenic_clk_set_parent(struct clk_hw *hw, u8 idx) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 3208c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 3218c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 3228c2ecf20Sopenharmony_ci unsigned long flags; 3238c2ecf20Sopenharmony_ci u8 curr_idx, hw_idx, num_poss; 3248c2ecf20Sopenharmony_ci u32 reg, mask; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (clk_info->type & CGU_CLK_MUX) { 3278c2ecf20Sopenharmony_ci /* 3288c2ecf20Sopenharmony_ci * Convert the parent index to the hardware index by adding 3298c2ecf20Sopenharmony_ci * 1 for any -1 in the parents array preceding the given 3308c2ecf20Sopenharmony_ci * index. That is, we want the index of idx'th entry in 3318c2ecf20Sopenharmony_ci * clk_info->parents which does not equal -1. 3328c2ecf20Sopenharmony_ci */ 3338c2ecf20Sopenharmony_ci hw_idx = curr_idx = 0; 3348c2ecf20Sopenharmony_ci num_poss = 1 << clk_info->mux.bits; 3358c2ecf20Sopenharmony_ci for (; hw_idx < num_poss; hw_idx++) { 3368c2ecf20Sopenharmony_ci if (clk_info->parents[hw_idx] == -1) 3378c2ecf20Sopenharmony_ci continue; 3388c2ecf20Sopenharmony_ci if (curr_idx == idx) 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci curr_idx++; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* idx should always be a valid parent */ 3448c2ecf20Sopenharmony_ci BUG_ON(curr_idx != idx); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci mask = GENMASK(clk_info->mux.bits - 1, 0); 3478c2ecf20Sopenharmony_ci mask <<= clk_info->mux.shift; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* write the register */ 3528c2ecf20Sopenharmony_ci reg = readl(cgu->base + clk_info->mux.reg); 3538c2ecf20Sopenharmony_ci reg &= ~mask; 3548c2ecf20Sopenharmony_ci reg |= hw_idx << clk_info->mux.shift; 3558c2ecf20Sopenharmony_ci writel(reg, cgu->base + clk_info->mux.reg); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return idx ? -EINVAL : 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic unsigned long 3658c2ecf20Sopenharmony_ciingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 3688c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 3698c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 3708c2ecf20Sopenharmony_ci unsigned long rate = parent_rate; 3718c2ecf20Sopenharmony_ci u32 div_reg, div; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (clk_info->type & CGU_CLK_DIV) { 3748c2ecf20Sopenharmony_ci div_reg = readl(cgu->base + clk_info->div.reg); 3758c2ecf20Sopenharmony_ci div = (div_reg >> clk_info->div.shift) & 3768c2ecf20Sopenharmony_ci GENMASK(clk_info->div.bits - 1, 0); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (clk_info->div.div_table) 3798c2ecf20Sopenharmony_ci div = clk_info->div.div_table[div]; 3808c2ecf20Sopenharmony_ci else 3818c2ecf20Sopenharmony_ci div = (div + 1) * clk_info->div.div; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci rate /= div; 3848c2ecf20Sopenharmony_ci } else if (clk_info->type & CGU_CLK_FIXDIV) { 3858c2ecf20Sopenharmony_ci rate /= clk_info->fixdiv.div; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return rate; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic unsigned int 3928c2ecf20Sopenharmony_ciingenic_clk_calc_hw_div(const struct ingenic_cgu_clk_info *clk_info, 3938c2ecf20Sopenharmony_ci unsigned int div) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci unsigned int i, best_i = 0, best = (unsigned int)-1; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci for (i = 0; i < (1 << clk_info->div.bits) 3988c2ecf20Sopenharmony_ci && clk_info->div.div_table[i]; i++) { 3998c2ecf20Sopenharmony_ci if (clk_info->div.div_table[i] >= div && 4008c2ecf20Sopenharmony_ci clk_info->div.div_table[i] < best) { 4018c2ecf20Sopenharmony_ci best = clk_info->div.div_table[i]; 4028c2ecf20Sopenharmony_ci best_i = i; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (div == best) 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return best_i; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic unsigned 4138c2ecf20Sopenharmony_ciingenic_clk_calc_div(const struct ingenic_cgu_clk_info *clk_info, 4148c2ecf20Sopenharmony_ci unsigned long parent_rate, unsigned long req_rate) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci unsigned int div, hw_div; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* calculate the divide */ 4198c2ecf20Sopenharmony_ci div = DIV_ROUND_UP(parent_rate, req_rate); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (clk_info->div.div_table) { 4228c2ecf20Sopenharmony_ci hw_div = ingenic_clk_calc_hw_div(clk_info, div); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return clk_info->div.div_table[hw_div]; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Impose hardware constraints */ 4288c2ecf20Sopenharmony_ci div = clamp_t(unsigned int, div, clk_info->div.div, 4298c2ecf20Sopenharmony_ci clk_info->div.div << clk_info->div.bits); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* 4328c2ecf20Sopenharmony_ci * If the divider value itself must be divided before being written to 4338c2ecf20Sopenharmony_ci * the divider register, we must ensure we don't have any bits set that 4348c2ecf20Sopenharmony_ci * would be lost as a result of doing so. 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_ci div = DIV_ROUND_UP(div, clk_info->div.div); 4378c2ecf20Sopenharmony_ci div *= clk_info->div.div; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return div; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic long 4438c2ecf20Sopenharmony_ciingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate, 4448c2ecf20Sopenharmony_ci unsigned long *parent_rate) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 4478c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 4488c2ecf20Sopenharmony_ci unsigned int div = 1; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (clk_info->type & CGU_CLK_DIV) 4518c2ecf20Sopenharmony_ci div = ingenic_clk_calc_div(clk_info, *parent_rate, req_rate); 4528c2ecf20Sopenharmony_ci else if (clk_info->type & CGU_CLK_FIXDIV) 4538c2ecf20Sopenharmony_ci div = clk_info->fixdiv.div; 4548c2ecf20Sopenharmony_ci else if (clk_hw_can_set_rate_parent(hw)) 4558c2ecf20Sopenharmony_ci *parent_rate = req_rate; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return DIV_ROUND_UP(*parent_rate, div); 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic inline int ingenic_clk_check_stable(struct ingenic_cgu *cgu, 4618c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci u32 reg; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return readl_poll_timeout(cgu->base + clk_info->div.reg, reg, 4668c2ecf20Sopenharmony_ci !(reg & BIT(clk_info->div.busy_bit)), 4678c2ecf20Sopenharmony_ci 0, 100 * USEC_PER_MSEC); 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int 4718c2ecf20Sopenharmony_ciingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate, 4728c2ecf20Sopenharmony_ci unsigned long parent_rate) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 4758c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 4768c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 4778c2ecf20Sopenharmony_ci unsigned long rate, flags; 4788c2ecf20Sopenharmony_ci unsigned int hw_div, div; 4798c2ecf20Sopenharmony_ci u32 reg, mask; 4808c2ecf20Sopenharmony_ci int ret = 0; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (clk_info->type & CGU_CLK_DIV) { 4838c2ecf20Sopenharmony_ci div = ingenic_clk_calc_div(clk_info, parent_rate, req_rate); 4848c2ecf20Sopenharmony_ci rate = DIV_ROUND_UP(parent_rate, div); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (rate != req_rate) 4878c2ecf20Sopenharmony_ci return -EINVAL; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (clk_info->div.div_table) 4908c2ecf20Sopenharmony_ci hw_div = ingenic_clk_calc_hw_div(clk_info, div); 4918c2ecf20Sopenharmony_ci else 4928c2ecf20Sopenharmony_ci hw_div = ((div / clk_info->div.div) - 1); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 4958c2ecf20Sopenharmony_ci reg = readl(cgu->base + clk_info->div.reg); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* update the divide */ 4988c2ecf20Sopenharmony_ci mask = GENMASK(clk_info->div.bits - 1, 0); 4998c2ecf20Sopenharmony_ci reg &= ~(mask << clk_info->div.shift); 5008c2ecf20Sopenharmony_ci reg |= hw_div << clk_info->div.shift; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* clear the stop bit */ 5038c2ecf20Sopenharmony_ci if (clk_info->div.stop_bit != -1) 5048c2ecf20Sopenharmony_ci reg &= ~BIT(clk_info->div.stop_bit); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* set the change enable bit */ 5078c2ecf20Sopenharmony_ci if (clk_info->div.ce_bit != -1) 5088c2ecf20Sopenharmony_ci reg |= BIT(clk_info->div.ce_bit); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* update the hardware */ 5118c2ecf20Sopenharmony_ci writel(reg, cgu->base + clk_info->div.reg); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* wait for the change to take effect */ 5148c2ecf20Sopenharmony_ci if (clk_info->div.busy_bit != -1) 5158c2ecf20Sopenharmony_ci ret = ingenic_clk_check_stable(cgu, clk_info); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return -EINVAL; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic int ingenic_clk_enable(struct clk_hw *hw) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 5278c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 5288c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 5298c2ecf20Sopenharmony_ci unsigned long flags; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (clk_info->type & CGU_CLK_GATE) { 5328c2ecf20Sopenharmony_ci /* ungate the clock */ 5338c2ecf20Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 5348c2ecf20Sopenharmony_ci ingenic_cgu_gate_set(cgu, &clk_info->gate, false); 5358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (clk_info->gate.delay_us) 5388c2ecf20Sopenharmony_ci udelay(clk_info->gate.delay_us); 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return 0; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic void ingenic_clk_disable(struct clk_hw *hw) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 5478c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 5488c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 5498c2ecf20Sopenharmony_ci unsigned long flags; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (clk_info->type & CGU_CLK_GATE) { 5528c2ecf20Sopenharmony_ci /* gate the clock */ 5538c2ecf20Sopenharmony_ci spin_lock_irqsave(&cgu->lock, flags); 5548c2ecf20Sopenharmony_ci ingenic_cgu_gate_set(cgu, &clk_info->gate, true); 5558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cgu->lock, flags); 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic int ingenic_clk_is_enabled(struct clk_hw *hw) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); 5628c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk); 5638c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu = ingenic_clk->cgu; 5648c2ecf20Sopenharmony_ci int enabled = 1; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if (clk_info->type & CGU_CLK_GATE) 5678c2ecf20Sopenharmony_ci enabled = !ingenic_cgu_gate_get(cgu, &clk_info->gate); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci return enabled; 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic const struct clk_ops ingenic_clk_ops = { 5738c2ecf20Sopenharmony_ci .get_parent = ingenic_clk_get_parent, 5748c2ecf20Sopenharmony_ci .set_parent = ingenic_clk_set_parent, 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci .recalc_rate = ingenic_clk_recalc_rate, 5778c2ecf20Sopenharmony_ci .round_rate = ingenic_clk_round_rate, 5788c2ecf20Sopenharmony_ci .set_rate = ingenic_clk_set_rate, 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci .enable = ingenic_clk_enable, 5818c2ecf20Sopenharmony_ci .disable = ingenic_clk_disable, 5828c2ecf20Sopenharmony_ci .is_enabled = ingenic_clk_is_enabled, 5838c2ecf20Sopenharmony_ci}; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/* 5868c2ecf20Sopenharmony_ci * Setup functions. 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic int ingenic_register_clock(struct ingenic_cgu *cgu, unsigned idx) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci const struct ingenic_cgu_clk_info *clk_info = &cgu->clock_info[idx]; 5928c2ecf20Sopenharmony_ci struct clk_init_data clk_init; 5938c2ecf20Sopenharmony_ci struct ingenic_clk *ingenic_clk = NULL; 5948c2ecf20Sopenharmony_ci struct clk *clk, *parent; 5958c2ecf20Sopenharmony_ci const char *parent_names[4]; 5968c2ecf20Sopenharmony_ci unsigned caps, i, num_possible; 5978c2ecf20Sopenharmony_ci int err = -EINVAL; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(clk_info->parents) > ARRAY_SIZE(parent_names)); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (clk_info->type == CGU_CLK_EXT) { 6028c2ecf20Sopenharmony_ci clk = of_clk_get_by_name(cgu->np, clk_info->name); 6038c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 6048c2ecf20Sopenharmony_ci pr_err("%s: no external clock '%s' provided\n", 6058c2ecf20Sopenharmony_ci __func__, clk_info->name); 6068c2ecf20Sopenharmony_ci err = -ENODEV; 6078c2ecf20Sopenharmony_ci goto out; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci err = clk_register_clkdev(clk, clk_info->name, NULL); 6108c2ecf20Sopenharmony_ci if (err) { 6118c2ecf20Sopenharmony_ci clk_put(clk); 6128c2ecf20Sopenharmony_ci goto out; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci cgu->clocks.clks[idx] = clk; 6158c2ecf20Sopenharmony_ci return 0; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (!clk_info->type) { 6198c2ecf20Sopenharmony_ci pr_err("%s: no clock type specified for '%s'\n", __func__, 6208c2ecf20Sopenharmony_ci clk_info->name); 6218c2ecf20Sopenharmony_ci goto out; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci ingenic_clk = kzalloc(sizeof(*ingenic_clk), GFP_KERNEL); 6258c2ecf20Sopenharmony_ci if (!ingenic_clk) { 6268c2ecf20Sopenharmony_ci err = -ENOMEM; 6278c2ecf20Sopenharmony_ci goto out; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci ingenic_clk->hw.init = &clk_init; 6318c2ecf20Sopenharmony_ci ingenic_clk->cgu = cgu; 6328c2ecf20Sopenharmony_ci ingenic_clk->idx = idx; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci clk_init.name = clk_info->name; 6358c2ecf20Sopenharmony_ci clk_init.flags = 0; 6368c2ecf20Sopenharmony_ci clk_init.parent_names = parent_names; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci caps = clk_info->type; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (caps & CGU_CLK_DIV) { 6418c2ecf20Sopenharmony_ci caps &= ~CGU_CLK_DIV; 6428c2ecf20Sopenharmony_ci } else if (!(caps & CGU_CLK_CUSTOM)) { 6438c2ecf20Sopenharmony_ci /* pass rate changes to the parent clock */ 6448c2ecf20Sopenharmony_ci clk_init.flags |= CLK_SET_RATE_PARENT; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (caps & (CGU_CLK_MUX | CGU_CLK_CUSTOM)) { 6488c2ecf20Sopenharmony_ci clk_init.num_parents = 0; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (caps & CGU_CLK_MUX) 6518c2ecf20Sopenharmony_ci num_possible = 1 << clk_info->mux.bits; 6528c2ecf20Sopenharmony_ci else 6538c2ecf20Sopenharmony_ci num_possible = ARRAY_SIZE(clk_info->parents); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci for (i = 0; i < num_possible; i++) { 6568c2ecf20Sopenharmony_ci if (clk_info->parents[i] == -1) 6578c2ecf20Sopenharmony_ci continue; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci parent = cgu->clocks.clks[clk_info->parents[i]]; 6608c2ecf20Sopenharmony_ci parent_names[clk_init.num_parents] = 6618c2ecf20Sopenharmony_ci __clk_get_name(parent); 6628c2ecf20Sopenharmony_ci clk_init.num_parents++; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci BUG_ON(!clk_init.num_parents); 6668c2ecf20Sopenharmony_ci BUG_ON(clk_init.num_parents > ARRAY_SIZE(parent_names)); 6678c2ecf20Sopenharmony_ci } else { 6688c2ecf20Sopenharmony_ci BUG_ON(clk_info->parents[0] == -1); 6698c2ecf20Sopenharmony_ci clk_init.num_parents = 1; 6708c2ecf20Sopenharmony_ci parent = cgu->clocks.clks[clk_info->parents[0]]; 6718c2ecf20Sopenharmony_ci parent_names[0] = __clk_get_name(parent); 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (caps & CGU_CLK_CUSTOM) { 6758c2ecf20Sopenharmony_ci clk_init.ops = clk_info->custom.clk_ops; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci caps &= ~CGU_CLK_CUSTOM; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (caps) { 6808c2ecf20Sopenharmony_ci pr_err("%s: custom clock may not be combined with type 0x%x\n", 6818c2ecf20Sopenharmony_ci __func__, caps); 6828c2ecf20Sopenharmony_ci goto out; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci } else if (caps & CGU_CLK_PLL) { 6858c2ecf20Sopenharmony_ci clk_init.ops = &ingenic_pll_ops; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci caps &= ~CGU_CLK_PLL; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (caps) { 6908c2ecf20Sopenharmony_ci pr_err("%s: PLL may not be combined with type 0x%x\n", 6918c2ecf20Sopenharmony_ci __func__, caps); 6928c2ecf20Sopenharmony_ci goto out; 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci } else { 6958c2ecf20Sopenharmony_ci clk_init.ops = &ingenic_clk_ops; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci /* nothing to do for gates or fixed dividers */ 6998c2ecf20Sopenharmony_ci caps &= ~(CGU_CLK_GATE | CGU_CLK_FIXDIV); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (caps & CGU_CLK_MUX) { 7028c2ecf20Sopenharmony_ci if (!(caps & CGU_CLK_MUX_GLITCHFREE)) 7038c2ecf20Sopenharmony_ci clk_init.flags |= CLK_SET_PARENT_GATE; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci caps &= ~(CGU_CLK_MUX | CGU_CLK_MUX_GLITCHFREE); 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (caps) { 7098c2ecf20Sopenharmony_ci pr_err("%s: unknown clock type 0x%x\n", __func__, caps); 7108c2ecf20Sopenharmony_ci goto out; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci clk = clk_register(NULL, &ingenic_clk->hw); 7148c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 7158c2ecf20Sopenharmony_ci pr_err("%s: failed to register clock '%s'\n", __func__, 7168c2ecf20Sopenharmony_ci clk_info->name); 7178c2ecf20Sopenharmony_ci err = PTR_ERR(clk); 7188c2ecf20Sopenharmony_ci goto out; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci err = clk_register_clkdev(clk, clk_info->name, NULL); 7228c2ecf20Sopenharmony_ci if (err) 7238c2ecf20Sopenharmony_ci goto out; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci cgu->clocks.clks[idx] = clk; 7268c2ecf20Sopenharmony_ciout: 7278c2ecf20Sopenharmony_ci if (err) 7288c2ecf20Sopenharmony_ci kfree(ingenic_clk); 7298c2ecf20Sopenharmony_ci return err; 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistruct ingenic_cgu * 7338c2ecf20Sopenharmony_ciingenic_cgu_new(const struct ingenic_cgu_clk_info *clock_info, 7348c2ecf20Sopenharmony_ci unsigned num_clocks, struct device_node *np) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct ingenic_cgu *cgu; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci cgu = kzalloc(sizeof(*cgu), GFP_KERNEL); 7398c2ecf20Sopenharmony_ci if (!cgu) 7408c2ecf20Sopenharmony_ci goto err_out; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci cgu->base = of_iomap(np, 0); 7438c2ecf20Sopenharmony_ci if (!cgu->base) { 7448c2ecf20Sopenharmony_ci pr_err("%s: failed to map CGU registers\n", __func__); 7458c2ecf20Sopenharmony_ci goto err_out_free; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci cgu->np = np; 7498c2ecf20Sopenharmony_ci cgu->clock_info = clock_info; 7508c2ecf20Sopenharmony_ci cgu->clocks.clk_num = num_clocks; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci spin_lock_init(&cgu->lock); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return cgu; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cierr_out_free: 7578c2ecf20Sopenharmony_ci kfree(cgu); 7588c2ecf20Sopenharmony_cierr_out: 7598c2ecf20Sopenharmony_ci return NULL; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ciint ingenic_cgu_register_clocks(struct ingenic_cgu *cgu) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci unsigned i; 7658c2ecf20Sopenharmony_ci int err; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci cgu->clocks.clks = kcalloc(cgu->clocks.clk_num, sizeof(struct clk *), 7688c2ecf20Sopenharmony_ci GFP_KERNEL); 7698c2ecf20Sopenharmony_ci if (!cgu->clocks.clks) { 7708c2ecf20Sopenharmony_ci err = -ENOMEM; 7718c2ecf20Sopenharmony_ci goto err_out; 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci for (i = 0; i < cgu->clocks.clk_num; i++) { 7758c2ecf20Sopenharmony_ci err = ingenic_register_clock(cgu, i); 7768c2ecf20Sopenharmony_ci if (err) 7778c2ecf20Sopenharmony_ci goto err_out_unregister; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci err = of_clk_add_provider(cgu->np, of_clk_src_onecell_get, 7818c2ecf20Sopenharmony_ci &cgu->clocks); 7828c2ecf20Sopenharmony_ci if (err) 7838c2ecf20Sopenharmony_ci goto err_out_unregister; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci return 0; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cierr_out_unregister: 7888c2ecf20Sopenharmony_ci for (i = 0; i < cgu->clocks.clk_num; i++) { 7898c2ecf20Sopenharmony_ci if (!cgu->clocks.clks[i]) 7908c2ecf20Sopenharmony_ci continue; 7918c2ecf20Sopenharmony_ci if (cgu->clock_info[i].type & CGU_CLK_EXT) 7928c2ecf20Sopenharmony_ci clk_put(cgu->clocks.clks[i]); 7938c2ecf20Sopenharmony_ci else 7948c2ecf20Sopenharmony_ci clk_unregister(cgu->clocks.clks[i]); 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci kfree(cgu->clocks.clks); 7978c2ecf20Sopenharmony_cierr_out: 7988c2ecf20Sopenharmony_ci return err; 7998c2ecf20Sopenharmony_ci} 800