diff options
Diffstat (limited to 'drivers/clk/sunxi-ng/ccu_frac.c')
-rw-r--r-- | drivers/clk/sunxi-ng/ccu_frac.c | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/drivers/clk/sunxi-ng/ccu_frac.c b/drivers/clk/sunxi-ng/ccu_frac.c new file mode 100644 index 000000000000..5c4b10cd15b5 --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu_frac.c | |||
@@ -0,0 +1,110 @@ | |||
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 | } | ||