xref: /openbmc/linux/drivers/clk/clk-pwm.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29a74ccdbSPhilipp Zabel /*
39a74ccdbSPhilipp Zabel  * Copyright (C) 2014 Philipp Zabel, Pengutronix
49a74ccdbSPhilipp Zabel  *
59a74ccdbSPhilipp Zabel  * PWM (mis)used as clock output
69a74ccdbSPhilipp Zabel  */
79a74ccdbSPhilipp Zabel #include <linux/clk-provider.h>
89a74ccdbSPhilipp Zabel #include <linux/kernel.h>
99a74ccdbSPhilipp Zabel #include <linux/module.h>
109a74ccdbSPhilipp Zabel #include <linux/of.h>
119a74ccdbSPhilipp Zabel #include <linux/platform_device.h>
129a74ccdbSPhilipp Zabel #include <linux/pwm.h>
139a74ccdbSPhilipp Zabel 
149a74ccdbSPhilipp Zabel struct clk_pwm {
159a74ccdbSPhilipp Zabel 	struct clk_hw hw;
169a74ccdbSPhilipp Zabel 	struct pwm_device *pwm;
179a74ccdbSPhilipp Zabel 	u32 fixed_rate;
189a74ccdbSPhilipp Zabel };
199a74ccdbSPhilipp Zabel 
to_clk_pwm(struct clk_hw * hw)209a74ccdbSPhilipp Zabel static inline struct clk_pwm *to_clk_pwm(struct clk_hw *hw)
219a74ccdbSPhilipp Zabel {
229a74ccdbSPhilipp Zabel 	return container_of(hw, struct clk_pwm, hw);
239a74ccdbSPhilipp Zabel }
249a74ccdbSPhilipp Zabel 
clk_pwm_prepare(struct clk_hw * hw)259a74ccdbSPhilipp Zabel static int clk_pwm_prepare(struct clk_hw *hw)
269a74ccdbSPhilipp Zabel {
279a74ccdbSPhilipp Zabel 	struct clk_pwm *clk_pwm = to_clk_pwm(hw);
289a74ccdbSPhilipp Zabel 
299a74ccdbSPhilipp Zabel 	return pwm_enable(clk_pwm->pwm);
309a74ccdbSPhilipp Zabel }
319a74ccdbSPhilipp Zabel 
clk_pwm_unprepare(struct clk_hw * hw)329a74ccdbSPhilipp Zabel static void clk_pwm_unprepare(struct clk_hw *hw)
339a74ccdbSPhilipp Zabel {
349a74ccdbSPhilipp Zabel 	struct clk_pwm *clk_pwm = to_clk_pwm(hw);
359a74ccdbSPhilipp Zabel 
369a74ccdbSPhilipp Zabel 	pwm_disable(clk_pwm->pwm);
379a74ccdbSPhilipp Zabel }
389a74ccdbSPhilipp Zabel 
clk_pwm_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)399a74ccdbSPhilipp Zabel static unsigned long clk_pwm_recalc_rate(struct clk_hw *hw,
409a74ccdbSPhilipp Zabel 					 unsigned long parent_rate)
419a74ccdbSPhilipp Zabel {
429a74ccdbSPhilipp Zabel 	struct clk_pwm *clk_pwm = to_clk_pwm(hw);
439a74ccdbSPhilipp Zabel 
449a74ccdbSPhilipp Zabel 	return clk_pwm->fixed_rate;
459a74ccdbSPhilipp Zabel }
469a74ccdbSPhilipp Zabel 
clk_pwm_get_duty_cycle(struct clk_hw * hw,struct clk_duty * duty)474c34282fSMartin Blumenstingl static int clk_pwm_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
484c34282fSMartin Blumenstingl {
494c34282fSMartin Blumenstingl 	struct clk_pwm *clk_pwm = to_clk_pwm(hw);
504c34282fSMartin Blumenstingl 	struct pwm_state state;
514c34282fSMartin Blumenstingl 
524c34282fSMartin Blumenstingl 	pwm_get_state(clk_pwm->pwm, &state);
534c34282fSMartin Blumenstingl 
544c34282fSMartin Blumenstingl 	duty->num = state.duty_cycle;
554c34282fSMartin Blumenstingl 	duty->den = state.period;
564c34282fSMartin Blumenstingl 
574c34282fSMartin Blumenstingl 	return 0;
584c34282fSMartin Blumenstingl }
594c34282fSMartin Blumenstingl 
609a74ccdbSPhilipp Zabel static const struct clk_ops clk_pwm_ops = {
619a74ccdbSPhilipp Zabel 	.prepare = clk_pwm_prepare,
629a74ccdbSPhilipp Zabel 	.unprepare = clk_pwm_unprepare,
639a74ccdbSPhilipp Zabel 	.recalc_rate = clk_pwm_recalc_rate,
644c34282fSMartin Blumenstingl 	.get_duty_cycle = clk_pwm_get_duty_cycle,
659a74ccdbSPhilipp Zabel };
669a74ccdbSPhilipp Zabel 
clk_pwm_probe(struct platform_device * pdev)679a74ccdbSPhilipp Zabel static int clk_pwm_probe(struct platform_device *pdev)
689a74ccdbSPhilipp Zabel {
699a74ccdbSPhilipp Zabel 	struct device_node *node = pdev->dev.of_node;
709a74ccdbSPhilipp Zabel 	struct clk_init_data init;
719a74ccdbSPhilipp Zabel 	struct clk_pwm *clk_pwm;
729a74ccdbSPhilipp Zabel 	struct pwm_device *pwm;
73dd0b38b7SBoris Brezillon 	struct pwm_args pargs;
749a74ccdbSPhilipp Zabel 	const char *clk_name;
759a74ccdbSPhilipp Zabel 	int ret;
769a74ccdbSPhilipp Zabel 
779a74ccdbSPhilipp Zabel 	clk_pwm = devm_kzalloc(&pdev->dev, sizeof(*clk_pwm), GFP_KERNEL);
789a74ccdbSPhilipp Zabel 	if (!clk_pwm)
799a74ccdbSPhilipp Zabel 		return -ENOMEM;
809a74ccdbSPhilipp Zabel 
819a74ccdbSPhilipp Zabel 	pwm = devm_pwm_get(&pdev->dev, NULL);
829a74ccdbSPhilipp Zabel 	if (IS_ERR(pwm))
839a74ccdbSPhilipp Zabel 		return PTR_ERR(pwm);
849a74ccdbSPhilipp Zabel 
85dd0b38b7SBoris Brezillon 	pwm_get_args(pwm, &pargs);
86dd0b38b7SBoris Brezillon 	if (!pargs.period) {
879a74ccdbSPhilipp Zabel 		dev_err(&pdev->dev, "invalid PWM period\n");
889a74ccdbSPhilipp Zabel 		return -EINVAL;
899a74ccdbSPhilipp Zabel 	}
909a74ccdbSPhilipp Zabel 
919a74ccdbSPhilipp Zabel 	if (of_property_read_u32(node, "clock-frequency", &clk_pwm->fixed_rate))
92a6733474SGuru Das Srinagesh 		clk_pwm->fixed_rate = div64_u64(NSEC_PER_SEC, pargs.period);
93a6733474SGuru Das Srinagesh 
94a6733474SGuru Das Srinagesh 	if (!clk_pwm->fixed_rate) {
95a6733474SGuru Das Srinagesh 		dev_err(&pdev->dev, "fixed_rate cannot be zero\n");
96a6733474SGuru Das Srinagesh 		return -EINVAL;
97a6733474SGuru Das Srinagesh 	}
989a74ccdbSPhilipp Zabel 
99dd0b38b7SBoris Brezillon 	if (pargs.period != NSEC_PER_SEC / clk_pwm->fixed_rate &&
100dd0b38b7SBoris Brezillon 	    pargs.period != DIV_ROUND_UP(NSEC_PER_SEC, clk_pwm->fixed_rate)) {
1019a74ccdbSPhilipp Zabel 		dev_err(&pdev->dev,
1029a74ccdbSPhilipp Zabel 			"clock-frequency does not match PWM period\n");
1039a74ccdbSPhilipp Zabel 		return -EINVAL;
1049a74ccdbSPhilipp Zabel 	}
1059a74ccdbSPhilipp Zabel 
106dd0b38b7SBoris Brezillon 	/*
107dd0b38b7SBoris Brezillon 	 * FIXME: pwm_apply_args() should be removed when switching to the
108dd0b38b7SBoris Brezillon 	 * atomic PWM API.
109dd0b38b7SBoris Brezillon 	 */
110dd0b38b7SBoris Brezillon 	pwm_apply_args(pwm);
111dd0b38b7SBoris Brezillon 	ret = pwm_config(pwm, (pargs.period + 1) >> 1, pargs.period);
1129a74ccdbSPhilipp Zabel 	if (ret < 0)
1139a74ccdbSPhilipp Zabel 		return ret;
1149a74ccdbSPhilipp Zabel 
1159a74ccdbSPhilipp Zabel 	clk_name = node->name;
1169a74ccdbSPhilipp Zabel 	of_property_read_string(node, "clock-output-names", &clk_name);
1179a74ccdbSPhilipp Zabel 
1189a74ccdbSPhilipp Zabel 	init.name = clk_name;
1199a74ccdbSPhilipp Zabel 	init.ops = &clk_pwm_ops;
12090b6c5c7SStephen Boyd 	init.flags = 0;
1219a74ccdbSPhilipp Zabel 	init.num_parents = 0;
1229a74ccdbSPhilipp Zabel 
1239a74ccdbSPhilipp Zabel 	clk_pwm->pwm = pwm;
1249a74ccdbSPhilipp Zabel 	clk_pwm->hw.init = &init;
1254cf915dfSStephen Boyd 	ret = devm_clk_hw_register(&pdev->dev, &clk_pwm->hw);
1264cf915dfSStephen Boyd 	if (ret)
1274cf915dfSStephen Boyd 		return ret;
1289a74ccdbSPhilipp Zabel 
1294cf915dfSStephen Boyd 	return of_clk_add_hw_provider(node, of_clk_hw_simple_get, &clk_pwm->hw);
1309a74ccdbSPhilipp Zabel }
1319a74ccdbSPhilipp Zabel 
clk_pwm_remove(struct platform_device * pdev)132*e72cdad5SUwe Kleine-König static void clk_pwm_remove(struct platform_device *pdev)
1339a74ccdbSPhilipp Zabel {
1349a74ccdbSPhilipp Zabel 	of_clk_del_provider(pdev->dev.of_node);
1359a74ccdbSPhilipp Zabel }
1369a74ccdbSPhilipp Zabel 
1379a74ccdbSPhilipp Zabel static const struct of_device_id clk_pwm_dt_ids[] = {
1389a74ccdbSPhilipp Zabel 	{ .compatible = "pwm-clock" },
1399a74ccdbSPhilipp Zabel 	{ }
1409a74ccdbSPhilipp Zabel };
1419a74ccdbSPhilipp Zabel MODULE_DEVICE_TABLE(of, clk_pwm_dt_ids);
1429a74ccdbSPhilipp Zabel 
1439a74ccdbSPhilipp Zabel static struct platform_driver clk_pwm_driver = {
1449a74ccdbSPhilipp Zabel 	.probe = clk_pwm_probe,
145*e72cdad5SUwe Kleine-König 	.remove_new = clk_pwm_remove,
1469a74ccdbSPhilipp Zabel 	.driver = {
1479a74ccdbSPhilipp Zabel 		.name = "pwm-clock",
148967069aaSKrzysztof Kozlowski 		.of_match_table = clk_pwm_dt_ids,
1499a74ccdbSPhilipp Zabel 	},
1509a74ccdbSPhilipp Zabel };
1519a74ccdbSPhilipp Zabel 
1529a74ccdbSPhilipp Zabel module_platform_driver(clk_pwm_driver);
1539a74ccdbSPhilipp Zabel 
1549a74ccdbSPhilipp Zabel MODULE_AUTHOR("Philipp Zabel <p.zabel@pengutronix.de>");
1559a74ccdbSPhilipp Zabel MODULE_DESCRIPTION("PWM clock driver");
1569a74ccdbSPhilipp Zabel MODULE_LICENSE("GPL");
157