1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2023 Nuvoton Technology Corp.
4 * Author: Chi-Fang Li <cfli0@nuvoton.com>
7 #include <linux/clk-provider.h>
8 #include <linux/device.h>
9 #include <linux/regmap.h>
10 #include <linux/spinlock.h>
12 #include "clk-ma35d1.h"
14 struct ma35d1_adc_clk_div
{
20 const struct clk_div_table
*table
;
21 /* protects concurrent access to clock divider registers */
25 static inline struct ma35d1_adc_clk_div
*to_ma35d1_adc_clk_div(struct clk_hw
*_hw
)
27 return container_of(_hw
, struct ma35d1_adc_clk_div
, hw
);
30 static unsigned long ma35d1_clkdiv_recalc_rate(struct clk_hw
*hw
, unsigned long parent_rate
)
33 struct ma35d1_adc_clk_div
*dclk
= to_ma35d1_adc_clk_div(hw
);
35 val
= readl_relaxed(dclk
->reg
) >> dclk
->shift
;
36 val
&= clk_div_mask(dclk
->width
);
38 return divider_recalc_rate(hw
, parent_rate
, val
, dclk
->table
,
39 CLK_DIVIDER_ROUND_CLOSEST
, dclk
->width
);
42 static long ma35d1_clkdiv_round_rate(struct clk_hw
*hw
, unsigned long rate
, unsigned long *prate
)
44 struct ma35d1_adc_clk_div
*dclk
= to_ma35d1_adc_clk_div(hw
);
46 return divider_round_rate(hw
, rate
, prate
, dclk
->table
,
47 dclk
->width
, CLK_DIVIDER_ROUND_CLOSEST
);
50 static int ma35d1_clkdiv_set_rate(struct clk_hw
*hw
, unsigned long rate
, unsigned long parent_rate
)
53 unsigned long flags
= 0;
55 struct ma35d1_adc_clk_div
*dclk
= to_ma35d1_adc_clk_div(hw
);
57 value
= divider_get_val(rate
, parent_rate
, dclk
->table
,
58 dclk
->width
, CLK_DIVIDER_ROUND_CLOSEST
);
60 spin_lock_irqsave(dclk
->lock
, flags
);
62 data
= readl_relaxed(dclk
->reg
);
63 data
&= ~(clk_div_mask(dclk
->width
) << dclk
->shift
);
64 data
|= (value
- 1) << dclk
->shift
;
66 writel_relaxed(data
, dclk
->reg
);
68 spin_unlock_irqrestore(dclk
->lock
, flags
);
72 static const struct clk_ops ma35d1_adc_clkdiv_ops
= {
73 .recalc_rate
= ma35d1_clkdiv_recalc_rate
,
74 .round_rate
= ma35d1_clkdiv_round_rate
,
75 .set_rate
= ma35d1_clkdiv_set_rate
,
78 struct clk_hw
*ma35d1_reg_adc_clkdiv(struct device
*dev
, const char *name
,
79 struct clk_hw
*parent_hw
, spinlock_t
*lock
,
80 unsigned long flags
, void __iomem
*reg
,
81 u8 shift
, u8 width
, u32 mask_bit
)
83 struct ma35d1_adc_clk_div
*div
;
84 struct clk_init_data init
;
85 struct clk_div_table
*table
;
86 struct clk_parent_data pdata
= { .index
= 0 };
92 div
= devm_kzalloc(dev
, sizeof(*div
), GFP_KERNEL
);
94 return ERR_PTR(-ENOMEM
);
96 max_div
= clk_div_mask(width
) + 1;
99 table
= devm_kcalloc(dev
, max_div
+ 1, sizeof(*table
), GFP_KERNEL
);
101 return ERR_PTR(-ENOMEM
);
103 for (i
= 0; i
< max_div
; i
++) {
104 table
[i
].val
= min_div
+ i
;
105 table
[i
].div
= 2 * table
[i
].val
;
107 table
[max_div
].val
= 0;
108 table
[max_div
].div
= 0;
110 memset(&init
, 0, sizeof(init
));
112 init
.ops
= &ma35d1_adc_clkdiv_ops
;
114 pdata
.hw
= parent_hw
;
115 init
.parent_data
= &pdata
;
116 init
.num_parents
= 1;
121 div
->mask
= mask_bit
? BIT(mask_bit
) : 0;
123 div
->hw
.init
= &init
;
127 ret
= devm_clk_hw_register(dev
, hw
);
132 EXPORT_SYMBOL_GPL(ma35d1_reg_adc_clkdiv
);