18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Maxime Ripard 48c2ecf20Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 88c2ecf20Sopenharmony_ci#include <linux/io.h> 98c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "ccu_frac.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cibool ccu_frac_helper_is_enabled(struct ccu_common *common, 148c2ecf20Sopenharmony_ci struct ccu_frac_internal *cf) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci if (!(common->features & CCU_FEATURE_FRACTIONAL)) 178c2ecf20Sopenharmony_ci return false; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci return !(readl(common->base + common->reg) & cf->enable); 208c2ecf20Sopenharmony_ci} 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_civoid ccu_frac_helper_enable(struct ccu_common *common, 238c2ecf20Sopenharmony_ci struct ccu_frac_internal *cf) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci unsigned long flags; 268c2ecf20Sopenharmony_ci u32 reg; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci if (!(common->features & CCU_FEATURE_FRACTIONAL)) 298c2ecf20Sopenharmony_ci return; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci spin_lock_irqsave(common->lock, flags); 328c2ecf20Sopenharmony_ci reg = readl(common->base + common->reg); 338c2ecf20Sopenharmony_ci writel(reg & ~cf->enable, common->base + common->reg); 348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(common->lock, flags); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_civoid ccu_frac_helper_disable(struct ccu_common *common, 388c2ecf20Sopenharmony_ci struct ccu_frac_internal *cf) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci unsigned long flags; 418c2ecf20Sopenharmony_ci u32 reg; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (!(common->features & CCU_FEATURE_FRACTIONAL)) 448c2ecf20Sopenharmony_ci return; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci spin_lock_irqsave(common->lock, flags); 478c2ecf20Sopenharmony_ci reg = readl(common->base + common->reg); 488c2ecf20Sopenharmony_ci writel(reg | cf->enable, common->base + common->reg); 498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(common->lock, flags); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cibool ccu_frac_helper_has_rate(struct ccu_common *common, 538c2ecf20Sopenharmony_ci struct ccu_frac_internal *cf, 548c2ecf20Sopenharmony_ci unsigned long rate) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci if (!(common->features & CCU_FEATURE_FRACTIONAL)) 578c2ecf20Sopenharmony_ci return false; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return (cf->rates[0] == rate) || (cf->rates[1] == rate); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ciunsigned long ccu_frac_helper_read_rate(struct ccu_common *common, 638c2ecf20Sopenharmony_ci struct ccu_frac_internal *cf) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci u32 reg; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci pr_debug("%s: Read fractional\n", clk_hw_get_name(&common->hw)); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (!(common->features & CCU_FEATURE_FRACTIONAL)) 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci pr_debug("%s: clock is fractional (rates %lu and %lu)\n", 738c2ecf20Sopenharmony_ci clk_hw_get_name(&common->hw), cf->rates[0], cf->rates[1]); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci reg = readl(common->base + common->reg); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci pr_debug("%s: clock reg is 0x%x (select is 0x%x)\n", 788c2ecf20Sopenharmony_ci clk_hw_get_name(&common->hw), reg, cf->select); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return (reg & cf->select) ? cf->rates[1] : cf->rates[0]; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ciint ccu_frac_helper_set_rate(struct ccu_common *common, 848c2ecf20Sopenharmony_ci struct ccu_frac_internal *cf, 858c2ecf20Sopenharmony_ci unsigned long rate, u32 lock) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci unsigned long flags; 888c2ecf20Sopenharmony_ci u32 reg, sel; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (!(common->features & CCU_FEATURE_FRACTIONAL)) 918c2ecf20Sopenharmony_ci return -EINVAL; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (cf->rates[0] == rate) 948c2ecf20Sopenharmony_ci sel = 0; 958c2ecf20Sopenharmony_ci else if (cf->rates[1] == rate) 968c2ecf20Sopenharmony_ci sel = cf->select; 978c2ecf20Sopenharmony_ci else 988c2ecf20Sopenharmony_ci return -EINVAL; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci spin_lock_irqsave(common->lock, flags); 1018c2ecf20Sopenharmony_ci reg = readl(common->base + common->reg); 1028c2ecf20Sopenharmony_ci reg &= ~cf->select; 1038c2ecf20Sopenharmony_ci writel(reg | sel, common->base + common->reg); 1048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(common->lock, flags); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ccu_helper_wait_for_lock(common, lock); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 110