xref: /kernel/linux/linux-5.10/drivers/pwm/pwm-zx.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Sanechips Technology Co., Ltd.
48c2ecf20Sopenharmony_ci * Copyright 2017 Linaro Ltd.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/clk.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/io.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/pwm.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define ZX_PWM_MODE		0x0
178c2ecf20Sopenharmony_ci#define ZX_PWM_CLKDIV_SHIFT	2
188c2ecf20Sopenharmony_ci#define ZX_PWM_CLKDIV_MASK	GENMASK(11, 2)
198c2ecf20Sopenharmony_ci#define ZX_PWM_CLKDIV(x)	(((x) << ZX_PWM_CLKDIV_SHIFT) & \
208c2ecf20Sopenharmony_ci					 ZX_PWM_CLKDIV_MASK)
218c2ecf20Sopenharmony_ci#define ZX_PWM_POLAR		BIT(1)
228c2ecf20Sopenharmony_ci#define ZX_PWM_EN		BIT(0)
238c2ecf20Sopenharmony_ci#define ZX_PWM_PERIOD		0x4
248c2ecf20Sopenharmony_ci#define ZX_PWM_DUTY		0x8
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define ZX_PWM_CLKDIV_MAX	1023
278c2ecf20Sopenharmony_ci#define ZX_PWM_PERIOD_MAX	65535
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct zx_pwm_chip {
308c2ecf20Sopenharmony_ci	struct pwm_chip chip;
318c2ecf20Sopenharmony_ci	struct clk *pclk;
328c2ecf20Sopenharmony_ci	struct clk *wclk;
338c2ecf20Sopenharmony_ci	void __iomem *base;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic inline struct zx_pwm_chip *to_zx_pwm_chip(struct pwm_chip *chip)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	return container_of(chip, struct zx_pwm_chip, chip);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic inline u32 zx_pwm_readl(struct zx_pwm_chip *zpc, unsigned int hwpwm,
428c2ecf20Sopenharmony_ci			       unsigned int offset)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	return readl(zpc->base + (hwpwm + 1) * 0x10 + offset);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic inline void zx_pwm_writel(struct zx_pwm_chip *zpc, unsigned int hwpwm,
488c2ecf20Sopenharmony_ci				 unsigned int offset, u32 value)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	writel(value, zpc->base + (hwpwm + 1) * 0x10 + offset);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void zx_pwm_set_mask(struct zx_pwm_chip *zpc, unsigned int hwpwm,
548c2ecf20Sopenharmony_ci			    unsigned int offset, u32 mask, u32 value)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	u32 data;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	data = zx_pwm_readl(zpc, hwpwm, offset);
598c2ecf20Sopenharmony_ci	data &= ~mask;
608c2ecf20Sopenharmony_ci	data |= value & mask;
618c2ecf20Sopenharmony_ci	zx_pwm_writel(zpc, hwpwm, offset, data);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic void zx_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
658c2ecf20Sopenharmony_ci			     struct pwm_state *state)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip);
688c2ecf20Sopenharmony_ci	unsigned long rate;
698c2ecf20Sopenharmony_ci	unsigned int div;
708c2ecf20Sopenharmony_ci	u32 value;
718c2ecf20Sopenharmony_ci	u64 tmp;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	value = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_MODE);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (value & ZX_PWM_POLAR)
768c2ecf20Sopenharmony_ci		state->polarity = PWM_POLARITY_NORMAL;
778c2ecf20Sopenharmony_ci	else
788c2ecf20Sopenharmony_ci		state->polarity = PWM_POLARITY_INVERSED;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (value & ZX_PWM_EN)
818c2ecf20Sopenharmony_ci		state->enabled = true;
828c2ecf20Sopenharmony_ci	else
838c2ecf20Sopenharmony_ci		state->enabled = false;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	div = (value & ZX_PWM_CLKDIV_MASK) >> ZX_PWM_CLKDIV_SHIFT;
868c2ecf20Sopenharmony_ci	rate = clk_get_rate(zpc->wclk);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	tmp = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_PERIOD);
898c2ecf20Sopenharmony_ci	tmp *= div * NSEC_PER_SEC;
908c2ecf20Sopenharmony_ci	state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	tmp = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_DUTY);
938c2ecf20Sopenharmony_ci	tmp *= div * NSEC_PER_SEC;
948c2ecf20Sopenharmony_ci	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int zx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
988c2ecf20Sopenharmony_ci			 unsigned int duty_ns, unsigned int period_ns)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip);
1018c2ecf20Sopenharmony_ci	unsigned int period_cycles, duty_cycles;
1028c2ecf20Sopenharmony_ci	unsigned long long c;
1038c2ecf20Sopenharmony_ci	unsigned int div = 1;
1048c2ecf20Sopenharmony_ci	unsigned long rate;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Find out the best divider */
1078c2ecf20Sopenharmony_ci	rate = clk_get_rate(zpc->wclk);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	while (1) {
1108c2ecf20Sopenharmony_ci		c = rate / div;
1118c2ecf20Sopenharmony_ci		c = c * period_ns;
1128c2ecf20Sopenharmony_ci		do_div(c, NSEC_PER_SEC);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		if (c < ZX_PWM_PERIOD_MAX)
1158c2ecf20Sopenharmony_ci			break;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		div++;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		if (div > ZX_PWM_CLKDIV_MAX)
1208c2ecf20Sopenharmony_ci			return -ERANGE;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* Calculate duty cycles */
1248c2ecf20Sopenharmony_ci	period_cycles = c;
1258c2ecf20Sopenharmony_ci	c *= duty_ns;
1268c2ecf20Sopenharmony_ci	do_div(c, period_ns);
1278c2ecf20Sopenharmony_ci	duty_cycles = c;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	/*
1308c2ecf20Sopenharmony_ci	 * If the PWM is being enabled, we have to temporarily disable it
1318c2ecf20Sopenharmony_ci	 * before configuring the registers.
1328c2ecf20Sopenharmony_ci	 */
1338c2ecf20Sopenharmony_ci	if (pwm_is_enabled(pwm))
1348c2ecf20Sopenharmony_ci		zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_EN, 0);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	/* Set up registers */
1378c2ecf20Sopenharmony_ci	zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_CLKDIV_MASK,
1388c2ecf20Sopenharmony_ci			ZX_PWM_CLKDIV(div));
1398c2ecf20Sopenharmony_ci	zx_pwm_writel(zpc, pwm->hwpwm, ZX_PWM_PERIOD, period_cycles);
1408c2ecf20Sopenharmony_ci	zx_pwm_writel(zpc, pwm->hwpwm, ZX_PWM_DUTY, duty_cycles);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/* Re-enable the PWM if needed */
1438c2ecf20Sopenharmony_ci	if (pwm_is_enabled(pwm))
1448c2ecf20Sopenharmony_ci		zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE,
1458c2ecf20Sopenharmony_ci				ZX_PWM_EN, ZX_PWM_EN);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int zx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
1518c2ecf20Sopenharmony_ci			const struct pwm_state *state)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip);
1548c2ecf20Sopenharmony_ci	struct pwm_state cstate;
1558c2ecf20Sopenharmony_ci	int ret;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	pwm_get_state(pwm, &cstate);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (state->polarity != cstate.polarity)
1608c2ecf20Sopenharmony_ci		zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_POLAR,
1618c2ecf20Sopenharmony_ci				(state->polarity == PWM_POLARITY_INVERSED) ?
1628c2ecf20Sopenharmony_ci				 0 : ZX_PWM_POLAR);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (state->period != cstate.period ||
1658c2ecf20Sopenharmony_ci	    state->duty_cycle != cstate.duty_cycle) {
1668c2ecf20Sopenharmony_ci		ret = zx_pwm_config(chip, pwm, state->duty_cycle,
1678c2ecf20Sopenharmony_ci				    state->period);
1688c2ecf20Sopenharmony_ci		if (ret)
1698c2ecf20Sopenharmony_ci			return ret;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (state->enabled != cstate.enabled) {
1738c2ecf20Sopenharmony_ci		if (state->enabled) {
1748c2ecf20Sopenharmony_ci			ret = clk_prepare_enable(zpc->wclk);
1758c2ecf20Sopenharmony_ci			if (ret)
1768c2ecf20Sopenharmony_ci				return ret;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci			zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE,
1798c2ecf20Sopenharmony_ci					ZX_PWM_EN, ZX_PWM_EN);
1808c2ecf20Sopenharmony_ci		} else {
1818c2ecf20Sopenharmony_ci			zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE,
1828c2ecf20Sopenharmony_ci					ZX_PWM_EN, 0);
1838c2ecf20Sopenharmony_ci			clk_disable_unprepare(zpc->wclk);
1848c2ecf20Sopenharmony_ci		}
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic const struct pwm_ops zx_pwm_ops = {
1918c2ecf20Sopenharmony_ci	.apply = zx_pwm_apply,
1928c2ecf20Sopenharmony_ci	.get_state = zx_pwm_get_state,
1938c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic int zx_pwm_probe(struct platform_device *pdev)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct zx_pwm_chip *zpc;
1998c2ecf20Sopenharmony_ci	struct resource *res;
2008c2ecf20Sopenharmony_ci	unsigned int i;
2018c2ecf20Sopenharmony_ci	int ret;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	zpc = devm_kzalloc(&pdev->dev, sizeof(*zpc), GFP_KERNEL);
2048c2ecf20Sopenharmony_ci	if (!zpc)
2058c2ecf20Sopenharmony_ci		return -ENOMEM;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2088c2ecf20Sopenharmony_ci	zpc->base = devm_ioremap_resource(&pdev->dev, res);
2098c2ecf20Sopenharmony_ci	if (IS_ERR(zpc->base))
2108c2ecf20Sopenharmony_ci		return PTR_ERR(zpc->base);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	zpc->pclk = devm_clk_get(&pdev->dev, "pclk");
2138c2ecf20Sopenharmony_ci	if (IS_ERR(zpc->pclk))
2148c2ecf20Sopenharmony_ci		return PTR_ERR(zpc->pclk);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	zpc->wclk = devm_clk_get(&pdev->dev, "wclk");
2178c2ecf20Sopenharmony_ci	if (IS_ERR(zpc->wclk))
2188c2ecf20Sopenharmony_ci		return PTR_ERR(zpc->wclk);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(zpc->pclk);
2218c2ecf20Sopenharmony_ci	if (ret)
2228c2ecf20Sopenharmony_ci		return ret;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	zpc->chip.dev = &pdev->dev;
2258c2ecf20Sopenharmony_ci	zpc->chip.ops = &zx_pwm_ops;
2268c2ecf20Sopenharmony_ci	zpc->chip.base = -1;
2278c2ecf20Sopenharmony_ci	zpc->chip.npwm = 4;
2288c2ecf20Sopenharmony_ci	zpc->chip.of_xlate = of_pwm_xlate_with_flags;
2298c2ecf20Sopenharmony_ci	zpc->chip.of_pwm_n_cells = 3;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/*
2328c2ecf20Sopenharmony_ci	 * PWM devices may be enabled by firmware, and let's disable all of
2338c2ecf20Sopenharmony_ci	 * them initially to save power.
2348c2ecf20Sopenharmony_ci	 */
2358c2ecf20Sopenharmony_ci	for (i = 0; i < zpc->chip.npwm; i++)
2368c2ecf20Sopenharmony_ci		zx_pwm_set_mask(zpc, i, ZX_PWM_MODE, ZX_PWM_EN, 0);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	ret = pwmchip_add(&zpc->chip);
2398c2ecf20Sopenharmony_ci	if (ret < 0) {
2408c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret);
2418c2ecf20Sopenharmony_ci		clk_disable_unprepare(zpc->pclk);
2428c2ecf20Sopenharmony_ci		return ret;
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, zpc);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return 0;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int zx_pwm_remove(struct platform_device *pdev)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct zx_pwm_chip *zpc = platform_get_drvdata(pdev);
2538c2ecf20Sopenharmony_ci	int ret;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	ret = pwmchip_remove(&zpc->chip);
2568c2ecf20Sopenharmony_ci	clk_disable_unprepare(zpc->pclk);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	return ret;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic const struct of_device_id zx_pwm_dt_ids[] = {
2628c2ecf20Sopenharmony_ci	{ .compatible = "zte,zx296718-pwm", },
2638c2ecf20Sopenharmony_ci	{ /* sentinel */ }
2648c2ecf20Sopenharmony_ci};
2658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, zx_pwm_dt_ids);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic struct platform_driver zx_pwm_driver = {
2688c2ecf20Sopenharmony_ci	.driver = {
2698c2ecf20Sopenharmony_ci		.name = "zx-pwm",
2708c2ecf20Sopenharmony_ci		.of_match_table = zx_pwm_dt_ids,
2718c2ecf20Sopenharmony_ci	},
2728c2ecf20Sopenharmony_ci	.probe = zx_pwm_probe,
2738c2ecf20Sopenharmony_ci	.remove = zx_pwm_remove,
2748c2ecf20Sopenharmony_ci};
2758c2ecf20Sopenharmony_cimodule_platform_driver(zx_pwm_driver);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:zx-pwm");
2788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
2798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ZTE ZX PWM Driver");
2808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
281