1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
6 #include <linux/kernel.h>
9 #include <linux/slab.h>
10 #include <linux/clk-provider.h>
14 #define pll_out_override(p) (BIT((p->shift - 6)))
15 #define div_mask(d) ((1 << (d->width)) - 1)
16 #define get_mul(d) (1 << d->frac_width)
17 #define get_max_div(d) div_mask(d)
19 #define PERIPH_CLK_UART_DIV_ENB BIT(24)
21 static int get_div(struct tegra_clk_frac_div
*divider
, unsigned long rate
,
22 unsigned long parent_rate
)
26 div
= div_frac_get(rate
, parent_rate
, divider
->width
,
27 divider
->frac_width
, divider
->flags
);
35 static unsigned long clk_frac_div_recalc_rate(struct clk_hw
*hw
,
36 unsigned long parent_rate
)
38 struct tegra_clk_frac_div
*divider
= to_clk_frac_div(hw
);
41 u64 rate
= parent_rate
;
43 reg
= readl_relaxed(divider
->reg
);
45 if ((divider
->flags
& TEGRA_DIVIDER_UART
) &&
46 !(reg
& PERIPH_CLK_UART_DIV_ENB
))
49 div
= (reg
>> divider
->shift
) & div_mask(divider
);
51 mul
= get_mul(divider
);
61 static long clk_frac_div_round_rate(struct clk_hw
*hw
, unsigned long rate
,
64 struct tegra_clk_frac_div
*divider
= to_clk_frac_div(hw
);
66 unsigned long output_rate
= *prate
;
71 div
= get_div(divider
, rate
, output_rate
);
75 mul
= get_mul(divider
);
77 return DIV_ROUND_UP(output_rate
* mul
, div
+ mul
);
80 static int clk_frac_div_set_rate(struct clk_hw
*hw
, unsigned long rate
,
81 unsigned long parent_rate
)
83 struct tegra_clk_frac_div
*divider
= to_clk_frac_div(hw
);
85 unsigned long flags
= 0;
88 div
= get_div(divider
, rate
, parent_rate
);
93 spin_lock_irqsave(divider
->lock
, flags
);
95 val
= readl_relaxed(divider
->reg
);
96 val
&= ~(div_mask(divider
) << divider
->shift
);
97 val
|= div
<< divider
->shift
;
99 if (divider
->flags
& TEGRA_DIVIDER_UART
) {
101 val
|= PERIPH_CLK_UART_DIV_ENB
;
103 val
&= ~PERIPH_CLK_UART_DIV_ENB
;
106 if (divider
->flags
& TEGRA_DIVIDER_FIXED
)
107 val
|= pll_out_override(divider
);
109 writel_relaxed(val
, divider
->reg
);
112 spin_unlock_irqrestore(divider
->lock
, flags
);
117 static void clk_divider_restore_context(struct clk_hw
*hw
)
119 struct clk_hw
*parent
= clk_hw_get_parent(hw
);
120 unsigned long parent_rate
= clk_hw_get_rate(parent
);
121 unsigned long rate
= clk_hw_get_rate(hw
);
123 if (clk_frac_div_set_rate(hw
, rate
, parent_rate
) < 0)
127 const struct clk_ops tegra_clk_frac_div_ops
= {
128 .recalc_rate
= clk_frac_div_recalc_rate
,
129 .set_rate
= clk_frac_div_set_rate
,
130 .round_rate
= clk_frac_div_round_rate
,
131 .restore_context
= clk_divider_restore_context
,
134 struct clk
*tegra_clk_register_divider(const char *name
,
135 const char *parent_name
, void __iomem
*reg
,
136 unsigned long flags
, u8 clk_divider_flags
, u8 shift
, u8 width
,
137 u8 frac_width
, spinlock_t
*lock
)
139 struct tegra_clk_frac_div
*divider
;
141 struct clk_init_data init
;
143 divider
= kzalloc(sizeof(*divider
), GFP_KERNEL
);
145 pr_err("%s: could not allocate fractional divider clk\n",
147 return ERR_PTR(-ENOMEM
);
151 init
.ops
= &tegra_clk_frac_div_ops
;
153 init
.parent_names
= parent_name
? &parent_name
: NULL
;
154 init
.num_parents
= parent_name
? 1 : 0;
157 divider
->shift
= shift
;
158 divider
->width
= width
;
159 divider
->frac_width
= frac_width
;
160 divider
->lock
= lock
;
161 divider
->flags
= clk_divider_flags
;
163 /* Data in .init is copied by clk_register(), so stack variable OK */
164 divider
->hw
.init
= &init
;
166 clk
= clk_register(NULL
, ÷r
->hw
);
173 static const struct clk_div_table mc_div_table
[] = {
174 { .val
= 0, .div
= 2 },
175 { .val
= 1, .div
= 1 },
176 { .val
= 0, .div
= 0 },
179 struct clk
*tegra_clk_register_mc(const char *name
, const char *parent_name
,
180 void __iomem
*reg
, spinlock_t
*lock
)
182 return clk_register_divider_table(NULL
, name
, parent_name
,
184 reg
, 16, 1, CLK_DIVIDER_READ_ONLY
,