Linux-libre 5.0.10-gnu
[librecmc/linux-libre.git] / drivers / clk / imx / clk-pllv4.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 /* PLL Control Status Register (xPLLCSR) */
18 #define PLL_CSR_OFFSET          0x0
19 #define PLL_VLD                 BIT(24)
20 #define PLL_EN                  BIT(0)
21
22 /* PLL Configuration Register (xPLLCFG) */
23 #define PLL_CFG_OFFSET          0x08
24 #define BP_PLL_MULT             16
25 #define BM_PLL_MULT             (0x7f << 16)
26
27 /* PLL Numerator Register (xPLLNUM) */
28 #define PLL_NUM_OFFSET          0x10
29
30 /* PLL Denominator Register (xPLLDENOM) */
31 #define PLL_DENOM_OFFSET        0x14
32
33 struct clk_pllv4 {
34         struct clk_hw   hw;
35         void __iomem    *base;
36 };
37
38 /* Valid PLL MULT Table */
39 static const int pllv4_mult_table[] = {33, 27, 22, 20, 17, 16};
40
41 #define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw)
42
43 #define LOCK_TIMEOUT_US         USEC_PER_MSEC
44
45 static inline int clk_pllv4_wait_lock(struct clk_pllv4 *pll)
46 {
47         u32 csr;
48
49         return readl_poll_timeout(pll->base  + PLL_CSR_OFFSET,
50                                   csr, csr & PLL_VLD, 0, LOCK_TIMEOUT_US);
51 }
52
53 static int clk_pllv4_is_enabled(struct clk_hw *hw)
54 {
55         struct clk_pllv4 *pll = to_clk_pllv4(hw);
56
57         if (readl_relaxed(pll->base) & PLL_EN)
58                 return 1;
59
60         return 0;
61 }
62
63 static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
64                                            unsigned long parent_rate)
65 {
66         struct clk_pllv4 *pll = to_clk_pllv4(hw);
67         u32 div;
68
69         div = readl_relaxed(pll->base + PLL_CFG_OFFSET);
70         div &= BM_PLL_MULT;
71         div >>= BP_PLL_MULT;
72
73         return parent_rate * div;
74 }
75
76 static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
77                                  unsigned long *prate)
78 {
79         unsigned long parent_rate = *prate;
80         unsigned long round_rate, i;
81
82         for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
83                 round_rate = parent_rate * pllv4_mult_table[i];
84                 if (rate >= round_rate)
85                         return round_rate;
86         }
87
88         return round_rate;
89 }
90
91 static bool clk_pllv4_is_valid_mult(unsigned int mult)
92 {
93         int i;
94
95         /* check if mult is in valid MULT table */
96         for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
97                 if (pllv4_mult_table[i] == mult)
98                         return true;
99         }
100
101         return false;
102 }
103
104 static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
105                               unsigned long parent_rate)
106 {
107         struct clk_pllv4 *pll = to_clk_pllv4(hw);
108         u32 val, mult;
109
110         mult = rate / parent_rate;
111
112         if (!clk_pllv4_is_valid_mult(mult))
113                 return -EINVAL;
114
115         val = readl_relaxed(pll->base + PLL_CFG_OFFSET);
116         val &= ~BM_PLL_MULT;
117         val |= mult << BP_PLL_MULT;
118         writel_relaxed(val, pll->base + PLL_CFG_OFFSET);
119
120         return 0;
121 }
122
123 static int clk_pllv4_enable(struct clk_hw *hw)
124 {
125         u32 val;
126         struct clk_pllv4 *pll = to_clk_pllv4(hw);
127
128         val = readl_relaxed(pll->base);
129         val |= PLL_EN;
130         writel_relaxed(val, pll->base);
131
132         return clk_pllv4_wait_lock(pll);
133 }
134
135 static void clk_pllv4_disable(struct clk_hw *hw)
136 {
137         u32 val;
138         struct clk_pllv4 *pll = to_clk_pllv4(hw);
139
140         val = readl_relaxed(pll->base);
141         val &= ~PLL_EN;
142         writel_relaxed(val, pll->base);
143 }
144
145 static const struct clk_ops clk_pllv4_ops = {
146         .recalc_rate    = clk_pllv4_recalc_rate,
147         .round_rate     = clk_pllv4_round_rate,
148         .set_rate       = clk_pllv4_set_rate,
149         .enable         = clk_pllv4_enable,
150         .disable        = clk_pllv4_disable,
151         .is_enabled     = clk_pllv4_is_enabled,
152 };
153
154 struct clk_hw *imx_clk_pllv4(const char *name, const char *parent_name,
155                           void __iomem *base)
156 {
157         struct clk_pllv4 *pll;
158         struct clk_hw *hw;
159         struct clk_init_data init;
160         int ret;
161
162         pll = kzalloc(sizeof(*pll), GFP_KERNEL);
163         if (!pll)
164                 return ERR_PTR(-ENOMEM);
165
166         pll->base = base;
167
168         init.name = name;
169         init.ops = &clk_pllv4_ops;
170         init.parent_names = &parent_name;
171         init.num_parents = 1;
172         init.flags = CLK_SET_RATE_GATE;
173
174         pll->hw.init = &init;
175
176         hw = &pll->hw;
177         ret = clk_hw_register(NULL, hw);
178         if (ret) {
179                 kfree(pll);
180                 hw = ERR_PTR(ret);
181         }
182
183         return hw;
184 }