Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 //
0003 // clk-s2mps11.c - Clock driver for S2MPS11.
0004 //
0005 // Copyright (C) 2013,2014 Samsung Electornics
0006 
0007 #include <linux/module.h>
0008 #include <linux/err.h>
0009 #include <linux/of.h>
0010 #include <linux/clkdev.h>
0011 #include <linux/regmap.h>
0012 #include <linux/clk-provider.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/mfd/samsung/s2mps11.h>
0015 #include <linux/mfd/samsung/s2mps13.h>
0016 #include <linux/mfd/samsung/s2mps14.h>
0017 #include <linux/mfd/samsung/s5m8767.h>
0018 #include <linux/mfd/samsung/core.h>
0019 
0020 #include <dt-bindings/clock/samsung,s2mps11.h>
0021 
0022 struct s2mps11_clk {
0023     struct sec_pmic_dev *iodev;
0024     struct device_node *clk_np;
0025     struct clk_hw hw;
0026     struct clk *clk;
0027     struct clk_lookup *lookup;
0028     u32 mask;
0029     unsigned int reg;
0030 };
0031 
0032 static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw)
0033 {
0034     return container_of(hw, struct s2mps11_clk, hw);
0035 }
0036 
0037 static int s2mps11_clk_prepare(struct clk_hw *hw)
0038 {
0039     struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
0040 
0041     return regmap_update_bits(s2mps11->iodev->regmap_pmic,
0042                  s2mps11->reg,
0043                  s2mps11->mask, s2mps11->mask);
0044 }
0045 
0046 static void s2mps11_clk_unprepare(struct clk_hw *hw)
0047 {
0048     struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
0049 
0050     regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg,
0051                s2mps11->mask, ~s2mps11->mask);
0052 }
0053 
0054 static int s2mps11_clk_is_prepared(struct clk_hw *hw)
0055 {
0056     int ret;
0057     u32 val;
0058     struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
0059 
0060     ret = regmap_read(s2mps11->iodev->regmap_pmic,
0061                 s2mps11->reg, &val);
0062     if (ret < 0)
0063         return -EINVAL;
0064 
0065     return val & s2mps11->mask;
0066 }
0067 
0068 static unsigned long s2mps11_clk_recalc_rate(struct clk_hw *hw,
0069                          unsigned long parent_rate)
0070 {
0071     return 32768;
0072 }
0073 
0074 static const struct clk_ops s2mps11_clk_ops = {
0075     .prepare    = s2mps11_clk_prepare,
0076     .unprepare  = s2mps11_clk_unprepare,
0077     .is_prepared    = s2mps11_clk_is_prepared,
0078     .recalc_rate    = s2mps11_clk_recalc_rate,
0079 };
0080 
0081 /* This s2mps11_clks_init tructure is common to s2mps11, s2mps13 and s2mps14 */
0082 static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = {
0083     [S2MPS11_CLK_AP] = {
0084         .name = "s2mps11_ap",
0085         .ops = &s2mps11_clk_ops,
0086     },
0087     [S2MPS11_CLK_CP] = {
0088         .name = "s2mps11_cp",
0089         .ops = &s2mps11_clk_ops,
0090     },
0091     [S2MPS11_CLK_BT] = {
0092         .name = "s2mps11_bt",
0093         .ops = &s2mps11_clk_ops,
0094     },
0095 };
0096 
0097 static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev,
0098         struct clk_init_data *clks_init)
0099 {
0100     struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
0101     struct device_node *clk_np;
0102     int i;
0103 
0104     if (!iodev->dev->of_node)
0105         return ERR_PTR(-EINVAL);
0106 
0107     clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks");
0108     if (!clk_np) {
0109         dev_err(&pdev->dev, "could not find clock sub-node\n");
0110         return ERR_PTR(-EINVAL);
0111     }
0112 
0113     for (i = 0; i < S2MPS11_CLKS_NUM; i++)
0114         of_property_read_string_index(clk_np, "clock-output-names", i,
0115                 &clks_init[i].name);
0116 
0117     return clk_np;
0118 }
0119 
0120 static int s2mps11_clk_probe(struct platform_device *pdev)
0121 {
0122     struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
0123     struct s2mps11_clk *s2mps11_clks;
0124     struct clk_hw_onecell_data *clk_data;
0125     unsigned int s2mps11_reg;
0126     int i, ret = 0;
0127     enum sec_device_type hwid = platform_get_device_id(pdev)->driver_data;
0128 
0129     s2mps11_clks = devm_kcalloc(&pdev->dev, S2MPS11_CLKS_NUM,
0130                 sizeof(*s2mps11_clks), GFP_KERNEL);
0131     if (!s2mps11_clks)
0132         return -ENOMEM;
0133 
0134     clk_data = devm_kzalloc(&pdev->dev,
0135                 struct_size(clk_data, hws, S2MPS11_CLKS_NUM),
0136                 GFP_KERNEL);
0137     if (!clk_data)
0138         return -ENOMEM;
0139 
0140     switch (hwid) {
0141     case S2MPS11X:
0142         s2mps11_reg = S2MPS11_REG_RTC_CTRL;
0143         break;
0144     case S2MPS13X:
0145         s2mps11_reg = S2MPS13_REG_RTCCTRL;
0146         break;
0147     case S2MPS14X:
0148         s2mps11_reg = S2MPS14_REG_RTCCTRL;
0149         break;
0150     case S5M8767X:
0151         s2mps11_reg = S5M8767_REG_CTRL1;
0152         break;
0153     default:
0154         dev_err(&pdev->dev, "Invalid device type\n");
0155         return -EINVAL;
0156     }
0157 
0158     /* Store clocks of_node in first element of s2mps11_clks array */
0159     s2mps11_clks->clk_np = s2mps11_clk_parse_dt(pdev, s2mps11_clks_init);
0160     if (IS_ERR(s2mps11_clks->clk_np))
0161         return PTR_ERR(s2mps11_clks->clk_np);
0162 
0163     for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
0164         if (i == S2MPS11_CLK_CP && hwid == S2MPS14X)
0165             continue; /* Skip clocks not present in some devices */
0166         s2mps11_clks[i].iodev = iodev;
0167         s2mps11_clks[i].hw.init = &s2mps11_clks_init[i];
0168         s2mps11_clks[i].mask = 1 << i;
0169         s2mps11_clks[i].reg = s2mps11_reg;
0170 
0171         s2mps11_clks[i].clk = devm_clk_register(&pdev->dev,
0172                             &s2mps11_clks[i].hw);
0173         if (IS_ERR(s2mps11_clks[i].clk)) {
0174             dev_err(&pdev->dev, "Fail to register : %s\n",
0175                         s2mps11_clks_init[i].name);
0176             ret = PTR_ERR(s2mps11_clks[i].clk);
0177             goto err_reg;
0178         }
0179 
0180         s2mps11_clks[i].lookup = clkdev_hw_create(&s2mps11_clks[i].hw,
0181                     s2mps11_clks_init[i].name, NULL);
0182         if (!s2mps11_clks[i].lookup) {
0183             ret = -ENOMEM;
0184             goto err_reg;
0185         }
0186         clk_data->hws[i] = &s2mps11_clks[i].hw;
0187     }
0188 
0189     clk_data->num = S2MPS11_CLKS_NUM;
0190     of_clk_add_hw_provider(s2mps11_clks->clk_np, of_clk_hw_onecell_get,
0191                    clk_data);
0192 
0193     platform_set_drvdata(pdev, s2mps11_clks);
0194 
0195     return ret;
0196 
0197 err_reg:
0198     of_node_put(s2mps11_clks[0].clk_np);
0199     while (--i >= 0)
0200         clkdev_drop(s2mps11_clks[i].lookup);
0201 
0202     return ret;
0203 }
0204 
0205 static int s2mps11_clk_remove(struct platform_device *pdev)
0206 {
0207     struct s2mps11_clk *s2mps11_clks = platform_get_drvdata(pdev);
0208     int i;
0209 
0210     of_clk_del_provider(s2mps11_clks[0].clk_np);
0211     /* Drop the reference obtained in s2mps11_clk_parse_dt */
0212     of_node_put(s2mps11_clks[0].clk_np);
0213 
0214     for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
0215         /* Skip clocks not present on S2MPS14 */
0216         if (!s2mps11_clks[i].lookup)
0217             continue;
0218         clkdev_drop(s2mps11_clks[i].lookup);
0219     }
0220 
0221     return 0;
0222 }
0223 
0224 static const struct platform_device_id s2mps11_clk_id[] = {
0225     { "s2mps11-clk", S2MPS11X},
0226     { "s2mps13-clk", S2MPS13X},
0227     { "s2mps14-clk", S2MPS14X},
0228     { "s5m8767-clk", S5M8767X},
0229     { },
0230 };
0231 MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
0232 
0233 #ifdef CONFIG_OF
0234 /*
0235  * Device is instantiated through parent MFD device and device matching is done
0236  * through platform_device_id.
0237  *
0238  * However if device's DT node contains proper clock compatible and driver is
0239  * built as a module, then the *module* matching will be done trough DT aliases.
0240  * This requires of_device_id table.  In the same time this will not change the
0241  * actual *device* matching so do not add .of_match_table.
0242  */
0243 static const struct of_device_id s2mps11_dt_match[] __used = {
0244     {
0245         .compatible = "samsung,s2mps11-clk",
0246         .data = (void *)S2MPS11X,
0247     }, {
0248         .compatible = "samsung,s2mps13-clk",
0249         .data = (void *)S2MPS13X,
0250     }, {
0251         .compatible = "samsung,s2mps14-clk",
0252         .data = (void *)S2MPS14X,
0253     }, {
0254         .compatible = "samsung,s5m8767-clk",
0255         .data = (void *)S5M8767X,
0256     }, {
0257         /* Sentinel */
0258     },
0259 };
0260 MODULE_DEVICE_TABLE(of, s2mps11_dt_match);
0261 #endif
0262 
0263 static struct platform_driver s2mps11_clk_driver = {
0264     .driver = {
0265         .name  = "s2mps11-clk",
0266     },
0267     .probe = s2mps11_clk_probe,
0268     .remove = s2mps11_clk_remove,
0269     .id_table = s2mps11_clk_id,
0270 };
0271 module_platform_driver(s2mps11_clk_driver);
0272 
0273 MODULE_DESCRIPTION("S2MPS11 Clock Driver");
0274 MODULE_AUTHOR("Yadwinder Singh Brar <yadi.brar@samsung.com>");
0275 MODULE_LICENSE("GPL");