doc: rockchip: Adapt Pine64 Rock64 board instructions
[platform/kernel/u-boot.git] / drivers / clk / clk-divider.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2019 DENX Software Engineering
4  * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
5  *
6  * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
7  * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
8  * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
9  *
10  */
11
12 #include <common.h>
13 #include <asm/io.h>
14 #include <malloc.h>
15 #include <clk-uclass.h>
16 #include <dm/device.h>
17 #include <dm/uclass.h>
18 #include <dm/lists.h>
19 #include <dm/device-internal.h>
20 #include <linux/clk-provider.h>
21 #include <div64.h>
22 #include <clk.h>
23 #include "clk.h"
24
25 #define UBOOT_DM_CLK_CCF_DIVIDER "ccf_clk_divider"
26
27 static unsigned int _get_table_div(const struct clk_div_table *table,
28                                    unsigned int val)
29 {
30         const struct clk_div_table *clkt;
31
32         for (clkt = table; clkt->div; clkt++)
33                 if (clkt->val == val)
34                         return clkt->div;
35         return 0;
36 }
37
38 static unsigned int _get_div(const struct clk_div_table *table,
39                              unsigned int val, unsigned long flags, u8 width)
40 {
41         if (flags & CLK_DIVIDER_ONE_BASED)
42                 return val;
43         if (flags & CLK_DIVIDER_POWER_OF_TWO)
44                 return 1 << val;
45         if (flags & CLK_DIVIDER_MAX_AT_ZERO)
46                 return val ? val : clk_div_mask(width) + 1;
47         if (table)
48                 return _get_table_div(table, val);
49         return val + 1;
50 }
51
52 unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
53                                   unsigned int val,
54                                   const struct clk_div_table *table,
55                                   unsigned long flags, unsigned long width)
56 {
57         unsigned int div;
58
59         div = _get_div(table, val, flags, width);
60         if (!div) {
61                 WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
62                      "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
63                      clk_hw_get_name(hw));
64                 return parent_rate;
65         }
66
67         return DIV_ROUND_UP_ULL((u64)parent_rate, div);
68 }
69
70 static ulong clk_divider_recalc_rate(struct clk *clk)
71 {
72         struct clk_divider *divider =
73                 to_clk_divider(dev_get_clk_ptr(clk->dev));
74         unsigned long parent_rate = clk_get_parent_rate(clk);
75         unsigned int val;
76
77 #if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
78         val = divider->io_divider_val;
79 #else
80         val = readl(divider->reg);
81 #endif
82         val >>= divider->shift;
83         val &= clk_div_mask(divider->width);
84
85         return divider_recalc_rate(clk, parent_rate, val, divider->table,
86                                    divider->flags, divider->width);
87 }
88
89 const struct clk_ops clk_divider_ops = {
90         .get_rate = clk_divider_recalc_rate,
91 };
92
93 static struct clk *_register_divider(struct device *dev, const char *name,
94                 const char *parent_name, unsigned long flags,
95                 void __iomem *reg, u8 shift, u8 width,
96                 u8 clk_divider_flags, const struct clk_div_table *table)
97 {
98         struct clk_divider *div;
99         struct clk *clk;
100         int ret;
101
102         if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
103                 if (width + shift > 16) {
104                         pr_warn("divider value exceeds LOWORD field\n");
105                         return ERR_PTR(-EINVAL);
106                 }
107         }
108
109         /* allocate the divider */
110         div = kzalloc(sizeof(*div), GFP_KERNEL);
111         if (!div)
112                 return ERR_PTR(-ENOMEM);
113
114         /* struct clk_divider assignments */
115         div->reg = reg;
116         div->shift = shift;
117         div->width = width;
118         div->flags = clk_divider_flags;
119         div->table = table;
120 #if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
121         div->io_divider_val = *(u32 *)reg;
122 #endif
123
124         /* register the clock */
125         clk = &div->clk;
126
127         ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name);
128         if (ret) {
129                 kfree(div);
130                 return ERR_PTR(ret);
131         }
132
133         return clk;
134 }
135
136 struct clk *clk_register_divider(struct device *dev, const char *name,
137                 const char *parent_name, unsigned long flags,
138                 void __iomem *reg, u8 shift, u8 width,
139                 u8 clk_divider_flags)
140 {
141         struct clk *clk;
142
143         clk =  _register_divider(dev, name, parent_name, flags, reg, shift,
144                                  width, clk_divider_flags, NULL);
145         if (IS_ERR(clk))
146                 return ERR_CAST(clk);
147         return clk;
148 }
149
150 U_BOOT_DRIVER(ccf_clk_divider) = {
151         .name   = UBOOT_DM_CLK_CCF_DIVIDER,
152         .id     = UCLASS_CLK,
153         .ops    = &clk_divider_ops,
154         .flags = DM_FLAG_PRE_RELOC,
155 };