xref: /openbmc/linux/drivers/clk/sunxi-ng/ccu_frac.c (revision 151f4e2b)
1 /*
2  * Copyright (C) 2016 Maxime Ripard
3  * Maxime Ripard <maxime.ripard@free-electrons.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  */
10 
11 #include <linux/clk-provider.h>
12 #include <linux/io.h>
13 #include <linux/spinlock.h>
14 
15 #include "ccu_frac.h"
16 
17 bool ccu_frac_helper_is_enabled(struct ccu_common *common,
18 				struct ccu_frac_internal *cf)
19 {
20 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
21 		return false;
22 
23 	return !(readl(common->base + common->reg) & cf->enable);
24 }
25 
26 void ccu_frac_helper_enable(struct ccu_common *common,
27 			    struct ccu_frac_internal *cf)
28 {
29 	unsigned long flags;
30 	u32 reg;
31 
32 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
33 		return;
34 
35 	spin_lock_irqsave(common->lock, flags);
36 	reg = readl(common->base + common->reg);
37 	writel(reg & ~cf->enable, common->base + common->reg);
38 	spin_unlock_irqrestore(common->lock, flags);
39 }
40 
41 void ccu_frac_helper_disable(struct ccu_common *common,
42 			     struct ccu_frac_internal *cf)
43 {
44 	unsigned long flags;
45 	u32 reg;
46 
47 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
48 		return;
49 
50 	spin_lock_irqsave(common->lock, flags);
51 	reg = readl(common->base + common->reg);
52 	writel(reg | cf->enable, common->base + common->reg);
53 	spin_unlock_irqrestore(common->lock, flags);
54 }
55 
56 bool ccu_frac_helper_has_rate(struct ccu_common *common,
57 			      struct ccu_frac_internal *cf,
58 			      unsigned long rate)
59 {
60 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
61 		return false;
62 
63 	return (cf->rates[0] == rate) || (cf->rates[1] == rate);
64 }
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 
87 int ccu_frac_helper_set_rate(struct ccu_common *common,
88 			     struct ccu_frac_internal *cf,
89 			     unsigned long rate, u32 lock)
90 {
91 	unsigned long flags;
92 	u32 reg, sel;
93 
94 	if (!(common->features & CCU_FEATURE_FRACTIONAL))
95 		return -EINVAL;
96 
97 	if (cf->rates[0] == rate)
98 		sel = 0;
99 	else if (cf->rates[1] == rate)
100 		sel = cf->select;
101 	else
102 		return -EINVAL;
103 
104 	spin_lock_irqsave(common->lock, flags);
105 	reg = readl(common->base + common->reg);
106 	reg &= ~cf->select;
107 	writel(reg | sel, common->base + common->reg);
108 	spin_unlock_irqrestore(common->lock, flags);
109 
110 	ccu_helper_wait_for_lock(common, lock);
111 
112 	return 0;
113 }
114