blob: 936d2fa81662dbc63dbb317df5c0c56f4522efb5 [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2019 MediaTek Inc.
4 */
5
6#include <linux/clk-provider.h>
7#include <linux/err.h>
8#include <linux/io.h>
9#include <linux/slab.h>
10#include "clk-mtk.h"
11#include "clk-fixup-div.h"
12
13#define div_mask(d) ((1 << (d)) - 1)
14
15static unsigned long clk_fixup_div_recalc_rate(struct clk_hw *hw,
16 unsigned long parent_rate)
17{
18 struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
19 unsigned int val;
20
21 val = readl(fixup_div->reg_fixup) >> fixup_div->divider.shift;
22 val &= div_mask(fixup_div->divider.width);
23 pr_debug("%s: val = %x\n", __func__, val);
24
25 return divider_recalc_rate(hw, parent_rate, val,
26 fixup_div->clk_div_table,
27 fixup_div->divider.flags,
28 fixup_div->divider.width);
29}
30
31static long clk_fixup_div_round_rate(struct clk_hw *hw, unsigned long rate,
32 unsigned long *prate)
33{
34 struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
35
36 pr_debug("%s: rate = %lu, prate = %lu\n", __func__, rate, *prate);
37
38 return divider_round_rate(hw, rate, prate, fixup_div->clk_div_table,
39 fixup_div->divider.width,
40 fixup_div->divider.flags);
41}
42
43static int clk_fixup_div_set_rate(struct clk_hw *hw, unsigned long rate,
44 unsigned long parent_rate)
45{
46 struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
47 struct clk_divider *div = to_clk_divider(hw);
48 unsigned long flags = 0;
49 int val, value;
50
51 value = divider_get_val(rate, parent_rate, div->table,
52 div->width, div->flags);
53 if (value < 0)
54 return value;
55
56 spin_lock_irqsave(div->lock, flags);
57
58 val = readl(fixup_div->reg_fixup);
59 val &= ~(div_mask(div->width) << div->shift);
60 val |= (u32)value << div->shift;
61
62 writel(val, div->reg);
63 writel(val, fixup_div->reg_fixup);
64
65 pr_debug("%s: %s: reg_fixup = %x\n",
66 __func__, clk_hw_get_name(hw), readl(fixup_div->reg_fixup));
67
68 spin_unlock_irqrestore(div->lock, flags);
69
70 return 0;
71}
72
73static const struct clk_ops clk_fixup_div_ops = {
74 .recalc_rate = clk_fixup_div_recalc_rate,
75 .round_rate = clk_fixup_div_round_rate,
76 .set_rate = clk_fixup_div_set_rate,
77};
78
79struct clk *mtk_clk_fixup_divider(const char *name, const char *parent,
80 unsigned long flags, void __iomem *reg,
81 void __iomem *reg_fixup, u8 shift, u8 width,
82 u8 clk_divider_flags, spinlock_t *lock)
83{
84 struct clk_fixup_div *fixup_div;
85 struct clk *clk;
86 struct clk_init_data init;
87
88 fixup_div = kzalloc(sizeof(*fixup_div), GFP_KERNEL);
89 if (!fixup_div)
90 return ERR_PTR(-ENOMEM);
91
92 init.name = name;
93 init.ops = &clk_fixup_div_ops;
94 init.flags = flags | CLK_IS_BASIC;
95 init.parent_names = parent ? &parent : NULL;
96 init.num_parents = parent ? 1 : 0;
97
98 fixup_div->reg_fixup = reg_fixup;
99 fixup_div->divider.reg = reg;
100 fixup_div->divider.flags = clk_divider_flags;
101 fixup_div->divider.shift = shift;
102 fixup_div->divider.width = width;
103 fixup_div->divider.lock = lock;
104 fixup_div->divider.hw.init = &init;
105 fixup_div->ops = &clk_divider_ops;
106
107 clk = clk_register(NULL, &fixup_div->divider.hw);
108 if (IS_ERR(clk))
109 kfree(fixup_div);
110
111 return clk;
112}