Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * MStar MSC313 MPLL driver
0004  *
0005  * Copyright (C) 2020 Daniel Palmer <daniel@thingy.jp>
0006  */
0007 
0008 #include <linux/platform_device.h>
0009 #include <linux/of_address.h>
0010 #include <linux/clk-provider.h>
0011 #include <linux/regmap.h>
0012 
0013 #define REG_CONFIG1 0x8
0014 #define REG_CONFIG2 0xc
0015 
0016 static const struct regmap_config msc313_mpll_regmap_config = {
0017     .reg_bits = 16,
0018     .val_bits = 16,
0019     .reg_stride = 4,
0020 };
0021 
0022 static const struct reg_field config1_loop_div_first = REG_FIELD(REG_CONFIG1, 8, 9);
0023 static const struct reg_field config1_input_div_first = REG_FIELD(REG_CONFIG1, 4, 5);
0024 static const struct reg_field config2_output_div_first = REG_FIELD(REG_CONFIG2, 12, 13);
0025 static const struct reg_field config2_loop_div_second = REG_FIELD(REG_CONFIG2, 0, 7);
0026 
0027 static const unsigned int output_dividers[] = {
0028     2, 3, 4, 5, 6, 7, 10
0029 };
0030 
0031 #define NUMOUTPUTS (ARRAY_SIZE(output_dividers) + 1)
0032 
0033 struct msc313_mpll {
0034     struct clk_hw clk_hw;
0035     struct regmap_field *input_div;
0036     struct regmap_field *loop_div_first;
0037     struct regmap_field *loop_div_second;
0038     struct regmap_field *output_div;
0039     struct clk_hw_onecell_data *clk_data;
0040 };
0041 
0042 #define to_mpll(_hw) container_of(_hw, struct msc313_mpll, clk_hw)
0043 
0044 static unsigned long msc313_mpll_recalc_rate(struct clk_hw *hw,
0045         unsigned long parent_rate)
0046 {
0047     struct msc313_mpll *mpll = to_mpll(hw);
0048     unsigned int input_div, output_div, loop_first, loop_second;
0049     unsigned long output_rate;
0050 
0051     regmap_field_read(mpll->input_div, &input_div);
0052     regmap_field_read(mpll->output_div, &output_div);
0053     regmap_field_read(mpll->loop_div_first, &loop_first);
0054     regmap_field_read(mpll->loop_div_second, &loop_second);
0055 
0056     output_rate = parent_rate / (1 << input_div);
0057     output_rate *= (1 << loop_first) * max(loop_second, 1U);
0058     output_rate /= max(output_div, 1U);
0059 
0060     return output_rate;
0061 }
0062 
0063 static const struct clk_ops msc313_mpll_ops = {
0064     .recalc_rate = msc313_mpll_recalc_rate,
0065 };
0066 
0067 static const struct clk_parent_data mpll_parent = {
0068     .index  = 0,
0069 };
0070 
0071 static int msc313_mpll_probe(struct platform_device *pdev)
0072 {
0073     void __iomem *base;
0074     struct msc313_mpll *mpll;
0075     struct clk_init_data clk_init = { };
0076     struct device *dev = &pdev->dev;
0077     struct regmap *regmap;
0078     char *outputname;
0079     struct clk_hw *divhw;
0080     int ret, i;
0081 
0082     mpll = devm_kzalloc(dev, sizeof(*mpll), GFP_KERNEL);
0083     if (!mpll)
0084         return -ENOMEM;
0085 
0086     base = devm_platform_ioremap_resource(pdev, 0);
0087     if (IS_ERR(base))
0088         return PTR_ERR(base);
0089 
0090     regmap = devm_regmap_init_mmio(dev, base, &msc313_mpll_regmap_config);
0091     if (IS_ERR(regmap))
0092         return PTR_ERR(regmap);
0093 
0094     mpll->input_div = devm_regmap_field_alloc(dev, regmap, config1_input_div_first);
0095     if (IS_ERR(mpll->input_div))
0096         return PTR_ERR(mpll->input_div);
0097     mpll->output_div = devm_regmap_field_alloc(dev, regmap, config2_output_div_first);
0098     if (IS_ERR(mpll->output_div))
0099         return PTR_ERR(mpll->output_div);
0100     mpll->loop_div_first = devm_regmap_field_alloc(dev, regmap, config1_loop_div_first);
0101     if (IS_ERR(mpll->loop_div_first))
0102         return PTR_ERR(mpll->loop_div_first);
0103     mpll->loop_div_second = devm_regmap_field_alloc(dev, regmap, config2_loop_div_second);
0104     if (IS_ERR(mpll->loop_div_second))
0105         return PTR_ERR(mpll->loop_div_second);
0106 
0107     mpll->clk_data = devm_kzalloc(dev, struct_size(mpll->clk_data, hws,
0108             ARRAY_SIZE(output_dividers)), GFP_KERNEL);
0109     if (!mpll->clk_data)
0110         return -ENOMEM;
0111 
0112     clk_init.name = dev_name(dev);
0113     clk_init.ops = &msc313_mpll_ops;
0114     clk_init.parent_data = &mpll_parent;
0115     clk_init.num_parents = 1;
0116     mpll->clk_hw.init = &clk_init;
0117 
0118     ret = devm_clk_hw_register(dev, &mpll->clk_hw);
0119     if (ret)
0120         return ret;
0121 
0122     mpll->clk_data->num = NUMOUTPUTS;
0123     mpll->clk_data->hws[0] = &mpll->clk_hw;
0124 
0125     for (i = 0; i < ARRAY_SIZE(output_dividers); i++) {
0126         outputname = devm_kasprintf(dev, GFP_KERNEL, "%s_div_%u",
0127                 clk_init.name, output_dividers[i]);
0128         if (!outputname)
0129             return -ENOMEM;
0130         divhw = devm_clk_hw_register_fixed_factor(dev, outputname,
0131                 clk_init.name, 0, 1, output_dividers[i]);
0132         if (IS_ERR(divhw))
0133             return PTR_ERR(divhw);
0134         mpll->clk_data->hws[i + 1] = divhw;
0135     }
0136 
0137     platform_set_drvdata(pdev, mpll);
0138 
0139     return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
0140             mpll->clk_data);
0141 }
0142 
0143 static const struct of_device_id msc313_mpll_of_match[] = {
0144     { .compatible = "mstar,msc313-mpll", },
0145     {}
0146 };
0147 
0148 static struct platform_driver msc313_mpll_driver = {
0149     .driver = {
0150         .name = "mstar-msc313-mpll",
0151         .of_match_table = msc313_mpll_of_match,
0152     },
0153     .probe = msc313_mpll_probe,
0154 };
0155 builtin_platform_driver(msc313_mpll_driver);