xref: /linux/drivers/clk/sunxi-ng/ccu_frac.c (revision 288440de9e5fdb4a3ff73864850f080c1250fc81)
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 
13 bool 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 EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_is_enabled, SUNXI_CCU);
22 
23 void ccu_frac_helper_enable(struct ccu_common *common,
24 			    struct ccu_frac_internal *cf)
25 {
26 	unsigned long flags;
27 	u32 reg;
28 
29 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
30 		return;
31 
32 	spin_lock_irqsave(common->lock, flags);
33 	reg = readl(common->base + common->reg);
34 	writel(reg & ~cf->enable, common->base + common->reg);
35 	spin_unlock_irqrestore(common->lock, flags);
36 }
37 EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_enable, SUNXI_CCU);
38 
39 void ccu_frac_helper_disable(struct ccu_common *common,
40 			     struct ccu_frac_internal *cf)
41 {
42 	unsigned long flags;
43 	u32 reg;
44 
45 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
46 		return;
47 
48 	spin_lock_irqsave(common->lock, flags);
49 	reg = readl(common->base + common->reg);
50 	writel(reg | cf->enable, common->base + common->reg);
51 	spin_unlock_irqrestore(common->lock, flags);
52 }
53 EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_disable, SUNXI_CCU);
54 
55 bool ccu_frac_helper_has_rate(struct ccu_common *common,
56 			      struct ccu_frac_internal *cf,
57 			      unsigned long rate)
58 {
59 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
60 		return false;
61 
62 	return (cf->rates[0] == rate) || (cf->rates[1] == rate);
63 }
64 EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_has_rate, SUNXI_CCU);
65 
66 unsigned long ccu_frac_helper_read_rate(struct ccu_common *common,
67 					struct ccu_frac_internal *cf)
68 {
69 	u32 reg;
70 
71 	pr_debug("%s: Read fractional\n", clk_hw_get_name(&common->hw));
72 
73 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
74 		return 0;
75 
76 	pr_debug("%s: clock is fractional (rates %lu and %lu)\n",
77 		 clk_hw_get_name(&common->hw), cf->rates[0], cf->rates[1]);
78 
79 	reg = readl(common->base + common->reg);
80 
81 	pr_debug("%s: clock reg is 0x%x (select is 0x%x)\n",
82 		 clk_hw_get_name(&common->hw), reg, cf->select);
83 
84 	return (reg & cf->select) ? cf->rates[1] : cf->rates[0];
85 }
86 EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_read_rate, SUNXI_CCU);
87 
88 int ccu_frac_helper_set_rate(struct ccu_common *common,
89 			     struct ccu_frac_internal *cf,
90 			     unsigned long rate, u32 lock)
91 {
92 	unsigned long flags;
93 	u32 reg, sel;
94 
95 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
96 		return -EINVAL;
97 
98 	if (cf->rates[0] == rate)
99 		sel = 0;
100 	else if (cf->rates[1] == rate)
101 		sel = cf->select;
102 	else
103 		return -EINVAL;
104 
105 	spin_lock_irqsave(common->lock, flags);
106 	reg = readl(common->base + common->reg);
107 	reg &= ~cf->select;
108 	writel(reg | sel, common->base + common->reg);
109 	spin_unlock_irqrestore(common->lock, flags);
110 
111 	ccu_helper_wait_for_lock(common, lock);
112 
113 	return 0;
114 }
115 EXPORT_SYMBOL_NS_GPL(ccu_frac_helper_set_rate, SUNXI_CCU);
116