clk: core: replace clk_{readl,writel} with {readl,writel}
[linux-2.6-microblaze.git] / drivers / clk / imx / clk-pfdv2.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2016 Freescale Semiconductor, Inc.
4  * Copyright 2017~2018 NXP
5  *
6  * Author: Dong Aisheng <aisheng.dong@nxp.com>
7  *
8  */
9
10 #include <linux/clk-provider.h>
11 #include <linux/err.h>
12 #include <linux/iopoll.h>
13 #include <linux/slab.h>
14
15 #include "clk.h"
16
17 /**
18  * struct clk_pfdv2 - IMX PFD clock
19  * @clk_hw:     clock source
20  * @reg:        PFD register address
21  * @gate_bit:   Gate bit offset
22  * @vld_bit:    Valid bit offset
23  * @frac_off:   PLL Fractional Divider offset
24  */
25
26 struct clk_pfdv2 {
27         struct clk_hw   hw;
28         void __iomem    *reg;
29         u8              gate_bit;
30         u8              vld_bit;
31         u8              frac_off;
32 };
33
34 #define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw)
35
36 #define CLK_PFDV2_FRAC_MASK 0x3f
37
38 #define LOCK_TIMEOUT_US         USEC_PER_MSEC
39
40 static DEFINE_SPINLOCK(pfd_lock);
41
42 static int clk_pfdv2_wait(struct clk_pfdv2 *pfd)
43 {
44         u32 val;
45
46         return readl_poll_timeout(pfd->reg, val, val & pfd->vld_bit,
47                                   0, LOCK_TIMEOUT_US);
48 }
49
50 static int clk_pfdv2_enable(struct clk_hw *hw)
51 {
52         struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
53         unsigned long flags;
54         u32 val;
55
56         spin_lock_irqsave(&pfd_lock, flags);
57         val = readl_relaxed(pfd->reg);
58         val &= ~pfd->gate_bit;
59         writel_relaxed(val, pfd->reg);
60         spin_unlock_irqrestore(&pfd_lock, flags);
61
62         return clk_pfdv2_wait(pfd);
63 }
64
65 static void clk_pfdv2_disable(struct clk_hw *hw)
66 {
67         struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
68         unsigned long flags;
69         u32 val;
70
71         spin_lock_irqsave(&pfd_lock, flags);
72         val = readl_relaxed(pfd->reg);
73         val |= pfd->gate_bit;
74         writel_relaxed(val, pfd->reg);
75         spin_unlock_irqrestore(&pfd_lock, flags);
76 }
77
78 static unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw,
79                                            unsigned long parent_rate)
80 {
81         struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
82         u64 tmp = parent_rate;
83         u8 frac;
84
85         frac = (readl_relaxed(pfd->reg) >> pfd->frac_off)
86                 & CLK_PFDV2_FRAC_MASK;
87
88         if (!frac) {
89                 pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n",
90                          clk_hw_get_name(hw));
91                 return 0;
92         }
93
94         tmp *= 18;
95         do_div(tmp, frac);
96
97         return tmp;
98 }
99
100 static long clk_pfdv2_round_rate(struct clk_hw *hw, unsigned long rate,
101                                  unsigned long *prate)
102 {
103         u64 tmp = *prate;
104         u8 frac;
105
106         tmp = tmp * 18 + rate / 2;
107         do_div(tmp, rate);
108         frac = tmp;
109
110         if (frac < 12)
111                 frac = 12;
112         else if (frac > 35)
113                 frac = 35;
114
115         tmp = *prate;
116         tmp *= 18;
117         do_div(tmp, frac);
118
119         return tmp;
120 }
121
122 static int clk_pfdv2_is_enabled(struct clk_hw *hw)
123 {
124         struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
125
126         if (readl_relaxed(pfd->reg) & pfd->gate_bit)
127                 return 0;
128
129         return 1;
130 }
131
132 static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate,
133                               unsigned long parent_rate)
134 {
135         struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
136         unsigned long flags;
137         u64 tmp = parent_rate;
138         u32 val;
139         u8 frac;
140
141         tmp = tmp * 18 + rate / 2;
142         do_div(tmp, rate);
143         frac = tmp;
144         if (frac < 12)
145                 frac = 12;
146         else if (frac > 35)
147                 frac = 35;
148
149         spin_lock_irqsave(&pfd_lock, flags);
150         val = readl_relaxed(pfd->reg);
151         val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off);
152         val |= frac << pfd->frac_off;
153         writel_relaxed(val, pfd->reg);
154         spin_unlock_irqrestore(&pfd_lock, flags);
155
156         return 0;
157 }
158
159 static const struct clk_ops clk_pfdv2_ops = {
160         .enable         = clk_pfdv2_enable,
161         .disable        = clk_pfdv2_disable,
162         .recalc_rate    = clk_pfdv2_recalc_rate,
163         .round_rate     = clk_pfdv2_round_rate,
164         .set_rate       = clk_pfdv2_set_rate,
165         .is_enabled     = clk_pfdv2_is_enabled,
166 };
167
168 struct clk_hw *imx_clk_pfdv2(const char *name, const char *parent_name,
169                              void __iomem *reg, u8 idx)
170 {
171         struct clk_init_data init;
172         struct clk_pfdv2 *pfd;
173         struct clk_hw *hw;
174         int ret;
175
176         WARN_ON(idx > 3);
177
178         pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
179         if (!pfd)
180                 return ERR_PTR(-ENOMEM);
181
182         pfd->reg = reg;
183         pfd->gate_bit = 1 << ((idx + 1) * 8 - 1);
184         pfd->vld_bit = pfd->gate_bit - 1;
185         pfd->frac_off = idx * 8;
186
187         init.name = name;
188         init.ops = &clk_pfdv2_ops;
189         init.parent_names = &parent_name;
190         init.num_parents = 1;
191         init.flags = CLK_SET_RATE_GATE;
192
193         pfd->hw.init = &init;
194
195         hw = &pfd->hw;
196         ret = clk_hw_register(NULL, hw);
197         if (ret) {
198                 kfree(pfd);
199                 hw = ERR_PTR(ret);
200         }
201
202         return hw;
203 }