xref: /openbmc/linux/drivers/clk/clk-s2mps11.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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