1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2016 Maxime Ripard
4 * Maxime Ripard <maxime.ripard@free-electrons.com>
5 */
6
7#include <linux/clk-provider.h>
8#include <linux/io.h>
9#include <linux/spinlock.h>
10
11#include "ccu_frac.h"
12
13bool ccu_frac_helper_is_enabled(struct ccu_common *common,
14				struct ccu_frac_internal *cf)
15{
16	if (!(common->features & CCU_FEATURE_FRACTIONAL))
17		return false;
18
19	return !(readl(common->base + common->reg) & cf->enable);
20}
21
22void ccu_frac_helper_enable(struct ccu_common *common,
23			    struct ccu_frac_internal *cf)
24{
25	unsigned long flags;
26	u32 reg;
27
28	if (!(common->features & CCU_FEATURE_FRACTIONAL))
29		return;
30
31	spin_lock_irqsave(common->lock, flags);
32	reg = readl(common->base + common->reg);
33	writel(reg & ~cf->enable, common->base + common->reg);
34	spin_unlock_irqrestore(common->lock, flags);
35}
36
37void ccu_frac_helper_disable(struct ccu_common *common,
38			     struct ccu_frac_internal *cf)
39{
40	unsigned long flags;
41	u32 reg;
42
43	if (!(common->features & CCU_FEATURE_FRACTIONAL))
44		return;
45
46	spin_lock_irqsave(common->lock, flags);
47	reg = readl(common->base + common->reg);
48	writel(reg | cf->enable, common->base + common->reg);
49	spin_unlock_irqrestore(common->lock, flags);
50}
51
52bool ccu_frac_helper_has_rate(struct ccu_common *common,
53			      struct ccu_frac_internal *cf,
54			      unsigned long rate)
55{
56	if (!(common->features & CCU_FEATURE_FRACTIONAL))
57		return false;
58
59	return (cf->rates[0] == rate) || (cf->rates[1] == rate);
60}
61
62unsigned long ccu_frac_helper_read_rate(struct ccu_common *common,
63					struct ccu_frac_internal *cf)
64{
65	u32 reg;
66
67	pr_debug("%s: Read fractional\n", clk_hw_get_name(&common->hw));
68
69	if (!(common->features & CCU_FEATURE_FRACTIONAL))
70		return 0;
71
72	pr_debug("%s: clock is fractional (rates %lu and %lu)\n",
73		 clk_hw_get_name(&common->hw), cf->rates[0], cf->rates[1]);
74
75	reg = readl(common->base + common->reg);
76
77	pr_debug("%s: clock reg is 0x%x (select is 0x%x)\n",
78		 clk_hw_get_name(&common->hw), reg, cf->select);
79
80	return (reg & cf->select) ? cf->rates[1] : cf->rates[0];
81}
82
83int ccu_frac_helper_set_rate(struct ccu_common *common,
84			     struct ccu_frac_internal *cf,
85			     unsigned long rate, u32 lock)
86{
87	unsigned long flags;
88	u32 reg, sel;
89
90	if (!(common->features & CCU_FEATURE_FRACTIONAL))
91		return -EINVAL;
92
93	if (cf->rates[0] == rate)
94		sel = 0;
95	else if (cf->rates[1] == rate)
96		sel = cf->select;
97	else
98		return -EINVAL;
99
100	spin_lock_irqsave(common->lock, flags);
101	reg = readl(common->base + common->reg);
102	reg &= ~cf->select;
103	writel(reg | sel, common->base + common->reg);
104	spin_unlock_irqrestore(common->lock, flags);
105
106	ccu_helper_wait_for_lock(common, lock);
107
108	return 0;
109}
110