18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2014 Free Electrons
48c2ecf20Sopenharmony_ci * Copyright (C) 2014 Atmel
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/clk.h>
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <linux/mfd/atmel-hlcdc.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/pwm.h>
158c2ecf20Sopenharmony_ci#include <linux/regmap.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define ATMEL_HLCDC_PWMCVAL_MASK	GENMASK(15, 8)
188c2ecf20Sopenharmony_ci#define ATMEL_HLCDC_PWMCVAL(x)		(((x) << 8) & ATMEL_HLCDC_PWMCVAL_MASK)
198c2ecf20Sopenharmony_ci#define ATMEL_HLCDC_PWMPOL		BIT(4)
208c2ecf20Sopenharmony_ci#define ATMEL_HLCDC_PWMPS_MASK		GENMASK(2, 0)
218c2ecf20Sopenharmony_ci#define ATMEL_HLCDC_PWMPS_MAX		0x6
228c2ecf20Sopenharmony_ci#define ATMEL_HLCDC_PWMPS(x)		((x) & ATMEL_HLCDC_PWMPS_MASK)
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistruct atmel_hlcdc_pwm_errata {
258c2ecf20Sopenharmony_ci	bool slow_clk_erratum;
268c2ecf20Sopenharmony_ci	bool div1_clk_erratum;
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct atmel_hlcdc_pwm {
308c2ecf20Sopenharmony_ci	struct pwm_chip chip;
318c2ecf20Sopenharmony_ci	struct atmel_hlcdc *hlcdc;
328c2ecf20Sopenharmony_ci	struct clk *cur_clk;
338c2ecf20Sopenharmony_ci	const struct atmel_hlcdc_pwm_errata *errata;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	return container_of(chip, struct atmel_hlcdc_pwm, chip);
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm,
428c2ecf20Sopenharmony_ci				 const struct pwm_state *state)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c);
458c2ecf20Sopenharmony_ci	struct atmel_hlcdc *hlcdc = chip->hlcdc;
468c2ecf20Sopenharmony_ci	unsigned int status;
478c2ecf20Sopenharmony_ci	int ret;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (state->enabled) {
508c2ecf20Sopenharmony_ci		struct clk *new_clk = hlcdc->slow_clk;
518c2ecf20Sopenharmony_ci		u64 pwmcval = state->duty_cycle * 256;
528c2ecf20Sopenharmony_ci		unsigned long clk_freq;
538c2ecf20Sopenharmony_ci		u64 clk_period_ns;
548c2ecf20Sopenharmony_ci		u32 pwmcfg;
558c2ecf20Sopenharmony_ci		int pres;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		if (!chip->errata || !chip->errata->slow_clk_erratum) {
588c2ecf20Sopenharmony_ci			clk_freq = clk_get_rate(new_clk);
598c2ecf20Sopenharmony_ci			if (!clk_freq)
608c2ecf20Sopenharmony_ci				return -EINVAL;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci			clk_period_ns = (u64)NSEC_PER_SEC * 256;
638c2ecf20Sopenharmony_ci			do_div(clk_period_ns, clk_freq);
648c2ecf20Sopenharmony_ci		}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		/* Errata: cannot use slow clk on some IP revisions */
678c2ecf20Sopenharmony_ci		if ((chip->errata && chip->errata->slow_clk_erratum) ||
688c2ecf20Sopenharmony_ci		    clk_period_ns > state->period) {
698c2ecf20Sopenharmony_ci			new_clk = hlcdc->sys_clk;
708c2ecf20Sopenharmony_ci			clk_freq = clk_get_rate(new_clk);
718c2ecf20Sopenharmony_ci			if (!clk_freq)
728c2ecf20Sopenharmony_ci				return -EINVAL;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci			clk_period_ns = (u64)NSEC_PER_SEC * 256;
758c2ecf20Sopenharmony_ci			do_div(clk_period_ns, clk_freq);
768c2ecf20Sopenharmony_ci		}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci		for (pres = 0; pres <= ATMEL_HLCDC_PWMPS_MAX; pres++) {
798c2ecf20Sopenharmony_ci		/* Errata: cannot divide by 1 on some IP revisions */
808c2ecf20Sopenharmony_ci			if (!pres && chip->errata &&
818c2ecf20Sopenharmony_ci			    chip->errata->div1_clk_erratum)
828c2ecf20Sopenharmony_ci				continue;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci			if ((clk_period_ns << pres) >= state->period)
858c2ecf20Sopenharmony_ci				break;
868c2ecf20Sopenharmony_ci		}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci		if (pres > ATMEL_HLCDC_PWMPS_MAX)
898c2ecf20Sopenharmony_ci			return -EINVAL;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci		pwmcfg = ATMEL_HLCDC_PWMPS(pres);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		if (new_clk != chip->cur_clk) {
948c2ecf20Sopenharmony_ci			u32 gencfg = 0;
958c2ecf20Sopenharmony_ci			int ret;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci			ret = clk_prepare_enable(new_clk);
988c2ecf20Sopenharmony_ci			if (ret)
998c2ecf20Sopenharmony_ci				return ret;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci			clk_disable_unprepare(chip->cur_clk);
1028c2ecf20Sopenharmony_ci			chip->cur_clk = new_clk;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci			if (new_clk == hlcdc->sys_clk)
1058c2ecf20Sopenharmony_ci				gencfg = ATMEL_HLCDC_CLKPWMSEL;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci			ret = regmap_update_bits(hlcdc->regmap,
1088c2ecf20Sopenharmony_ci						 ATMEL_HLCDC_CFG(0),
1098c2ecf20Sopenharmony_ci						 ATMEL_HLCDC_CLKPWMSEL,
1108c2ecf20Sopenharmony_ci						 gencfg);
1118c2ecf20Sopenharmony_ci			if (ret)
1128c2ecf20Sopenharmony_ci				return ret;
1138c2ecf20Sopenharmony_ci		}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci		do_div(pwmcval, state->period);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		/*
1188c2ecf20Sopenharmony_ci		 * The PWM duty cycle is configurable from 0/256 to 255/256 of
1198c2ecf20Sopenharmony_ci		 * the period cycle. Hence we can't set a duty cycle occupying
1208c2ecf20Sopenharmony_ci		 * the whole period cycle if we're asked to.
1218c2ecf20Sopenharmony_ci		 * Set it to 255 if pwmcval is greater than 256.
1228c2ecf20Sopenharmony_ci		 */
1238c2ecf20Sopenharmony_ci		if (pwmcval > 255)
1248c2ecf20Sopenharmony_ci			pwmcval = 255;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		pwmcfg |= ATMEL_HLCDC_PWMCVAL(pwmcval);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		if (state->polarity == PWM_POLARITY_NORMAL)
1298c2ecf20Sopenharmony_ci			pwmcfg |= ATMEL_HLCDC_PWMPOL;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci		ret = regmap_update_bits(hlcdc->regmap, ATMEL_HLCDC_CFG(6),
1328c2ecf20Sopenharmony_ci					 ATMEL_HLCDC_PWMCVAL_MASK |
1338c2ecf20Sopenharmony_ci					 ATMEL_HLCDC_PWMPS_MASK |
1348c2ecf20Sopenharmony_ci					 ATMEL_HLCDC_PWMPOL,
1358c2ecf20Sopenharmony_ci					 pwmcfg);
1368c2ecf20Sopenharmony_ci		if (ret)
1378c2ecf20Sopenharmony_ci			return ret;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_EN,
1408c2ecf20Sopenharmony_ci				   ATMEL_HLCDC_PWM);
1418c2ecf20Sopenharmony_ci		if (ret)
1428c2ecf20Sopenharmony_ci			return ret;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci		ret = regmap_read_poll_timeout(hlcdc->regmap, ATMEL_HLCDC_SR,
1458c2ecf20Sopenharmony_ci					       status,
1468c2ecf20Sopenharmony_ci					       status & ATMEL_HLCDC_PWM,
1478c2ecf20Sopenharmony_ci					       10, 0);
1488c2ecf20Sopenharmony_ci		if (ret)
1498c2ecf20Sopenharmony_ci			return ret;
1508c2ecf20Sopenharmony_ci	} else {
1518c2ecf20Sopenharmony_ci		ret = regmap_write(hlcdc->regmap, ATMEL_HLCDC_DIS,
1528c2ecf20Sopenharmony_ci				   ATMEL_HLCDC_PWM);
1538c2ecf20Sopenharmony_ci		if (ret)
1548c2ecf20Sopenharmony_ci			return ret;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		ret = regmap_read_poll_timeout(hlcdc->regmap, ATMEL_HLCDC_SR,
1578c2ecf20Sopenharmony_ci					       status,
1588c2ecf20Sopenharmony_ci					       !(status & ATMEL_HLCDC_PWM),
1598c2ecf20Sopenharmony_ci					       10, 0);
1608c2ecf20Sopenharmony_ci		if (ret)
1618c2ecf20Sopenharmony_ci			return ret;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci		clk_disable_unprepare(chip->cur_clk);
1648c2ecf20Sopenharmony_ci		chip->cur_clk = NULL;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic const struct pwm_ops atmel_hlcdc_pwm_ops = {
1718c2ecf20Sopenharmony_ci	.apply = atmel_hlcdc_pwm_apply,
1728c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_at91sam9x5_errata = {
1768c2ecf20Sopenharmony_ci	.slow_clk_erratum = true,
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic const struct atmel_hlcdc_pwm_errata atmel_hlcdc_pwm_sama5d3_errata = {
1808c2ecf20Sopenharmony_ci	.div1_clk_erratum = true,
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
1848c2ecf20Sopenharmony_cistatic int atmel_hlcdc_pwm_suspend(struct device *dev)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	struct atmel_hlcdc_pwm *chip = dev_get_drvdata(dev);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* Keep the periph clock enabled if the PWM is still running. */
1898c2ecf20Sopenharmony_ci	if (pwm_is_enabled(&chip->chip.pwms[0]))
1908c2ecf20Sopenharmony_ci		clk_disable_unprepare(chip->hlcdc->periph_clk);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic int atmel_hlcdc_pwm_resume(struct device *dev)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct atmel_hlcdc_pwm *chip = dev_get_drvdata(dev);
1988c2ecf20Sopenharmony_ci	struct pwm_state state;
1998c2ecf20Sopenharmony_ci	int ret;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	pwm_get_state(&chip->chip.pwms[0], &state);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/* Re-enable the periph clock it was stopped during suspend. */
2048c2ecf20Sopenharmony_ci	if (!state.enabled) {
2058c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(chip->hlcdc->periph_clk);
2068c2ecf20Sopenharmony_ci		if (ret)
2078c2ecf20Sopenharmony_ci			return ret;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return atmel_hlcdc_pwm_apply(&chip->chip, &chip->chip.pwms[0], &state);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci#endif
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(atmel_hlcdc_pwm_pm_ops,
2158c2ecf20Sopenharmony_ci			 atmel_hlcdc_pwm_suspend, atmel_hlcdc_pwm_resume);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_hlcdc_dt_ids[] = {
2188c2ecf20Sopenharmony_ci	{
2198c2ecf20Sopenharmony_ci		.compatible = "atmel,at91sam9n12-hlcdc",
2208c2ecf20Sopenharmony_ci		/* 9n12 has same errata as 9x5 HLCDC PWM */
2218c2ecf20Sopenharmony_ci		.data = &atmel_hlcdc_pwm_at91sam9x5_errata,
2228c2ecf20Sopenharmony_ci	},
2238c2ecf20Sopenharmony_ci	{
2248c2ecf20Sopenharmony_ci		.compatible = "atmel,at91sam9x5-hlcdc",
2258c2ecf20Sopenharmony_ci		.data = &atmel_hlcdc_pwm_at91sam9x5_errata,
2268c2ecf20Sopenharmony_ci	},
2278c2ecf20Sopenharmony_ci	{
2288c2ecf20Sopenharmony_ci		.compatible = "atmel,sama5d2-hlcdc",
2298c2ecf20Sopenharmony_ci	},
2308c2ecf20Sopenharmony_ci	{
2318c2ecf20Sopenharmony_ci		.compatible = "atmel,sama5d3-hlcdc",
2328c2ecf20Sopenharmony_ci		.data = &atmel_hlcdc_pwm_sama5d3_errata,
2338c2ecf20Sopenharmony_ci	},
2348c2ecf20Sopenharmony_ci	{
2358c2ecf20Sopenharmony_ci		.compatible = "atmel,sama5d4-hlcdc",
2368c2ecf20Sopenharmony_ci		.data = &atmel_hlcdc_pwm_sama5d3_errata,
2378c2ecf20Sopenharmony_ci	},
2388c2ecf20Sopenharmony_ci	{	.compatible = "microchip,sam9x60-hlcdc", },
2398c2ecf20Sopenharmony_ci	{ /* sentinel */ },
2408c2ecf20Sopenharmony_ci};
2418c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_hlcdc_dt_ids);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int atmel_hlcdc_pwm_probe(struct platform_device *pdev)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	const struct of_device_id *match;
2468c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
2478c2ecf20Sopenharmony_ci	struct atmel_hlcdc_pwm *chip;
2488c2ecf20Sopenharmony_ci	struct atmel_hlcdc *hlcdc;
2498c2ecf20Sopenharmony_ci	int ret;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	hlcdc = dev_get_drvdata(dev->parent);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
2548c2ecf20Sopenharmony_ci	if (!chip)
2558c2ecf20Sopenharmony_ci		return -ENOMEM;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(hlcdc->periph_clk);
2588c2ecf20Sopenharmony_ci	if (ret)
2598c2ecf20Sopenharmony_ci		return ret;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	match = of_match_node(atmel_hlcdc_dt_ids, dev->parent->of_node);
2628c2ecf20Sopenharmony_ci	if (match)
2638c2ecf20Sopenharmony_ci		chip->errata = match->data;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	chip->hlcdc = hlcdc;
2668c2ecf20Sopenharmony_ci	chip->chip.ops = &atmel_hlcdc_pwm_ops;
2678c2ecf20Sopenharmony_ci	chip->chip.dev = dev;
2688c2ecf20Sopenharmony_ci	chip->chip.base = -1;
2698c2ecf20Sopenharmony_ci	chip->chip.npwm = 1;
2708c2ecf20Sopenharmony_ci	chip->chip.of_xlate = of_pwm_xlate_with_flags;
2718c2ecf20Sopenharmony_ci	chip->chip.of_pwm_n_cells = 3;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	ret = pwmchip_add_with_polarity(&chip->chip, PWM_POLARITY_INVERSED);
2748c2ecf20Sopenharmony_ci	if (ret) {
2758c2ecf20Sopenharmony_ci		clk_disable_unprepare(hlcdc->periph_clk);
2768c2ecf20Sopenharmony_ci		return ret;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, chip);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return 0;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic int atmel_hlcdc_pwm_remove(struct platform_device *pdev)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct atmel_hlcdc_pwm *chip = platform_get_drvdata(pdev);
2878c2ecf20Sopenharmony_ci	int ret;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	ret = pwmchip_remove(&chip->chip);
2908c2ecf20Sopenharmony_ci	if (ret)
2918c2ecf20Sopenharmony_ci		return ret;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	clk_disable_unprepare(chip->hlcdc->periph_clk);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return 0;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_hlcdc_pwm_dt_ids[] = {
2998c2ecf20Sopenharmony_ci	{ .compatible = "atmel,hlcdc-pwm" },
3008c2ecf20Sopenharmony_ci	{ /* sentinel */ },
3018c2ecf20Sopenharmony_ci};
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic struct platform_driver atmel_hlcdc_pwm_driver = {
3048c2ecf20Sopenharmony_ci	.driver = {
3058c2ecf20Sopenharmony_ci		.name = "atmel-hlcdc-pwm",
3068c2ecf20Sopenharmony_ci		.of_match_table = atmel_hlcdc_pwm_dt_ids,
3078c2ecf20Sopenharmony_ci		.pm = &atmel_hlcdc_pwm_pm_ops,
3088c2ecf20Sopenharmony_ci	},
3098c2ecf20Sopenharmony_ci	.probe = atmel_hlcdc_pwm_probe,
3108c2ecf20Sopenharmony_ci	.remove = atmel_hlcdc_pwm_remove,
3118c2ecf20Sopenharmony_ci};
3128c2ecf20Sopenharmony_cimodule_platform_driver(atmel_hlcdc_pwm_driver);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:atmel-hlcdc-pwm");
3158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
3168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Atmel HLCDC PWM driver");
3178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
318