162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2012 Freescale Semiconductor, Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/clk.h>
762306a36Sopenharmony_ci#include <linux/err.h>
862306a36Sopenharmony_ci#include <linux/io.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/pwm.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/stmp_device.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define SET	0x4
1862306a36Sopenharmony_ci#define CLR	0x8
1962306a36Sopenharmony_ci#define TOG	0xc
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define PWM_CTRL		0x0
2262306a36Sopenharmony_ci#define PWM_ACTIVE0		0x10
2362306a36Sopenharmony_ci#define PWM_PERIOD0		0x20
2462306a36Sopenharmony_ci#define  PERIOD_PERIOD(p)	((p) & 0xffff)
2562306a36Sopenharmony_ci#define  PERIOD_PERIOD_MAX	0x10000
2662306a36Sopenharmony_ci#define  PERIOD_ACTIVE_HIGH	(3 << 16)
2762306a36Sopenharmony_ci#define  PERIOD_ACTIVE_LOW	(2 << 16)
2862306a36Sopenharmony_ci#define  PERIOD_INACTIVE_HIGH	(3 << 18)
2962306a36Sopenharmony_ci#define  PERIOD_INACTIVE_LOW	(2 << 18)
3062306a36Sopenharmony_ci#define  PERIOD_POLARITY_NORMAL	(PERIOD_ACTIVE_HIGH | PERIOD_INACTIVE_LOW)
3162306a36Sopenharmony_ci#define  PERIOD_POLARITY_INVERSE	(PERIOD_ACTIVE_LOW | PERIOD_INACTIVE_HIGH)
3262306a36Sopenharmony_ci#define  PERIOD_CDIV(div)	(((div) & 0x7) << 20)
3362306a36Sopenharmony_ci#define  PERIOD_CDIV_MAX	8
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic const u8 cdiv_shift[PERIOD_CDIV_MAX] = {
3662306a36Sopenharmony_ci	0, 1, 2, 3, 4, 6, 8, 10
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct mxs_pwm_chip {
4062306a36Sopenharmony_ci	struct pwm_chip chip;
4162306a36Sopenharmony_ci	struct clk *clk;
4262306a36Sopenharmony_ci	void __iomem *base;
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define to_mxs_pwm_chip(_chip) container_of(_chip, struct mxs_pwm_chip, chip)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int mxs_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
4862306a36Sopenharmony_ci			 const struct pwm_state *state)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct mxs_pwm_chip *mxs = to_mxs_pwm_chip(chip);
5162306a36Sopenharmony_ci	int ret, div = 0;
5262306a36Sopenharmony_ci	unsigned int period_cycles, duty_cycles;
5362306a36Sopenharmony_ci	unsigned long rate;
5462306a36Sopenharmony_ci	unsigned long long c;
5562306a36Sopenharmony_ci	unsigned int pol_bits;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/*
5862306a36Sopenharmony_ci	 * If the PWM channel is disabled, make sure to turn on the
5962306a36Sopenharmony_ci	 * clock before calling clk_get_rate() and writing to the
6062306a36Sopenharmony_ci	 * registers. Otherwise, just keep it enabled.
6162306a36Sopenharmony_ci	 */
6262306a36Sopenharmony_ci	if (!pwm_is_enabled(pwm)) {
6362306a36Sopenharmony_ci		ret = clk_prepare_enable(mxs->clk);
6462306a36Sopenharmony_ci		if (ret)
6562306a36Sopenharmony_ci			return ret;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (!state->enabled && pwm_is_enabled(pwm))
6962306a36Sopenharmony_ci		writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + CLR);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	rate = clk_get_rate(mxs->clk);
7262306a36Sopenharmony_ci	while (1) {
7362306a36Sopenharmony_ci		c = rate >> cdiv_shift[div];
7462306a36Sopenharmony_ci		c = c * state->period;
7562306a36Sopenharmony_ci		do_div(c, 1000000000);
7662306a36Sopenharmony_ci		if (c < PERIOD_PERIOD_MAX)
7762306a36Sopenharmony_ci			break;
7862306a36Sopenharmony_ci		div++;
7962306a36Sopenharmony_ci		if (div >= PERIOD_CDIV_MAX)
8062306a36Sopenharmony_ci			return -EINVAL;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	period_cycles = c;
8462306a36Sopenharmony_ci	c *= state->duty_cycle;
8562306a36Sopenharmony_ci	do_div(c, state->period);
8662306a36Sopenharmony_ci	duty_cycles = c;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/*
8962306a36Sopenharmony_ci	 * The data sheet the says registers must be written to in
9062306a36Sopenharmony_ci	 * this order (ACTIVEn, then PERIODn). Also, the new settings
9162306a36Sopenharmony_ci	 * only take effect at the beginning of a new period, avoiding
9262306a36Sopenharmony_ci	 * glitches.
9362306a36Sopenharmony_ci	 */
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	pol_bits = state->polarity == PWM_POLARITY_NORMAL ?
9662306a36Sopenharmony_ci		PERIOD_POLARITY_NORMAL : PERIOD_POLARITY_INVERSE;
9762306a36Sopenharmony_ci	writel(duty_cycles << 16,
9862306a36Sopenharmony_ci	       mxs->base + PWM_ACTIVE0 + pwm->hwpwm * 0x20);
9962306a36Sopenharmony_ci	writel(PERIOD_PERIOD(period_cycles) | pol_bits | PERIOD_CDIV(div),
10062306a36Sopenharmony_ci	       mxs->base + PWM_PERIOD0 + pwm->hwpwm * 0x20);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (state->enabled) {
10362306a36Sopenharmony_ci		if (!pwm_is_enabled(pwm)) {
10462306a36Sopenharmony_ci			/*
10562306a36Sopenharmony_ci			 * The clock was enabled above. Just enable
10662306a36Sopenharmony_ci			 * the channel in the control register.
10762306a36Sopenharmony_ci			 */
10862306a36Sopenharmony_ci			writel(1 << pwm->hwpwm, mxs->base + PWM_CTRL + SET);
10962306a36Sopenharmony_ci		}
11062306a36Sopenharmony_ci	} else {
11162306a36Sopenharmony_ci		clk_disable_unprepare(mxs->clk);
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic const struct pwm_ops mxs_pwm_ops = {
11762306a36Sopenharmony_ci	.apply = mxs_pwm_apply,
11862306a36Sopenharmony_ci	.owner = THIS_MODULE,
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int mxs_pwm_probe(struct platform_device *pdev)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
12462306a36Sopenharmony_ci	struct mxs_pwm_chip *mxs;
12562306a36Sopenharmony_ci	int ret;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL);
12862306a36Sopenharmony_ci	if (!mxs)
12962306a36Sopenharmony_ci		return -ENOMEM;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	mxs->base = devm_platform_ioremap_resource(pdev, 0);
13262306a36Sopenharmony_ci	if (IS_ERR(mxs->base))
13362306a36Sopenharmony_ci		return PTR_ERR(mxs->base);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	mxs->clk = devm_clk_get(&pdev->dev, NULL);
13662306a36Sopenharmony_ci	if (IS_ERR(mxs->clk))
13762306a36Sopenharmony_ci		return PTR_ERR(mxs->clk);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	mxs->chip.dev = &pdev->dev;
14062306a36Sopenharmony_ci	mxs->chip.ops = &mxs_pwm_ops;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm);
14362306a36Sopenharmony_ci	if (ret < 0) {
14462306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get pwm number: %d\n", ret);
14562306a36Sopenharmony_ci		return ret;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* FIXME: Only do this if the PWM isn't already running */
14962306a36Sopenharmony_ci	ret = stmp_reset_block(mxs->base);
15062306a36Sopenharmony_ci	if (ret)
15162306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, ret, "failed to reset PWM\n");
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	ret = devm_pwmchip_add(&pdev->dev, &mxs->chip);
15462306a36Sopenharmony_ci	if (ret < 0) {
15562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to add pwm chip %d\n", ret);
15662306a36Sopenharmony_ci		return ret;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	return 0;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic const struct of_device_id mxs_pwm_dt_ids[] = {
16362306a36Sopenharmony_ci	{ .compatible = "fsl,imx23-pwm", },
16462306a36Sopenharmony_ci	{ /* sentinel */ }
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mxs_pwm_dt_ids);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic struct platform_driver mxs_pwm_driver = {
16962306a36Sopenharmony_ci	.driver = {
17062306a36Sopenharmony_ci		.name = "mxs-pwm",
17162306a36Sopenharmony_ci		.of_match_table = mxs_pwm_dt_ids,
17262306a36Sopenharmony_ci	},
17362306a36Sopenharmony_ci	.probe = mxs_pwm_probe,
17462306a36Sopenharmony_ci};
17562306a36Sopenharmony_cimodule_platform_driver(mxs_pwm_driver);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ciMODULE_ALIAS("platform:mxs-pwm");
17862306a36Sopenharmony_ciMODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
17962306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale MXS PWM Driver");
18062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
181