GNU Linux-libre 4.9.309-gnu1
[releases.git] / drivers / clk / sunxi-ng / ccu_frac.c
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
16 bool ccu_frac_helper_is_enabled(struct ccu_common *common,
17                                 struct _ccu_frac *cf)
18 {
19         if (!(common->features & CCU_FEATURE_FRACTIONAL))
20                 return false;
21
22         return !(readl(common->base + common->reg) & cf->enable);
23 }
24
25 void ccu_frac_helper_enable(struct ccu_common *common,
26                             struct _ccu_frac *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
40 void ccu_frac_helper_disable(struct ccu_common *common,
41                              struct _ccu_frac *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
55 bool ccu_frac_helper_has_rate(struct ccu_common *common,
56                               struct _ccu_frac *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
65 unsigned long ccu_frac_helper_read_rate(struct ccu_common *common,
66                                         struct _ccu_frac *cf)
67 {
68         u32 reg;
69
70         printk("%s: Read fractional\n", clk_hw_get_name(&common->hw));
71
72         if (!(common->features & CCU_FEATURE_FRACTIONAL))
73                 return 0;
74
75         printk("%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         printk("%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
86 int ccu_frac_helper_set_rate(struct ccu_common *common,
87                              struct _ccu_frac *cf,
88                              unsigned long rate)
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         return 0;
110 }