0001
0002
0003
0004
0005
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
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
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;
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
0212 of_node_put(s2mps11_clks[0].clk_np);
0213
0214 for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
0215
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
0236
0237
0238
0239
0240
0241
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
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");