blob: 2ed9ed6ab8c4ab1e17be696b894b653256b40f3c [file] [log] [blame]
Lukasz Majewski1d7993d2019-06-24 15:50:45 +02001// 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
27static 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
38static 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
52unsigned 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
70static ulong clk_divider_recalc_rate(struct clk *clk)
71{
Peng Fan5b27ff82019-07-31 07:01:26 +000072 struct clk_divider *divider = to_clk_divider(clk_dev_binded(clk) ?
73 dev_get_clk_ptr(clk->dev) : clk);
Lukasz Majewski1d7993d2019-06-24 15:50:45 +020074 unsigned long parent_rate = clk_get_parent_rate(clk);
75 unsigned int val;
76
Lukasz Majewski6bb15d62019-06-24 15:50:48 +020077#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;
Lukasz Majewski1d7993d2019-06-24 15:50:45 +020083 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
89const struct clk_ops clk_divider_ops = {
90 .get_rate = clk_divider_recalc_rate,
91};
92
93static 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;
Lukasz Majewski6bb15d62019-06-24 15:50:48 +0200120#if CONFIG_IS_ENABLED(SANDBOX_CLK_CCF)
121 div->io_divider_val = *(u32 *)reg;
122#endif
Lukasz Majewski1d7993d2019-06-24 15:50:45 +0200123
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
136struct 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
150U_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};