xref: /openbmc/linux/drivers/clk/hisilicon/clkdivider-hi6220.c (revision 72ea48610d43c59507d9ad39083d40085400ba12)
1*72ea4861SBintian Wang /*
2*72ea4861SBintian Wang  * Hisilicon hi6220 SoC divider clock driver
3*72ea4861SBintian Wang  *
4*72ea4861SBintian Wang  * Copyright (c) 2015 Hisilicon Limited.
5*72ea4861SBintian Wang  *
6*72ea4861SBintian Wang  * Author: Bintian Wang <bintian.wang@huawei.com>
7*72ea4861SBintian Wang  *
8*72ea4861SBintian Wang  * This program is free software; you can redistribute it and/or modify
9*72ea4861SBintian Wang  * it under the terms of the GNU General Public License version 2 as
10*72ea4861SBintian Wang  * published by the Free Software Foundation.
11*72ea4861SBintian Wang  *
12*72ea4861SBintian Wang  */
13*72ea4861SBintian Wang 
14*72ea4861SBintian Wang #include <linux/kernel.h>
15*72ea4861SBintian Wang #include <linux/clk-provider.h>
16*72ea4861SBintian Wang #include <linux/slab.h>
17*72ea4861SBintian Wang #include <linux/io.h>
18*72ea4861SBintian Wang #include <linux/err.h>
19*72ea4861SBintian Wang #include <linux/spinlock.h>
20*72ea4861SBintian Wang 
21*72ea4861SBintian Wang #define div_mask(width)	((1 << (width)) - 1)
22*72ea4861SBintian Wang 
23*72ea4861SBintian Wang /**
24*72ea4861SBintian Wang  * struct hi6220_clk_divider - divider clock for hi6220
25*72ea4861SBintian Wang  *
26*72ea4861SBintian Wang  * @hw:		handle between common and hardware-specific interfaces
27*72ea4861SBintian Wang  * @reg:	register containing divider
28*72ea4861SBintian Wang  * @shift:	shift to the divider bit field
29*72ea4861SBintian Wang  * @width:	width of the divider bit field
30*72ea4861SBintian Wang  * @mask:	mask for setting divider rate
31*72ea4861SBintian Wang  * @table:	the div table that the divider supports
32*72ea4861SBintian Wang  * @lock:	register lock
33*72ea4861SBintian Wang  */
34*72ea4861SBintian Wang struct hi6220_clk_divider {
35*72ea4861SBintian Wang 	struct clk_hw	hw;
36*72ea4861SBintian Wang 	void __iomem	*reg;
37*72ea4861SBintian Wang 	u8		shift;
38*72ea4861SBintian Wang 	u8		width;
39*72ea4861SBintian Wang 	u32		mask;
40*72ea4861SBintian Wang 	const struct clk_div_table *table;
41*72ea4861SBintian Wang 	spinlock_t	*lock;
42*72ea4861SBintian Wang };
43*72ea4861SBintian Wang 
44*72ea4861SBintian Wang #define to_hi6220_clk_divider(_hw)	\
45*72ea4861SBintian Wang 	container_of(_hw, struct hi6220_clk_divider, hw)
46*72ea4861SBintian Wang 
47*72ea4861SBintian Wang static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw,
48*72ea4861SBintian Wang 					unsigned long parent_rate)
49*72ea4861SBintian Wang {
50*72ea4861SBintian Wang 	unsigned int val;
51*72ea4861SBintian Wang 	struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
52*72ea4861SBintian Wang 
53*72ea4861SBintian Wang 	val = readl_relaxed(dclk->reg) >> dclk->shift;
54*72ea4861SBintian Wang 	val &= div_mask(dclk->width);
55*72ea4861SBintian Wang 
56*72ea4861SBintian Wang 	return divider_recalc_rate(hw, parent_rate, val, dclk->table,
57*72ea4861SBintian Wang 				   CLK_DIVIDER_ROUND_CLOSEST);
58*72ea4861SBintian Wang }
59*72ea4861SBintian Wang 
60*72ea4861SBintian Wang static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate,
61*72ea4861SBintian Wang 					unsigned long *prate)
62*72ea4861SBintian Wang {
63*72ea4861SBintian Wang 	struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
64*72ea4861SBintian Wang 
65*72ea4861SBintian Wang 	return divider_round_rate(hw, rate, prate, dclk->table,
66*72ea4861SBintian Wang 				  dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
67*72ea4861SBintian Wang }
68*72ea4861SBintian Wang 
69*72ea4861SBintian Wang static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
70*72ea4861SBintian Wang 					unsigned long parent_rate)
71*72ea4861SBintian Wang {
72*72ea4861SBintian Wang 	int value;
73*72ea4861SBintian Wang 	unsigned long flags = 0;
74*72ea4861SBintian Wang 	u32 data;
75*72ea4861SBintian Wang 	struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
76*72ea4861SBintian Wang 
77*72ea4861SBintian Wang 	value = divider_get_val(rate, parent_rate, dclk->table,
78*72ea4861SBintian Wang 				dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
79*72ea4861SBintian Wang 
80*72ea4861SBintian Wang 	if (dclk->lock)
81*72ea4861SBintian Wang 		spin_lock_irqsave(dclk->lock, flags);
82*72ea4861SBintian Wang 
83*72ea4861SBintian Wang 	data = readl_relaxed(dclk->reg);
84*72ea4861SBintian Wang 	data &= ~(div_mask(dclk->width) << dclk->shift);
85*72ea4861SBintian Wang 	data |= value << dclk->shift;
86*72ea4861SBintian Wang 	data |= dclk->mask;
87*72ea4861SBintian Wang 
88*72ea4861SBintian Wang 	writel_relaxed(data, dclk->reg);
89*72ea4861SBintian Wang 
90*72ea4861SBintian Wang 	if (dclk->lock)
91*72ea4861SBintian Wang 		spin_unlock_irqrestore(dclk->lock, flags);
92*72ea4861SBintian Wang 
93*72ea4861SBintian Wang 	return 0;
94*72ea4861SBintian Wang }
95*72ea4861SBintian Wang 
96*72ea4861SBintian Wang static const struct clk_ops hi6220_clkdiv_ops = {
97*72ea4861SBintian Wang 	.recalc_rate = hi6220_clkdiv_recalc_rate,
98*72ea4861SBintian Wang 	.round_rate = hi6220_clkdiv_round_rate,
99*72ea4861SBintian Wang 	.set_rate = hi6220_clkdiv_set_rate,
100*72ea4861SBintian Wang };
101*72ea4861SBintian Wang 
102*72ea4861SBintian Wang struct clk *hi6220_register_clkdiv(struct device *dev, const char *name,
103*72ea4861SBintian Wang 	const char *parent_name, unsigned long flags, void __iomem *reg,
104*72ea4861SBintian Wang 	u8 shift, u8 width, u32 mask_bit, spinlock_t *lock)
105*72ea4861SBintian Wang {
106*72ea4861SBintian Wang 	struct hi6220_clk_divider *div;
107*72ea4861SBintian Wang 	struct clk *clk;
108*72ea4861SBintian Wang 	struct clk_init_data init;
109*72ea4861SBintian Wang 	struct clk_div_table *table;
110*72ea4861SBintian Wang 	u32 max_div, min_div;
111*72ea4861SBintian Wang 	int i;
112*72ea4861SBintian Wang 
113*72ea4861SBintian Wang 	/* allocate the divider */
114*72ea4861SBintian Wang 	div = kzalloc(sizeof(*div), GFP_KERNEL);
115*72ea4861SBintian Wang 	if (!div)
116*72ea4861SBintian Wang 		return ERR_PTR(-ENOMEM);
117*72ea4861SBintian Wang 
118*72ea4861SBintian Wang 	/* Init the divider table */
119*72ea4861SBintian Wang 	max_div = div_mask(width) + 1;
120*72ea4861SBintian Wang 	min_div = 1;
121*72ea4861SBintian Wang 
122*72ea4861SBintian Wang 	table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL);
123*72ea4861SBintian Wang 	if (!table) {
124*72ea4861SBintian Wang 		kfree(div);
125*72ea4861SBintian Wang 		return ERR_PTR(-ENOMEM);
126*72ea4861SBintian Wang 	}
127*72ea4861SBintian Wang 
128*72ea4861SBintian Wang 	for (i = 0; i < max_div; i++) {
129*72ea4861SBintian Wang 		table[i].div = min_div + i;
130*72ea4861SBintian Wang 		table[i].val = table[i].div - 1;
131*72ea4861SBintian Wang 	}
132*72ea4861SBintian Wang 
133*72ea4861SBintian Wang 	init.name = name;
134*72ea4861SBintian Wang 	init.ops = &hi6220_clkdiv_ops;
135*72ea4861SBintian Wang 	init.flags = flags;
136*72ea4861SBintian Wang 	init.parent_names = parent_name ? &parent_name : NULL;
137*72ea4861SBintian Wang 	init.num_parents = parent_name ? 1 : 0;
138*72ea4861SBintian Wang 
139*72ea4861SBintian Wang 	/* struct hi6220_clk_divider assignments */
140*72ea4861SBintian Wang 	div->reg = reg;
141*72ea4861SBintian Wang 	div->shift = shift;
142*72ea4861SBintian Wang 	div->width = width;
143*72ea4861SBintian Wang 	div->mask = mask_bit ? BIT(mask_bit) : 0;
144*72ea4861SBintian Wang 	div->lock = lock;
145*72ea4861SBintian Wang 	div->hw.init = &init;
146*72ea4861SBintian Wang 	div->table = table;
147*72ea4861SBintian Wang 
148*72ea4861SBintian Wang 	/* register the clock */
149*72ea4861SBintian Wang 	clk = clk_register(dev, &div->hw);
150*72ea4861SBintian Wang 	if (IS_ERR(clk)) {
151*72ea4861SBintian Wang 		kfree(table);
152*72ea4861SBintian Wang 		kfree(div);
153*72ea4861SBintian Wang 	}
154*72ea4861SBintian Wang 
155*72ea4861SBintian Wang 	return clk;
156*72ea4861SBintian Wang }
157