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