194047d97SKrzysztof Kozlowski // SPDX-License-Identifier: GPL-2.0+
294047d97SKrzysztof Kozlowski //
394047d97SKrzysztof Kozlowski // clk-s2mps11.c - Clock driver for S2MPS11.
494047d97SKrzysztof Kozlowski //
594047d97SKrzysztof Kozlowski // Copyright (C) 2013,2014 Samsung Electornics
67cc560deSYadwinder Singh Brar
77cc560deSYadwinder Singh Brar #include <linux/module.h>
87cc560deSYadwinder Singh Brar #include <linux/err.h>
97cc560deSYadwinder Singh Brar #include <linux/of.h>
107cc560deSYadwinder Singh Brar #include <linux/clkdev.h>
117cc560deSYadwinder Singh Brar #include <linux/regmap.h>
127cc560deSYadwinder Singh Brar #include <linux/clk-provider.h>
137cc560deSYadwinder Singh Brar #include <linux/platform_device.h>
147cc560deSYadwinder Singh Brar #include <linux/mfd/samsung/s2mps11.h>
15f928b53dSChanwoo Choi #include <linux/mfd/samsung/s2mps13.h>
16e8b60a45SKrzysztof Kozlowski #include <linux/mfd/samsung/s2mps14.h>
17e8e6b840STushar Behera #include <linux/mfd/samsung/s5m8767.h>
187cc560deSYadwinder Singh Brar #include <linux/mfd/samsung/core.h>
197cc560deSYadwinder Singh Brar
208748b4a7SKrzysztof Kozlowski #include <dt-bindings/clock/samsung,s2mps11.h>
217cc560deSYadwinder Singh Brar
227cc560deSYadwinder Singh Brar struct s2mps11_clk {
237cc560deSYadwinder Singh Brar struct sec_pmic_dev *iodev;
24bf416bd4SKrzysztof Kozlowski struct device_node *clk_np;
257cc560deSYadwinder Singh Brar struct clk_hw hw;
267cc560deSYadwinder Singh Brar struct clk *clk;
277cc560deSYadwinder Singh Brar struct clk_lookup *lookup;
287cc560deSYadwinder Singh Brar u32 mask;
2964d64c35STushar Behera unsigned int reg;
307cc560deSYadwinder Singh Brar };
317cc560deSYadwinder Singh Brar
to_s2mps11_clk(struct clk_hw * hw)327cc560deSYadwinder Singh Brar static struct s2mps11_clk *to_s2mps11_clk(struct clk_hw *hw)
337cc560deSYadwinder Singh Brar {
347cc560deSYadwinder Singh Brar return container_of(hw, struct s2mps11_clk, hw);
357cc560deSYadwinder Singh Brar }
367cc560deSYadwinder Singh Brar
s2mps11_clk_prepare(struct clk_hw * hw)377cc560deSYadwinder Singh Brar static int s2mps11_clk_prepare(struct clk_hw *hw)
387cc560deSYadwinder Singh Brar {
397cc560deSYadwinder Singh Brar struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
407cc560deSYadwinder Singh Brar
417764d0cdSVaibhav Hiremath return regmap_update_bits(s2mps11->iodev->regmap_pmic,
4264d64c35STushar Behera s2mps11->reg,
437cc560deSYadwinder Singh Brar s2mps11->mask, s2mps11->mask);
447cc560deSYadwinder Singh Brar }
457cc560deSYadwinder Singh Brar
s2mps11_clk_unprepare(struct clk_hw * hw)467cc560deSYadwinder Singh Brar static void s2mps11_clk_unprepare(struct clk_hw *hw)
477cc560deSYadwinder Singh Brar {
487cc560deSYadwinder Singh Brar struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
497cc560deSYadwinder Singh Brar
507764d0cdSVaibhav Hiremath regmap_update_bits(s2mps11->iodev->regmap_pmic, s2mps11->reg,
517cc560deSYadwinder Singh Brar s2mps11->mask, ~s2mps11->mask);
527cc560deSYadwinder Singh Brar }
537cc560deSYadwinder Singh Brar
s2mps11_clk_is_prepared(struct clk_hw * hw)5489ed7e6eSKarol Wrona static int s2mps11_clk_is_prepared(struct clk_hw *hw)
557cc560deSYadwinder Singh Brar {
5689ed7e6eSKarol Wrona int ret;
5789ed7e6eSKarol Wrona u32 val;
587cc560deSYadwinder Singh Brar struct s2mps11_clk *s2mps11 = to_s2mps11_clk(hw);
597cc560deSYadwinder Singh Brar
6089ed7e6eSKarol Wrona ret = regmap_read(s2mps11->iodev->regmap_pmic,
6189ed7e6eSKarol Wrona s2mps11->reg, &val);
6289ed7e6eSKarol Wrona if (ret < 0)
6389ed7e6eSKarol Wrona return -EINVAL;
6489ed7e6eSKarol Wrona
6589ed7e6eSKarol Wrona return val & s2mps11->mask;
667cc560deSYadwinder Singh Brar }
677cc560deSYadwinder Singh Brar
s2mps11_clk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)687cc560deSYadwinder Singh Brar static unsigned long s2mps11_clk_recalc_rate(struct clk_hw *hw,
697cc560deSYadwinder Singh Brar unsigned long parent_rate)
707cc560deSYadwinder Singh Brar {
717cc560deSYadwinder Singh Brar return 32768;
727cc560deSYadwinder Singh Brar }
737cc560deSYadwinder Singh Brar
7456950ff8SJulia Lawall static const struct clk_ops s2mps11_clk_ops = {
757cc560deSYadwinder Singh Brar .prepare = s2mps11_clk_prepare,
767cc560deSYadwinder Singh Brar .unprepare = s2mps11_clk_unprepare,
7789ed7e6eSKarol Wrona .is_prepared = s2mps11_clk_is_prepared,
787cc560deSYadwinder Singh Brar .recalc_rate = s2mps11_clk_recalc_rate,
797cc560deSYadwinder Singh Brar };
807cc560deSYadwinder Singh Brar
8131ad0e2aSAndi Shyti /* This s2mps11_clks_init tructure is common to s2mps11, s2mps13 and s2mps14 */
827cc560deSYadwinder Singh Brar static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = {
837cc560deSYadwinder Singh Brar [S2MPS11_CLK_AP] = {
847cc560deSYadwinder Singh Brar .name = "s2mps11_ap",
857cc560deSYadwinder Singh Brar .ops = &s2mps11_clk_ops,
867cc560deSYadwinder Singh Brar },
877cc560deSYadwinder Singh Brar [S2MPS11_CLK_CP] = {
887cc560deSYadwinder Singh Brar .name = "s2mps11_cp",
897cc560deSYadwinder Singh Brar .ops = &s2mps11_clk_ops,
907cc560deSYadwinder Singh Brar },
917cc560deSYadwinder Singh Brar [S2MPS11_CLK_BT] = {
927cc560deSYadwinder Singh Brar .name = "s2mps11_bt",
937cc560deSYadwinder Singh Brar .ops = &s2mps11_clk_ops,
947cc560deSYadwinder Singh Brar },
957cc560deSYadwinder Singh Brar };
967cc560deSYadwinder Singh Brar
s2mps11_clk_parse_dt(struct platform_device * pdev,struct clk_init_data * clks_init)97e8b60a45SKrzysztof Kozlowski static struct device_node *s2mps11_clk_parse_dt(struct platform_device *pdev,
98e8b60a45SKrzysztof Kozlowski struct clk_init_data *clks_init)
997cc560deSYadwinder Singh Brar {
1007cc560deSYadwinder Singh Brar struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
1017cc560deSYadwinder Singh Brar struct device_node *clk_np;
1027cc560deSYadwinder Singh Brar int i;
1037cc560deSYadwinder Singh Brar
1047cc560deSYadwinder Singh Brar if (!iodev->dev->of_node)
105238e1405SKrzysztof Kozlowski return ERR_PTR(-EINVAL);
1067cc560deSYadwinder Singh Brar
107afbd1a0cSKrzysztof Kozlowski clk_np = of_get_child_by_name(iodev->dev->of_node, "clocks");
1087cc560deSYadwinder Singh Brar if (!clk_np) {
1097cc560deSYadwinder Singh Brar dev_err(&pdev->dev, "could not find clock sub-node\n");
1107cc560deSYadwinder Singh Brar return ERR_PTR(-EINVAL);
1117cc560deSYadwinder Singh Brar }
1127cc560deSYadwinder Singh Brar
11331ad0e2aSAndi Shyti for (i = 0; i < S2MPS11_CLKS_NUM; i++)
1147cc560deSYadwinder Singh Brar of_property_read_string_index(clk_np, "clock-output-names", i,
115e8b60a45SKrzysztof Kozlowski &clks_init[i].name);
1167cc560deSYadwinder Singh Brar
1177cc560deSYadwinder Singh Brar return clk_np;
1187cc560deSYadwinder Singh Brar }
1197cc560deSYadwinder Singh Brar
s2mps11_clk_probe(struct platform_device * pdev)1207cc560deSYadwinder Singh Brar static int s2mps11_clk_probe(struct platform_device *pdev)
1217cc560deSYadwinder Singh Brar {
1227cc560deSYadwinder Singh Brar struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
123dedbb272SAndi Shyti struct s2mps11_clk *s2mps11_clks;
124b228fad5SStephen Boyd struct clk_hw_onecell_data *clk_data;
12564d64c35STushar Behera unsigned int s2mps11_reg;
1267cc560deSYadwinder Singh Brar int i, ret = 0;
12731ad0e2aSAndi Shyti enum sec_device_type hwid = platform_get_device_id(pdev)->driver_data;
1287cc560deSYadwinder Singh Brar
1297764d0cdSVaibhav Hiremath s2mps11_clks = devm_kcalloc(&pdev->dev, S2MPS11_CLKS_NUM,
130dedbb272SAndi Shyti sizeof(*s2mps11_clks), GFP_KERNEL);
1317cc560deSYadwinder Singh Brar if (!s2mps11_clks)
1327cc560deSYadwinder Singh Brar return -ENOMEM;
1337cc560deSYadwinder Singh Brar
1340ed2dd03SKees Cook clk_data = devm_kzalloc(&pdev->dev,
1350ed2dd03SKees Cook struct_size(clk_data, hws, S2MPS11_CLKS_NUM),
136b228fad5SStephen Boyd GFP_KERNEL);
13736da4defSAndi Shyti if (!clk_data)
13836da4defSAndi Shyti return -ENOMEM;
13936da4defSAndi Shyti
14031ad0e2aSAndi Shyti switch (hwid) {
14164d64c35STushar Behera case S2MPS11X:
14264d64c35STushar Behera s2mps11_reg = S2MPS11_REG_RTC_CTRL;
143e8b60a45SKrzysztof Kozlowski break;
144f928b53dSChanwoo Choi case S2MPS13X:
145f928b53dSChanwoo Choi s2mps11_reg = S2MPS13_REG_RTCCTRL;
146f928b53dSChanwoo Choi break;
147e8b60a45SKrzysztof Kozlowski case S2MPS14X:
148e8b60a45SKrzysztof Kozlowski s2mps11_reg = S2MPS14_REG_RTCCTRL;
14964d64c35STushar Behera break;
150e8e6b840STushar Behera case S5M8767X:
151e8e6b840STushar Behera s2mps11_reg = S5M8767_REG_CTRL1;
152e8e6b840STushar Behera break;
15364d64c35STushar Behera default:
15464d64c35STushar Behera dev_err(&pdev->dev, "Invalid device type\n");
15564d64c35STushar Behera return -EINVAL;
156250d07d1Skbuild test robot }
15764d64c35STushar Behera
158e8b60a45SKrzysztof Kozlowski /* Store clocks of_node in first element of s2mps11_clks array */
15931ad0e2aSAndi Shyti s2mps11_clks->clk_np = s2mps11_clk_parse_dt(pdev, s2mps11_clks_init);
160e8b60a45SKrzysztof Kozlowski if (IS_ERR(s2mps11_clks->clk_np))
161e8b60a45SKrzysztof Kozlowski return PTR_ERR(s2mps11_clks->clk_np);
162e8b60a45SKrzysztof Kozlowski
163dedbb272SAndi Shyti for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
16431ad0e2aSAndi Shyti if (i == S2MPS11_CLK_CP && hwid == S2MPS14X)
165e8b60a45SKrzysztof Kozlowski continue; /* Skip clocks not present in some devices */
166dedbb272SAndi Shyti s2mps11_clks[i].iodev = iodev;
167dedbb272SAndi Shyti s2mps11_clks[i].hw.init = &s2mps11_clks_init[i];
168dedbb272SAndi Shyti s2mps11_clks[i].mask = 1 << i;
169dedbb272SAndi Shyti s2mps11_clks[i].reg = s2mps11_reg;
1707cc560deSYadwinder Singh Brar
171dedbb272SAndi Shyti s2mps11_clks[i].clk = devm_clk_register(&pdev->dev,
172dedbb272SAndi Shyti &s2mps11_clks[i].hw);
173dedbb272SAndi Shyti if (IS_ERR(s2mps11_clks[i].clk)) {
1747cc560deSYadwinder Singh Brar dev_err(&pdev->dev, "Fail to register : %s\n",
175dedbb272SAndi Shyti s2mps11_clks_init[i].name);
176dedbb272SAndi Shyti ret = PTR_ERR(s2mps11_clks[i].clk);
1777cc560deSYadwinder Singh Brar goto err_reg;
1787cc560deSYadwinder Singh Brar }
1797cc560deSYadwinder Singh Brar
180b228fad5SStephen Boyd s2mps11_clks[i].lookup = clkdev_hw_create(&s2mps11_clks[i].hw,
181dedbb272SAndi Shyti s2mps11_clks_init[i].name, NULL);
182dedbb272SAndi Shyti if (!s2mps11_clks[i].lookup) {
1837cc560deSYadwinder Singh Brar ret = -ENOMEM;
184264e3b75SAxel Lin goto err_reg;
1857cc560deSYadwinder Singh Brar }
186b228fad5SStephen Boyd clk_data->hws[i] = &s2mps11_clks[i].hw;
187e8b60a45SKrzysztof Kozlowski }
1887cc560deSYadwinder Singh Brar
189b228fad5SStephen Boyd clk_data->num = S2MPS11_CLKS_NUM;
190b228fad5SStephen Boyd of_clk_add_hw_provider(s2mps11_clks->clk_np, of_clk_hw_onecell_get,
19136da4defSAndi Shyti clk_data);
1927cc560deSYadwinder Singh Brar
1937cc560deSYadwinder Singh Brar platform_set_drvdata(pdev, s2mps11_clks);
1947cc560deSYadwinder Singh Brar
1957cc560deSYadwinder Singh Brar return ret;
196264e3b75SAxel Lin
1977cc560deSYadwinder Singh Brar err_reg:
198d2d94fc5SChristophe JAILLET of_node_put(s2mps11_clks[0].clk_np);
199264e3b75SAxel Lin while (--i >= 0)
200264e3b75SAxel Lin clkdev_drop(s2mps11_clks[i].lookup);
2017cc560deSYadwinder Singh Brar
2027cc560deSYadwinder Singh Brar return ret;
2037cc560deSYadwinder Singh Brar }
2047cc560deSYadwinder Singh Brar
s2mps11_clk_remove(struct platform_device * pdev)205*34014ff8SUwe Kleine-König static void s2mps11_clk_remove(struct platform_device *pdev)
2067cc560deSYadwinder Singh Brar {
2077cc560deSYadwinder Singh Brar struct s2mps11_clk *s2mps11_clks = platform_get_drvdata(pdev);
2087cc560deSYadwinder Singh Brar int i;
2097cc560deSYadwinder Singh Brar
210bf416bd4SKrzysztof Kozlowski of_clk_del_provider(s2mps11_clks[0].clk_np);
211bf416bd4SKrzysztof Kozlowski /* Drop the reference obtained in s2mps11_clk_parse_dt */
212bf416bd4SKrzysztof Kozlowski of_node_put(s2mps11_clks[0].clk_np);
213bf416bd4SKrzysztof Kozlowski
214e8b60a45SKrzysztof Kozlowski for (i = 0; i < S2MPS11_CLKS_NUM; i++) {
215e8b60a45SKrzysztof Kozlowski /* Skip clocks not present on S2MPS14 */
216e8b60a45SKrzysztof Kozlowski if (!s2mps11_clks[i].lookup)
217e8b60a45SKrzysztof Kozlowski continue;
2187cc560deSYadwinder Singh Brar clkdev_drop(s2mps11_clks[i].lookup);
219e8b60a45SKrzysztof Kozlowski }
2207cc560deSYadwinder Singh Brar }
2217cc560deSYadwinder Singh Brar
2227cc560deSYadwinder Singh Brar static const struct platform_device_id s2mps11_clk_id[] = {
22364d64c35STushar Behera { "s2mps11-clk", S2MPS11X},
224f928b53dSChanwoo Choi { "s2mps13-clk", S2MPS13X},
225e8b60a45SKrzysztof Kozlowski { "s2mps14-clk", S2MPS14X},
226e8e6b840STushar Behera { "s5m8767-clk", S5M8767X},
2277cc560deSYadwinder Singh Brar { },
2287cc560deSYadwinder Singh Brar };
2297cc560deSYadwinder Singh Brar MODULE_DEVICE_TABLE(platform, s2mps11_clk_id);
2307cc560deSYadwinder Singh Brar
2318985167eSKrzysztof Kozlowski #ifdef CONFIG_OF
2328985167eSKrzysztof Kozlowski /*
2338985167eSKrzysztof Kozlowski * Device is instantiated through parent MFD device and device matching is done
2348985167eSKrzysztof Kozlowski * through platform_device_id.
2358985167eSKrzysztof Kozlowski *
2368985167eSKrzysztof Kozlowski * However if device's DT node contains proper clock compatible and driver is
2378985167eSKrzysztof Kozlowski * built as a module, then the *module* matching will be done trough DT aliases.
2388985167eSKrzysztof Kozlowski * This requires of_device_id table. In the same time this will not change the
2398985167eSKrzysztof Kozlowski * actual *device* matching so do not add .of_match_table.
2408985167eSKrzysztof Kozlowski */
2419c940bbeSNathan Chancellor static const struct of_device_id s2mps11_dt_match[] __used = {
2428985167eSKrzysztof Kozlowski {
2438985167eSKrzysztof Kozlowski .compatible = "samsung,s2mps11-clk",
2448985167eSKrzysztof Kozlowski .data = (void *)S2MPS11X,
2458985167eSKrzysztof Kozlowski }, {
2468985167eSKrzysztof Kozlowski .compatible = "samsung,s2mps13-clk",
2478985167eSKrzysztof Kozlowski .data = (void *)S2MPS13X,
2488985167eSKrzysztof Kozlowski }, {
2498985167eSKrzysztof Kozlowski .compatible = "samsung,s2mps14-clk",
2508985167eSKrzysztof Kozlowski .data = (void *)S2MPS14X,
2518985167eSKrzysztof Kozlowski }, {
2528985167eSKrzysztof Kozlowski .compatible = "samsung,s5m8767-clk",
2538985167eSKrzysztof Kozlowski .data = (void *)S5M8767X,
2548985167eSKrzysztof Kozlowski }, {
2558985167eSKrzysztof Kozlowski /* Sentinel */
2568985167eSKrzysztof Kozlowski },
2578985167eSKrzysztof Kozlowski };
2588985167eSKrzysztof Kozlowski MODULE_DEVICE_TABLE(of, s2mps11_dt_match);
2598985167eSKrzysztof Kozlowski #endif
2608985167eSKrzysztof Kozlowski
2617cc560deSYadwinder Singh Brar static struct platform_driver s2mps11_clk_driver = {
2627cc560deSYadwinder Singh Brar .driver = {
2637cc560deSYadwinder Singh Brar .name = "s2mps11-clk",
2647cc560deSYadwinder Singh Brar },
2657cc560deSYadwinder Singh Brar .probe = s2mps11_clk_probe,
266*34014ff8SUwe Kleine-König .remove_new = s2mps11_clk_remove,
2677cc560deSYadwinder Singh Brar .id_table = s2mps11_clk_id,
2687cc560deSYadwinder Singh Brar };
269533852d7SKrzysztof Kozlowski module_platform_driver(s2mps11_clk_driver);
2707cc560deSYadwinder Singh Brar
2717cc560deSYadwinder Singh Brar MODULE_DESCRIPTION("S2MPS11 Clock Driver");
2727cc560deSYadwinder Singh Brar MODULE_AUTHOR("Yadwinder Singh Brar <yadi.brar@samsung.com>");
2737cc560deSYadwinder Singh Brar MODULE_LICENSE("GPL");
274