1 // SPDX-License-Identifier: GPL-2.0
3 * MStar MSC313 MPLL driver
5 * Copyright (C) 2020 Daniel Palmer <daniel@thingy.jp>
8 #include <linux/platform_device.h>
9 #include <linux/of_address.h>
10 #include <linux/clk-provider.h>
11 #include <linux/regmap.h>
13 #define REG_CONFIG1 0x8
14 #define REG_CONFIG2 0xc
16 static const struct regmap_config msc313_mpll_regmap_config
= {
22 static const struct reg_field config1_loop_div_first
= REG_FIELD(REG_CONFIG1
, 8, 9);
23 static const struct reg_field config1_input_div_first
= REG_FIELD(REG_CONFIG1
, 4, 5);
24 static const struct reg_field config2_output_div_first
= REG_FIELD(REG_CONFIG2
, 12, 13);
25 static const struct reg_field config2_loop_div_second
= REG_FIELD(REG_CONFIG2
, 0, 7);
27 static const unsigned int output_dividers
[] = {
31 #define NUMOUTPUTS (ARRAY_SIZE(output_dividers) + 1)
35 struct regmap_field
*input_div
;
36 struct regmap_field
*loop_div_first
;
37 struct regmap_field
*loop_div_second
;
38 struct regmap_field
*output_div
;
39 struct clk_hw_onecell_data
*clk_data
;
42 #define to_mpll(_hw) container_of(_hw, struct msc313_mpll, clk_hw)
44 static unsigned long msc313_mpll_recalc_rate(struct clk_hw
*hw
,
45 unsigned long parent_rate
)
47 struct msc313_mpll
*mpll
= to_mpll(hw
);
48 unsigned int input_div
, output_div
, loop_first
, loop_second
;
49 unsigned long output_rate
;
51 regmap_field_read(mpll
->input_div
, &input_div
);
52 regmap_field_read(mpll
->output_div
, &output_div
);
53 regmap_field_read(mpll
->loop_div_first
, &loop_first
);
54 regmap_field_read(mpll
->loop_div_second
, &loop_second
);
56 output_rate
= parent_rate
/ (1 << input_div
);
57 output_rate
*= (1 << loop_first
) * max(loop_second
, 1U);
58 output_rate
/= max(output_div
, 1U);
63 static const struct clk_ops msc313_mpll_ops
= {
64 .recalc_rate
= msc313_mpll_recalc_rate
,
67 static const struct clk_parent_data mpll_parent
= {
71 static int msc313_mpll_probe(struct platform_device
*pdev
)
74 struct msc313_mpll
*mpll
;
75 struct clk_init_data clk_init
= { };
76 struct device
*dev
= &pdev
->dev
;
77 struct regmap
*regmap
;
82 mpll
= devm_kzalloc(dev
, sizeof(*mpll
), GFP_KERNEL
);
86 base
= devm_platform_ioremap_resource(pdev
, 0);
90 regmap
= devm_regmap_init_mmio(dev
, base
, &msc313_mpll_regmap_config
);
92 return PTR_ERR(regmap
);
94 mpll
->input_div
= devm_regmap_field_alloc(dev
, regmap
, config1_input_div_first
);
95 if (IS_ERR(mpll
->input_div
))
96 return PTR_ERR(mpll
->input_div
);
97 mpll
->output_div
= devm_regmap_field_alloc(dev
, regmap
, config2_output_div_first
);
98 if (IS_ERR(mpll
->output_div
))
99 return PTR_ERR(mpll
->output_div
);
100 mpll
->loop_div_first
= devm_regmap_field_alloc(dev
, regmap
, config1_loop_div_first
);
101 if (IS_ERR(mpll
->loop_div_first
))
102 return PTR_ERR(mpll
->loop_div_first
);
103 mpll
->loop_div_second
= devm_regmap_field_alloc(dev
, regmap
, config2_loop_div_second
);
104 if (IS_ERR(mpll
->loop_div_second
))
105 return PTR_ERR(mpll
->loop_div_second
);
107 mpll
->clk_data
= devm_kzalloc(dev
, struct_size(mpll
->clk_data
, hws
,
108 ARRAY_SIZE(output_dividers
)), GFP_KERNEL
);
112 clk_init
.name
= dev_name(dev
);
113 clk_init
.ops
= &msc313_mpll_ops
;
114 clk_init
.parent_data
= &mpll_parent
;
115 clk_init
.num_parents
= 1;
116 mpll
->clk_hw
.init
= &clk_init
;
118 ret
= devm_clk_hw_register(dev
, &mpll
->clk_hw
);
122 mpll
->clk_data
->num
= NUMOUTPUTS
;
123 mpll
->clk_data
->hws
[0] = &mpll
->clk_hw
;
125 for (i
= 0; i
< ARRAY_SIZE(output_dividers
); i
++) {
126 outputname
= devm_kasprintf(dev
, GFP_KERNEL
, "%s_div_%u",
127 clk_init
.name
, output_dividers
[i
]);
130 divhw
= devm_clk_hw_register_fixed_factor(dev
, outputname
,
131 clk_init
.name
, 0, 1, output_dividers
[i
]);
133 return PTR_ERR(divhw
);
134 mpll
->clk_data
->hws
[i
+ 1] = divhw
;
137 platform_set_drvdata(pdev
, mpll
);
139 return devm_of_clk_add_hw_provider(&pdev
->dev
, of_clk_hw_onecell_get
,
143 static const struct of_device_id msc313_mpll_of_match
[] = {
144 { .compatible
= "mstar,msc313-mpll", },
148 static struct platform_driver msc313_mpll_driver
= {
150 .name
= "mstar-msc313-mpll",
151 .of_match_table
= msc313_mpll_of_match
,
153 .probe
= msc313_mpll_probe
,
155 builtin_platform_driver(msc313_mpll_driver
);