blob: 3348d97829b205bbab148f3776c0ee074fc19c8b [file] [log] [blame]
Lukasz Majewski4de44bb2019-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{
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 val = readl(divider->reg) >> divider->shift;
78 val &= clk_div_mask(divider->width);
79
80 return divider_recalc_rate(clk, parent_rate, val, divider->table,
81 divider->flags, divider->width);
82}
83
84const struct clk_ops clk_divider_ops = {
85 .get_rate = clk_divider_recalc_rate,
86};
87
88static struct clk *_register_divider(struct device *dev, const char *name,
89 const char *parent_name, unsigned long flags,
90 void __iomem *reg, u8 shift, u8 width,
91 u8 clk_divider_flags, const struct clk_div_table *table)
92{
93 struct clk_divider *div;
94 struct clk *clk;
95 int ret;
96
97 if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
98 if (width + shift > 16) {
99 pr_warn("divider value exceeds LOWORD field\n");
100 return ERR_PTR(-EINVAL);
101 }
102 }
103
104 /* allocate the divider */
105 div = kzalloc(sizeof(*div), GFP_KERNEL);
106 if (!div)
107 return ERR_PTR(-ENOMEM);
108
109 /* struct clk_divider assignments */
110 div->reg = reg;
111 div->shift = shift;
112 div->width = width;
113 div->flags = clk_divider_flags;
114 div->table = table;
115
116 /* register the clock */
117 clk = &div->clk;
118
119 ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, name, parent_name);
120 if (ret) {
121 kfree(div);
122 return ERR_PTR(ret);
123 }
124
125 return clk;
126}
127
128struct clk *clk_register_divider(struct device *dev, const char *name,
129 const char *parent_name, unsigned long flags,
130 void __iomem *reg, u8 shift, u8 width,
131 u8 clk_divider_flags)
132{
133 struct clk *clk;
134
135 clk = _register_divider(dev, name, parent_name, flags, reg, shift,
136 width, clk_divider_flags, NULL);
137 if (IS_ERR(clk))
138 return ERR_CAST(clk);
139 return clk;
140}
141
142U_BOOT_DRIVER(ccf_clk_divider) = {
143 .name = UBOOT_DM_CLK_CCF_DIVIDER,
144 .id = UCLASS_CLK,
145 .ops = &clk_divider_ops,
146 .flags = DM_FLAG_PRE_RELOC,
147};