1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2013 Freescale Semiconductor, Inc.
6 #include <linux/clk-provider.h>
9 #include <linux/slab.h>
12 #define div_mask(d) ((1 << (d->width)) - 1)
15 * struct clk_fixup_div - imx integer fixup divider clock
16 * @divider: the parent class
17 * @ops: pointer to clk_ops of parent class
18 * @fixup: a hook to fixup the write value
20 * The imx fixup divider clock is a subclass of basic clk_divider
21 * with an addtional fixup hook.
23 struct clk_fixup_div
{
24 struct clk_divider divider
;
25 const struct clk_ops
*ops
;
26 void (*fixup
)(u32
*val
);
29 static inline struct clk_fixup_div
*to_clk_fixup_div(struct clk_hw
*hw
)
31 struct clk_divider
*divider
= to_clk_divider(hw
);
33 return container_of(divider
, struct clk_fixup_div
, divider
);
36 static unsigned long clk_fixup_div_recalc_rate(struct clk_hw
*hw
,
37 unsigned long parent_rate
)
39 struct clk_fixup_div
*fixup_div
= to_clk_fixup_div(hw
);
41 return fixup_div
->ops
->recalc_rate(&fixup_div
->divider
.hw
, parent_rate
);
44 static long clk_fixup_div_round_rate(struct clk_hw
*hw
, unsigned long rate
,
47 struct clk_fixup_div
*fixup_div
= to_clk_fixup_div(hw
);
49 return fixup_div
->ops
->round_rate(&fixup_div
->divider
.hw
, rate
, prate
);
52 static int clk_fixup_div_set_rate(struct clk_hw
*hw
, unsigned long rate
,
53 unsigned long parent_rate
)
55 struct clk_fixup_div
*fixup_div
= to_clk_fixup_div(hw
);
56 struct clk_divider
*div
= to_clk_divider(hw
);
57 unsigned int divider
, value
;
61 divider
= parent_rate
/ rate
;
63 /* Zero based divider */
66 if (value
> div_mask(div
))
67 value
= div_mask(div
);
69 spin_lock_irqsave(div
->lock
, flags
);
71 val
= readl(div
->reg
);
72 val
&= ~(div_mask(div
) << div
->shift
);
73 val
|= value
<< div
->shift
;
74 fixup_div
->fixup(&val
);
75 writel(val
, div
->reg
);
77 spin_unlock_irqrestore(div
->lock
, flags
);
82 static const struct clk_ops clk_fixup_div_ops
= {
83 .recalc_rate
= clk_fixup_div_recalc_rate
,
84 .round_rate
= clk_fixup_div_round_rate
,
85 .set_rate
= clk_fixup_div_set_rate
,
88 struct clk_hw
*imx_clk_hw_fixup_divider(const char *name
, const char *parent
,
89 void __iomem
*reg
, u8 shift
, u8 width
,
90 void (*fixup
)(u32
*val
))
92 struct clk_fixup_div
*fixup_div
;
94 struct clk_init_data init
;
98 return ERR_PTR(-EINVAL
);
100 fixup_div
= kzalloc(sizeof(*fixup_div
), GFP_KERNEL
);
102 return ERR_PTR(-ENOMEM
);
105 init
.ops
= &clk_fixup_div_ops
;
106 init
.flags
= CLK_SET_RATE_PARENT
;
107 init
.parent_names
= parent
? &parent
: NULL
;
108 init
.num_parents
= parent
? 1 : 0;
110 fixup_div
->divider
.reg
= reg
;
111 fixup_div
->divider
.shift
= shift
;
112 fixup_div
->divider
.width
= width
;
113 fixup_div
->divider
.lock
= &imx_ccm_lock
;
114 fixup_div
->divider
.hw
.init
= &init
;
115 fixup_div
->ops
= &clk_divider_ops
;
116 fixup_div
->fixup
= fixup
;
118 hw
= &fixup_div
->divider
.hw
;
120 ret
= clk_hw_register(NULL
, hw
);