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