xref: /openbmc/linux/drivers/pwm/pwm-vt8500.c (revision 22e1d1f4)
19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a245ccebSSascha Hauer /*
3261995ddSAxel Lin  * drivers/pwm/pwm-vt8500.c
4a245ccebSSascha Hauer  *
563e1ed23STony Prisk  * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
6a245ccebSSascha Hauer  * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
7a245ccebSSascha Hauer  */
8a245ccebSSascha Hauer 
9a245ccebSSascha Hauer #include <linux/module.h>
10a245ccebSSascha Hauer #include <linux/kernel.h>
11a245ccebSSascha Hauer #include <linux/platform_device.h>
12a245ccebSSascha Hauer #include <linux/slab.h>
13a245ccebSSascha Hauer #include <linux/err.h>
14a245ccebSSascha Hauer #include <linux/io.h>
15a245ccebSSascha Hauer #include <linux/pwm.h>
16a245ccebSSascha Hauer #include <linux/delay.h>
1763e1ed23STony Prisk #include <linux/clk.h>
18a245ccebSSascha Hauer 
19a245ccebSSascha Hauer #include <asm/div64.h>
20a245ccebSSascha Hauer 
2163e1ed23STony Prisk #include <linux/of.h>
2263e1ed23STony Prisk #include <linux/of_device.h>
2363e1ed23STony Prisk #include <linux/of_address.h>
2463e1ed23STony Prisk 
2563e1ed23STony Prisk /*
2663e1ed23STony Prisk  * SoC architecture allocates register space for 4 PWMs but only
2763e1ed23STony Prisk  * 2 are currently implemented.
2863e1ed23STony Prisk  */
2963e1ed23STony Prisk #define VT8500_NR_PWMS	2
30a245ccebSSascha Hauer 
318ab432caSTony Prisk #define REG_CTRL(pwm)		(((pwm) << 4) + 0x00)
328ab432caSTony Prisk #define REG_SCALAR(pwm)		(((pwm) << 4) + 0x04)
338ab432caSTony Prisk #define REG_PERIOD(pwm)		(((pwm) << 4) + 0x08)
348ab432caSTony Prisk #define REG_DUTY(pwm)		(((pwm) << 4) + 0x0C)
358ab432caSTony Prisk #define REG_STATUS		0x40
368ab432caSTony Prisk 
378ab432caSTony Prisk #define CTRL_ENABLE		BIT(0)
388ab432caSTony Prisk #define CTRL_INVERT		BIT(1)
398ab432caSTony Prisk #define CTRL_AUTOLOAD		BIT(2)
408ab432caSTony Prisk #define CTRL_STOP_IMM		BIT(3)
418ab432caSTony Prisk #define CTRL_LOAD_PRESCALE	BIT(4)
428ab432caSTony Prisk #define CTRL_LOAD_PERIOD	BIT(5)
438ab432caSTony Prisk 
448ab432caSTony Prisk #define STATUS_CTRL_UPDATE	BIT(0)
458ab432caSTony Prisk #define STATUS_SCALAR_UPDATE	BIT(1)
468ab432caSTony Prisk #define STATUS_PERIOD_UPDATE	BIT(2)
478ab432caSTony Prisk #define STATUS_DUTY_UPDATE	BIT(3)
488ab432caSTony Prisk #define STATUS_ALL_UPDATE	0x0F
498ab432caSTony Prisk 
50a245ccebSSascha Hauer struct vt8500_chip {
51a245ccebSSascha Hauer 	struct pwm_chip chip;
52a245ccebSSascha Hauer 	void __iomem *base;
5363e1ed23STony Prisk 	struct clk *clk;
54a245ccebSSascha Hauer };
55a245ccebSSascha Hauer 
56a245ccebSSascha Hauer #define to_vt8500_chip(chip)	container_of(chip, struct vt8500_chip, chip)
57a245ccebSSascha Hauer 
58a245ccebSSascha Hauer #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
59e9d866d5SUwe Kleine-König static inline void vt8500_pwm_busy_wait(struct vt8500_chip *vt8500, int nr, u8 bitmask)
60a245ccebSSascha Hauer {
61a245ccebSSascha Hauer 	int loops = msecs_to_loops(10);
628ab432caSTony Prisk 	u32 mask = bitmask << (nr << 8);
638ab432caSTony Prisk 
648ab432caSTony Prisk 	while ((readl(vt8500->base + REG_STATUS) & mask) && --loops)
65a245ccebSSascha Hauer 		cpu_relax();
66a245ccebSSascha Hauer 
67a245ccebSSascha Hauer 	if (unlikely(!loops))
688ab432caSTony Prisk 		dev_warn(vt8500->chip.dev, "Waiting for status bits 0x%x to clear timed out\n",
698ab432caSTony Prisk 			 mask);
70a245ccebSSascha Hauer }
71a245ccebSSascha Hauer 
72a245ccebSSascha Hauer static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
7314d89565SUwe Kleine-König 		u64 duty_ns, u64 period_ns)
74a245ccebSSascha Hauer {
75a245ccebSSascha Hauer 	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
76a245ccebSSascha Hauer 	unsigned long long c;
77a245ccebSSascha Hauer 	unsigned long period_cycles, prescale, pv, dc;
78422470a8STony Prisk 	int err;
798ab432caSTony Prisk 	u32 val;
80422470a8STony Prisk 
81422470a8STony Prisk 	err = clk_enable(vt8500->clk);
82422470a8STony Prisk 	if (err < 0) {
83422470a8STony Prisk 		dev_err(chip->dev, "failed to enable clock\n");
84422470a8STony Prisk 		return err;
85422470a8STony Prisk 	}
86a245ccebSSascha Hauer 
8763e1ed23STony Prisk 	c = clk_get_rate(vt8500->clk);
88a245ccebSSascha Hauer 	c = c * period_ns;
89a245ccebSSascha Hauer 	do_div(c, 1000000000);
90a245ccebSSascha Hauer 	period_cycles = c;
91a245ccebSSascha Hauer 
92a245ccebSSascha Hauer 	if (period_cycles < 1)
93a245ccebSSascha Hauer 		period_cycles = 1;
94a245ccebSSascha Hauer 	prescale = (period_cycles - 1) / 4096;
95a245ccebSSascha Hauer 	pv = period_cycles / (prescale + 1) - 1;
96a245ccebSSascha Hauer 	if (pv > 4095)
97a245ccebSSascha Hauer 		pv = 4095;
98a245ccebSSascha Hauer 
99422470a8STony Prisk 	if (prescale > 1023) {
100422470a8STony Prisk 		clk_disable(vt8500->clk);
101a245ccebSSascha Hauer 		return -EINVAL;
102422470a8STony Prisk 	}
103a245ccebSSascha Hauer 
104a245ccebSSascha Hauer 	c = (unsigned long long)pv * duty_ns;
10514d89565SUwe Kleine-König 
10614d89565SUwe Kleine-König 	dc = div64_u64(c, period_ns);
107a245ccebSSascha Hauer 
1088ab432caSTony Prisk 	writel(prescale, vt8500->base + REG_SCALAR(pwm->hwpwm));
109e9d866d5SUwe Kleine-König 	vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_SCALAR_UPDATE);
110a245ccebSSascha Hauer 
1118ab432caSTony Prisk 	writel(pv, vt8500->base + REG_PERIOD(pwm->hwpwm));
112e9d866d5SUwe Kleine-König 	vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_PERIOD_UPDATE);
113a245ccebSSascha Hauer 
1148ab432caSTony Prisk 	writel(dc, vt8500->base + REG_DUTY(pwm->hwpwm));
115e9d866d5SUwe Kleine-König 	vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_DUTY_UPDATE);
1168ab432caSTony Prisk 
1178ab432caSTony Prisk 	val = readl(vt8500->base + REG_CTRL(pwm->hwpwm));
1188ab432caSTony Prisk 	val |= CTRL_AUTOLOAD;
1198ab432caSTony Prisk 	writel(val, vt8500->base + REG_CTRL(pwm->hwpwm));
120e9d866d5SUwe Kleine-König 	vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE);
121a245ccebSSascha Hauer 
122422470a8STony Prisk 	clk_disable(vt8500->clk);
123a245ccebSSascha Hauer 	return 0;
124a245ccebSSascha Hauer }
125a245ccebSSascha Hauer 
126a245ccebSSascha Hauer static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
127a245ccebSSascha Hauer {
128a245ccebSSascha Hauer 	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
1298ab432caSTony Prisk 	int err;
1308ab432caSTony Prisk 	u32 val;
131a245ccebSSascha Hauer 
13263e1ed23STony Prisk 	err = clk_enable(vt8500->clk);
1332f9569f7STony Prisk 	if (err < 0) {
13463e1ed23STony Prisk 		dev_err(chip->dev, "failed to enable clock\n");
13563e1ed23STony Prisk 		return err;
136422470a8STony Prisk 	}
13763e1ed23STony Prisk 
1388ab432caSTony Prisk 	val = readl(vt8500->base + REG_CTRL(pwm->hwpwm));
1398ab432caSTony Prisk 	val |= CTRL_ENABLE;
1408ab432caSTony Prisk 	writel(val, vt8500->base + REG_CTRL(pwm->hwpwm));
141e9d866d5SUwe Kleine-König 	vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE);
1428ab432caSTony Prisk 
143a245ccebSSascha Hauer 	return 0;
144a245ccebSSascha Hauer }
145a245ccebSSascha Hauer 
146a245ccebSSascha Hauer static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
147a245ccebSSascha Hauer {
148a245ccebSSascha Hauer 	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
1498ab432caSTony Prisk 	u32 val;
150a245ccebSSascha Hauer 
1518ab432caSTony Prisk 	val = readl(vt8500->base + REG_CTRL(pwm->hwpwm));
1528ab432caSTony Prisk 	val &= ~CTRL_ENABLE;
1538ab432caSTony Prisk 	writel(val, vt8500->base + REG_CTRL(pwm->hwpwm));
154e9d866d5SUwe Kleine-König 	vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE);
15563e1ed23STony Prisk 
15663e1ed23STony Prisk 	clk_disable(vt8500->clk);
157a245ccebSSascha Hauer }
158a245ccebSSascha Hauer 
1593ccb1c17STony Prisk static int vt8500_pwm_set_polarity(struct pwm_chip *chip,
1603ccb1c17STony Prisk 				   struct pwm_device *pwm,
1613ccb1c17STony Prisk 				   enum pwm_polarity polarity)
1623ccb1c17STony Prisk {
1633ccb1c17STony Prisk 	struct vt8500_chip *vt8500 = to_vt8500_chip(chip);
1643ccb1c17STony Prisk 	u32 val;
1653ccb1c17STony Prisk 
1663ccb1c17STony Prisk 	val = readl(vt8500->base + REG_CTRL(pwm->hwpwm));
1673ccb1c17STony Prisk 
1683ccb1c17STony Prisk 	if (polarity == PWM_POLARITY_INVERSED)
1693ccb1c17STony Prisk 		val |= CTRL_INVERT;
1703ccb1c17STony Prisk 	else
1713ccb1c17STony Prisk 		val &= ~CTRL_INVERT;
1723ccb1c17STony Prisk 
1733ccb1c17STony Prisk 	writel(val, vt8500->base + REG_CTRL(pwm->hwpwm));
174e9d866d5SUwe Kleine-König 	vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE);
1753ccb1c17STony Prisk 
1763ccb1c17STony Prisk 	return 0;
1773ccb1c17STony Prisk }
1783ccb1c17STony Prisk 
17914d89565SUwe Kleine-König static int vt8500_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
18014d89565SUwe Kleine-König 			    const struct pwm_state *state)
18114d89565SUwe Kleine-König {
18214d89565SUwe Kleine-König 	int err;
18314d89565SUwe Kleine-König 	bool enabled = pwm->state.enabled;
18414d89565SUwe Kleine-König 
18514d89565SUwe Kleine-König 	if (state->polarity != pwm->state.polarity) {
18614d89565SUwe Kleine-König 		/*
18714d89565SUwe Kleine-König 		 * Changing the polarity of a running PWM is only allowed when
18814d89565SUwe Kleine-König 		 * the PWM driver implements ->apply().
18914d89565SUwe Kleine-König 		 */
19014d89565SUwe Kleine-König 		if (enabled) {
19114d89565SUwe Kleine-König 			vt8500_pwm_disable(chip, pwm);
19214d89565SUwe Kleine-König 
19314d89565SUwe Kleine-König 			enabled = false;
19414d89565SUwe Kleine-König 		}
19514d89565SUwe Kleine-König 
19614d89565SUwe Kleine-König 		err = vt8500_pwm_set_polarity(chip, pwm, state->polarity);
19714d89565SUwe Kleine-König 		if (err)
19814d89565SUwe Kleine-König 			return err;
19914d89565SUwe Kleine-König 	}
20014d89565SUwe Kleine-König 
20114d89565SUwe Kleine-König 	if (!state->enabled) {
20214d89565SUwe Kleine-König 		if (enabled)
20314d89565SUwe Kleine-König 			vt8500_pwm_disable(chip, pwm);
20414d89565SUwe Kleine-König 
20514d89565SUwe Kleine-König 		return 0;
20614d89565SUwe Kleine-König 	}
20714d89565SUwe Kleine-König 
20814d89565SUwe Kleine-König 	/*
20914d89565SUwe Kleine-König 	 * We cannot skip calling ->config even if state->period ==
21014d89565SUwe Kleine-König 	 * pwm->state.period && state->duty_cycle == pwm->state.duty_cycle
21114d89565SUwe Kleine-König 	 * because we might have exited early in the last call to
21214d89565SUwe Kleine-König 	 * pwm_apply_state because of !state->enabled and so the two values in
21314d89565SUwe Kleine-König 	 * pwm->state might not be configured in hardware.
21414d89565SUwe Kleine-König 	 */
21514d89565SUwe Kleine-König 	err = vt8500_pwm_config(pwm->chip, pwm, state->duty_cycle, state->period);
21614d89565SUwe Kleine-König 	if (err)
21714d89565SUwe Kleine-König 		return err;
21814d89565SUwe Kleine-König 
21914d89565SUwe Kleine-König 	if (!enabled)
22014d89565SUwe Kleine-König 		err = vt8500_pwm_enable(chip, pwm);
22114d89565SUwe Kleine-König 
22214d89565SUwe Kleine-König 	return err;
22314d89565SUwe Kleine-König }
22414d89565SUwe Kleine-König 
225b2ec9efcSBhumika Goyal static const struct pwm_ops vt8500_pwm_ops = {
22614d89565SUwe Kleine-König 	.apply = vt8500_pwm_apply,
227a245ccebSSascha Hauer 	.owner = THIS_MODULE,
228a245ccebSSascha Hauer };
229a245ccebSSascha Hauer 
23063e1ed23STony Prisk static const struct of_device_id vt8500_pwm_dt_ids[] = {
23163e1ed23STony Prisk 	{ .compatible = "via,vt8500-pwm", },
23263e1ed23STony Prisk 	{ /* Sentinel */ }
23363e1ed23STony Prisk };
23463e1ed23STony Prisk MODULE_DEVICE_TABLE(of, vt8500_pwm_dt_ids);
23563e1ed23STony Prisk 
23663e1ed23STony Prisk static int vt8500_pwm_probe(struct platform_device *pdev)
237a245ccebSSascha Hauer {
238635d324eSzhaoxiao 	struct vt8500_chip *vt8500;
23963e1ed23STony Prisk 	struct device_node *np = pdev->dev.of_node;
240a245ccebSSascha Hauer 	int ret;
241a245ccebSSascha Hauer 
24263e1ed23STony Prisk 	if (!np) {
24363e1ed23STony Prisk 		dev_err(&pdev->dev, "invalid devicetree node\n");
24463e1ed23STony Prisk 		return -EINVAL;
24563e1ed23STony Prisk 	}
24663e1ed23STony Prisk 
247635d324eSzhaoxiao 	vt8500 = devm_kzalloc(&pdev->dev, sizeof(*vt8500), GFP_KERNEL);
248635d324eSzhaoxiao 	if (vt8500 == NULL)
249a245ccebSSascha Hauer 		return -ENOMEM;
250a245ccebSSascha Hauer 
251635d324eSzhaoxiao 	vt8500->chip.dev = &pdev->dev;
252635d324eSzhaoxiao 	vt8500->chip.ops = &vt8500_pwm_ops;
253635d324eSzhaoxiao 	vt8500->chip.npwm = VT8500_NR_PWMS;
254a245ccebSSascha Hauer 
255635d324eSzhaoxiao 	vt8500->clk = devm_clk_get(&pdev->dev, NULL);
256635d324eSzhaoxiao 	if (IS_ERR(vt8500->clk)) {
25763e1ed23STony Prisk 		dev_err(&pdev->dev, "clock source not specified\n");
258635d324eSzhaoxiao 		return PTR_ERR(vt8500->clk);
25963e1ed23STony Prisk 	}
26063e1ed23STony Prisk 
261635d324eSzhaoxiao 	vt8500->base = devm_platform_ioremap_resource(pdev, 0);
262635d324eSzhaoxiao 	if (IS_ERR(vt8500->base))
263635d324eSzhaoxiao 		return PTR_ERR(vt8500->base);
264a245ccebSSascha Hauer 
265635d324eSzhaoxiao 	ret = clk_prepare(vt8500->clk);
26663e1ed23STony Prisk 	if (ret < 0) {
26763e1ed23STony Prisk 		dev_err(&pdev->dev, "failed to prepare clock\n");
268a245ccebSSascha Hauer 		return ret;
26963e1ed23STony Prisk 	}
27063e1ed23STony Prisk 
271635d324eSzhaoxiao 	ret = pwmchip_add(&vt8500->chip);
27263e1ed23STony Prisk 	if (ret < 0) {
27363e1ed23STony Prisk 		dev_err(&pdev->dev, "failed to add PWM chip\n");
274635d324eSzhaoxiao 		clk_unprepare(vt8500->clk);
27563e1ed23STony Prisk 		return ret;
27663e1ed23STony Prisk 	}
277a245ccebSSascha Hauer 
278635d324eSzhaoxiao 	platform_set_drvdata(pdev, vt8500);
279a245ccebSSascha Hauer 	return ret;
280a245ccebSSascha Hauer }
281a245ccebSSascha Hauer 
282*22e1d1f4SUwe Kleine-König static void vt8500_pwm_remove(struct platform_device *pdev)
283a245ccebSSascha Hauer {
284635d324eSzhaoxiao 	struct vt8500_chip *vt8500 = platform_get_drvdata(pdev);
285a245ccebSSascha Hauer 
286635d324eSzhaoxiao 	pwmchip_remove(&vt8500->chip);
287a245ccebSSascha Hauer 
288635d324eSzhaoxiao 	clk_unprepare(vt8500->clk);
289a245ccebSSascha Hauer }
290a245ccebSSascha Hauer 
29163e1ed23STony Prisk static struct platform_driver vt8500_pwm_driver = {
29263e1ed23STony Prisk 	.probe		= vt8500_pwm_probe,
293*22e1d1f4SUwe Kleine-König 	.remove_new	= vt8500_pwm_remove,
294a245ccebSSascha Hauer 	.driver		= {
295a245ccebSSascha Hauer 		.name	= "vt8500-pwm",
29663e1ed23STony Prisk 		.of_match_table = vt8500_pwm_dt_ids,
297a245ccebSSascha Hauer 	},
298a245ccebSSascha Hauer };
29963e1ed23STony Prisk module_platform_driver(vt8500_pwm_driver);
300a245ccebSSascha Hauer 
30163e1ed23STony Prisk MODULE_DESCRIPTION("VT8500 PWM Driver");
30263e1ed23STony Prisk MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
30363e1ed23STony Prisk MODULE_LICENSE("GPL v2");
304