1 // SPDX-License-Identifier: GPL-2.0-only
3 * Hisilicon hi6220 SoC divider clock driver
5 * Copyright (c) 2015 Hisilicon Limited.
7 * Author: Bintian Wang <bintian.wang@huawei.com>
10 #include <linux/kernel.h>
11 #include <linux/clk-provider.h>
12 #include <linux/slab.h>
14 #include <linux/err.h>
15 #include <linux/spinlock.h>
19 #define div_mask(width) ((1 << (width)) - 1)
22 * struct hi6220_clk_divider - divider clock for hi6220
24 * @hw: handle between common and hardware-specific interfaces
25 * @reg: register containing divider
26 * @shift: shift to the divider bit field
27 * @width: width of the divider bit field
28 * @mask: mask for setting divider rate
29 * @table: the div table that the divider supports
30 * @lock: register lock
32 struct hi6220_clk_divider
{
38 const struct clk_div_table
*table
;
42 #define to_hi6220_clk_divider(_hw) \
43 container_of(_hw, struct hi6220_clk_divider, hw)
45 static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw
*hw
,
46 unsigned long parent_rate
)
49 struct hi6220_clk_divider
*dclk
= to_hi6220_clk_divider(hw
);
51 val
= readl_relaxed(dclk
->reg
) >> dclk
->shift
;
52 val
&= div_mask(dclk
->width
);
54 return divider_recalc_rate(hw
, parent_rate
, val
, dclk
->table
,
55 CLK_DIVIDER_ROUND_CLOSEST
, dclk
->width
);
58 static long hi6220_clkdiv_round_rate(struct clk_hw
*hw
, unsigned long rate
,
61 struct hi6220_clk_divider
*dclk
= to_hi6220_clk_divider(hw
);
63 return divider_round_rate(hw
, rate
, prate
, dclk
->table
,
64 dclk
->width
, CLK_DIVIDER_ROUND_CLOSEST
);
67 static int hi6220_clkdiv_set_rate(struct clk_hw
*hw
, unsigned long rate
,
68 unsigned long parent_rate
)
71 unsigned long flags
= 0;
73 struct hi6220_clk_divider
*dclk
= to_hi6220_clk_divider(hw
);
75 value
= divider_get_val(rate
, parent_rate
, dclk
->table
,
76 dclk
->width
, CLK_DIVIDER_ROUND_CLOSEST
);
79 spin_lock_irqsave(dclk
->lock
, flags
);
81 data
= readl_relaxed(dclk
->reg
);
82 data
&= ~(div_mask(dclk
->width
) << dclk
->shift
);
83 data
|= value
<< dclk
->shift
;
86 writel_relaxed(data
, dclk
->reg
);
89 spin_unlock_irqrestore(dclk
->lock
, flags
);
94 static const struct clk_ops hi6220_clkdiv_ops
= {
95 .recalc_rate
= hi6220_clkdiv_recalc_rate
,
96 .round_rate
= hi6220_clkdiv_round_rate
,
97 .set_rate
= hi6220_clkdiv_set_rate
,
100 struct clk
*hi6220_register_clkdiv(struct device
*dev
, const char *name
,
101 const char *parent_name
, unsigned long flags
, void __iomem
*reg
,
102 u8 shift
, u8 width
, u32 mask_bit
, spinlock_t
*lock
)
104 struct hi6220_clk_divider
*div
;
106 struct clk_init_data init
;
107 struct clk_div_table
*table
;
108 u32 max_div
, min_div
;
111 /* allocate the divider */
112 div
= kzalloc(sizeof(*div
), GFP_KERNEL
);
114 return ERR_PTR(-ENOMEM
);
116 /* Init the divider table */
117 max_div
= div_mask(width
) + 1;
120 table
= kcalloc(max_div
+ 1, sizeof(*table
), GFP_KERNEL
);
123 return ERR_PTR(-ENOMEM
);
126 for (i
= 0; i
< max_div
; i
++) {
127 table
[i
].div
= min_div
+ i
;
128 table
[i
].val
= table
[i
].div
- 1;
132 init
.ops
= &hi6220_clkdiv_ops
;
134 init
.parent_names
= parent_name
? &parent_name
: NULL
;
135 init
.num_parents
= parent_name
? 1 : 0;
137 /* struct hi6220_clk_divider assignments */
141 div
->mask
= mask_bit
? BIT(mask_bit
) : 0;
143 div
->hw
.init
= &init
;
146 /* register the clock */
147 clk
= clk_register(dev
, &div
->hw
);