167761Smsmith// SPDX-License-Identifier: GPL-2.0-only
267761Smsmith/*
367761Smsmith * PWM driver for Rockchip SoCs
467761Smsmith *
567761Smsmith * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
667761Smsmith * Copyright (C) 2014 ROCKCHIP, Inc.
767761Smsmith */
867761Smsmith
967761Smsmith#include <linux/clk.h>
1067761Smsmith#include <linux/io.h>
1167761Smsmith#include <linux/module.h>
1267761Smsmith#include <linux/of.h>
1367761Smsmith#include <linux/platform_device.h>
1467761Smsmith#include <linux/property.h>
1567761Smsmith#include <linux/pwm.h>
1667761Smsmith#include <linux/time.h>
1767761Smsmith
1867761Smsmith#define PWM_CTRL_TIMER_EN	(1 << 0)
1967761Smsmith#define PWM_CTRL_OUTPUT_EN	(1 << 3)
2067761Smsmith
2167761Smsmith#define PWM_ENABLE		(1 << 0)
2267761Smsmith#define PWM_CONTINUOUS		(1 << 1)
2367761Smsmith#define PWM_DUTY_POSITIVE	(1 << 3)
2467761Smsmith#define PWM_DUTY_NEGATIVE	(0 << 3)
2567761Smsmith#define PWM_INACTIVE_NEGATIVE	(0 << 4)
2667761Smsmith#define PWM_INACTIVE_POSITIVE	(1 << 4)
2767761Smsmith#define PWM_POLARITY_MASK	(PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE)
2867761Smsmith#define PWM_OUTPUT_LEFT		(0 << 5)
2967761Smsmith#define PWM_LOCK_EN		(1 << 6)
3067761Smsmith#define PWM_LP_DISABLE		(0 << 8)
3167761Smsmith
3267761Smsmithstruct rockchip_pwm_chip {
3367761Smsmith	struct clk *clk;
3467761Smsmith	struct clk *pclk;
3567761Smsmith	const struct rockchip_pwm_data *data;
3667761Smsmith	void __iomem *base;
3767761Smsmith};
3867761Smsmith
3967761Smsmithstruct rockchip_pwm_regs {
4067761Smsmith	unsigned long duty;
4167761Smsmith	unsigned long period;
4269744Smsmith	unsigned long cntr;
4369744Smsmith	unsigned long ctrl;
4478993Smsmith};
4569744Smsmith
4669744Smsmithstruct rockchip_pwm_data {
4769744Smsmith	struct rockchip_pwm_regs regs;
4867761Smsmith	unsigned int prescaler;
4967761Smsmith	bool supports_polarity;
5067761Smsmith	bool supports_lock;
5167761Smsmith	u32 enable_conf;
5267761Smsmith};
5367761Smsmith
5467761Smsmithstatic inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *chip)
5567761Smsmith{
5667761Smsmith	return pwmchip_get_drvdata(chip);
5777432Smsmith}
5867761Smsmith
5967761Smsmithstatic int rockchip_pwm_get_state(struct pwm_chip *chip,
6067761Smsmith				  struct pwm_device *pwm,
6167761Smsmith				  struct pwm_state *state)
6267761Smsmith{
6377432Smsmith	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
6469744Smsmith	u32 enable_conf = pc->data->enable_conf;
6567761Smsmith	unsigned long clk_rate;
6667761Smsmith	u64 tmp;
6767761Smsmith	u32 val;
6867761Smsmith	int ret;
6967761Smsmith
7080078Smsmith	ret = clk_enable(pc->pclk);
7169744Smsmith	if (ret)
7267761Smsmith		return ret;
7382372Smsmith
7467761Smsmith	ret = clk_enable(pc->clk);
7567761Smsmith	if (ret)
7667761Smsmith		return ret;
7767761Smsmith
7867761Smsmith	clk_rate = clk_get_rate(pc->clk);
7967761Smsmith
8067761Smsmith	tmp = readl_relaxed(pc->base + pc->data->regs.period);
8167761Smsmith	tmp *= pc->data->prescaler * NSEC_PER_SEC;
8277432Smsmith	state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
8367761Smsmith
8467761Smsmith	tmp = readl_relaxed(pc->base + pc->data->regs.duty);
8567761Smsmith	tmp *= pc->data->prescaler * NSEC_PER_SEC;
8667761Smsmith	state->duty_cycle =  DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
8767761Smsmith
8867761Smsmith	val = readl_relaxed(pc->base + pc->data->regs.ctrl);
8977432Smsmith	state->enabled = (val & enable_conf) == enable_conf;
9082372Smsmith
9167761Smsmith	if (pc->data->supports_polarity && !(val & PWM_DUTY_POSITIVE))
9267761Smsmith		state->polarity = PWM_POLARITY_INVERSED;
9367761Smsmith	else
9477432Smsmith		state->polarity = PWM_POLARITY_NORMAL;
9582372Smsmith
9667761Smsmith	clk_disable(pc->clk);
9767761Smsmith	clk_disable(pc->pclk);
9867761Smsmith
9977432Smsmith	return 0;
10067761Smsmith}
10182372Smsmith
10267761Smsmithstatic void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
10367761Smsmith			       const struct pwm_state *state)
10482372Smsmith{
10582372Smsmith	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
10667761Smsmith	unsigned long period, duty;
10767761Smsmith	u64 clk_rate, div;
10867761Smsmith	u32 ctrl;
10967761Smsmith
11067761Smsmith	clk_rate = clk_get_rate(pc->clk);
11177432Smsmith
11282372Smsmith	/*
11382372Smsmith	 * Since period and duty cycle registers have a width of 32
11467761Smsmith	 * bits, every possible input period can be obtained using the
11567761Smsmith	 * default prescaler value for all practical clock rate values.
11667761Smsmith	 */
11767761Smsmith	div = clk_rate * state->period;
11877432Smsmith	period = DIV_ROUND_CLOSEST_ULL(div,
11967761Smsmith				       pc->data->prescaler * NSEC_PER_SEC);
12082372Smsmith
12182372Smsmith	div = clk_rate * state->duty_cycle;
12267761Smsmith	duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC);
12367761Smsmith
12482372Smsmith	/*
12582372Smsmith	 * Lock the period and duty of previous configuration, then
12667761Smsmith	 * change the duty and period, that would not be effective.
12767761Smsmith	 */
12867761Smsmith	ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
12967761Smsmith	if (pc->data->supports_lock) {
13067761Smsmith		ctrl |= PWM_LOCK_EN;
13177432Smsmith		writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl);
13267761Smsmith	}
13382372Smsmith
13482372Smsmith	writel(period, pc->base + pc->data->regs.period);
13567761Smsmith	writel(duty, pc->base + pc->data->regs.duty);
13667761Smsmith
13782372Smsmith	if (pc->data->supports_polarity) {
13882372Smsmith		ctrl &= ~PWM_POLARITY_MASK;
13967761Smsmith		if (state->polarity == PWM_POLARITY_INVERSED)
14067761Smsmith			ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE;
14167761Smsmith		else
14267761Smsmith			ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE;
14367761Smsmith	}
14477432Smsmith
14567761Smsmith	/*
14682372Smsmith	 * Unlock and set polarity at the same time,
14767761Smsmith	 * the configuration of duty, period and polarity
14867761Smsmith	 * would be effective together at next period.
14967761Smsmith	 */
15067761Smsmith	if (pc->data->supports_lock)
15177432Smsmith		ctrl &= ~PWM_LOCK_EN;
15267761Smsmith
15382372Smsmith	writel(ctrl, pc->base + pc->data->regs.ctrl);
15467761Smsmith}
15567761Smsmith
15667761Smsmithstatic int rockchip_pwm_enable(struct pwm_chip *chip,
15767761Smsmith			       struct pwm_device *pwm,
15877432Smsmith			       bool enable)
15982372Smsmith{
16077432Smsmith	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
16167761Smsmith	u32 enable_conf = pc->data->enable_conf;
16267761Smsmith	int ret;
16377432Smsmith	u32 val;
16482372Smsmith
16567761Smsmith	if (enable) {
16667761Smsmith		ret = clk_enable(pc->clk);
16767761Smsmith		if (ret)
16877432Smsmith			return ret;
16982372Smsmith	}
17067761Smsmith
17167761Smsmith	val = readl_relaxed(pc->base + pc->data->regs.ctrl);
17277432Smsmith
17382372Smsmith	if (enable)
17467761Smsmith		val |= enable_conf;
17567761Smsmith	else
17677432Smsmith		val &= ~enable_conf;
17782372Smsmith
17867761Smsmith	writel_relaxed(val, pc->base + pc->data->regs.ctrl);
17967761Smsmith
18077432Smsmith	if (!enable)
18182372Smsmith		clk_disable(pc->clk);
18267761Smsmith
18367761Smsmith	return 0;
18467761Smsmith}
18567761Smsmith
18667761Smsmithstatic int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
18767761Smsmith			      const struct pwm_state *state)
18867761Smsmith{
18969744Smsmith	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
19067761Smsmith	struct pwm_state curstate;
19167761Smsmith	bool enabled;
19267761Smsmith	int ret = 0;
19367761Smsmith
19467761Smsmith	ret = clk_enable(pc->pclk);
19567761Smsmith	if (ret)
19667761Smsmith		return ret;
19767761Smsmith
19867761Smsmith	ret = clk_enable(pc->clk);
19967761Smsmith	if (ret)
20067761Smsmith		return ret;
20167761Smsmith
20267761Smsmith	pwm_get_state(pwm, &curstate);
20367761Smsmith	enabled = curstate.enabled;
20467761Smsmith
20567761Smsmith	if (state->polarity != curstate.polarity && enabled &&
20667761Smsmith	    !pc->data->supports_lock) {
20767761Smsmith		ret = rockchip_pwm_enable(chip, pwm, false);
20867761Smsmith		if (ret)
20967761Smsmith			goto out;
21067761Smsmith		enabled = false;
21167761Smsmith	}
21267761Smsmith
21367761Smsmith	rockchip_pwm_config(chip, pwm, state);
21467761Smsmith	if (state->enabled != enabled) {
21567761Smsmith		ret = rockchip_pwm_enable(chip, pwm, state->enabled);
21667761Smsmith		if (ret)
21767761Smsmith			goto out;
21867761Smsmith	}
21967761Smsmith
22067761Smsmithout:
22167761Smsmith	clk_disable(pc->clk);
22267761Smsmith	clk_disable(pc->pclk);
22367761Smsmith
22467761Smsmith	return ret;
22567761Smsmith}
22667761Smsmith
22767761Smsmithstatic const struct pwm_ops rockchip_pwm_ops = {
22867761Smsmith	.get_state = rockchip_pwm_get_state,
22967761Smsmith	.apply = rockchip_pwm_apply,
23067761Smsmith};
23167761Smsmith
23267761Smsmithstatic const struct rockchip_pwm_data pwm_data_v1 = {
23367761Smsmith	.regs = {
23467761Smsmith		.duty = 0x04,
23567761Smsmith		.period = 0x08,
23667761Smsmith		.cntr = 0x00,
23767761Smsmith		.ctrl = 0x0c,
23867761Smsmith	},
23967761Smsmith	.prescaler = 2,
24067761Smsmith	.supports_polarity = false,
24167761Smsmith	.supports_lock = false,
24267761Smsmith	.enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN,
24367761Smsmith};
24467761Smsmith
24567761Smsmithstatic const struct rockchip_pwm_data pwm_data_v2 = {
24667761Smsmith	.regs = {
24767761Smsmith		.duty = 0x08,
24867761Smsmith		.period = 0x04,
24967761Smsmith		.cntr = 0x00,
25067761Smsmith		.ctrl = 0x0c,
25167761Smsmith	},
25267761Smsmith	.prescaler = 1,
25367761Smsmith	.supports_polarity = true,
25467761Smsmith	.supports_lock = false,
25567761Smsmith	.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
25667761Smsmith		       PWM_CONTINUOUS,
25767761Smsmith};
25867761Smsmith
25967761Smsmithstatic const struct rockchip_pwm_data pwm_data_vop = {
26067761Smsmith	.regs = {
26167761Smsmith		.duty = 0x08,
26267761Smsmith		.period = 0x04,
26367761Smsmith		.cntr = 0x0c,
26467761Smsmith		.ctrl = 0x00,
26567761Smsmith	},
26667761Smsmith	.prescaler = 1,
26767761Smsmith	.supports_polarity = true,
26867761Smsmith	.supports_lock = false,
26967761Smsmith	.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
27067761Smsmith		       PWM_CONTINUOUS,
27167761Smsmith};
27267761Smsmith
27367761Smsmithstatic const struct rockchip_pwm_data pwm_data_v3 = {
27467761Smsmith	.regs = {
27567761Smsmith		.duty = 0x08,
27667761Smsmith		.period = 0x04,
27767761Smsmith		.cntr = 0x00,
27867761Smsmith		.ctrl = 0x0c,
27967761Smsmith	},
28067761Smsmith	.prescaler = 1,
28167761Smsmith	.supports_polarity = true,
28267761Smsmith	.supports_lock = true,
28367761Smsmith	.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
28467761Smsmith		       PWM_CONTINUOUS,
28567761Smsmith};
28667761Smsmith
28767761Smsmithstatic const struct of_device_id rockchip_pwm_dt_ids[] = {
28867761Smsmith	{ .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1},
28967761Smsmith	{ .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2},
29067761Smsmith	{ .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop},
29167761Smsmith	{ .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3},
29267761Smsmith	{ /* sentinel */ }
29367761Smsmith};
29467761SmsmithMODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids);
29567761Smsmith
29667761Smsmithstatic int rockchip_pwm_probe(struct platform_device *pdev)
29767761Smsmith{
29867761Smsmith	struct pwm_chip *chip;
29967761Smsmith	struct rockchip_pwm_chip *pc;
30067761Smsmith	u32 enable_conf, ctrl;
30167761Smsmith	bool enabled;
30267761Smsmith	int ret, count;
30367761Smsmith
30467761Smsmith	chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*pc));
30567761Smsmith	if (IS_ERR(chip))
30667761Smsmith		return PTR_ERR(chip);
30767761Smsmith	pc = to_rockchip_pwm_chip(chip);
30867761Smsmith
30967761Smsmith	pc->base = devm_platform_ioremap_resource(pdev, 0);
31067761Smsmith	if (IS_ERR(pc->base))
31167761Smsmith		return PTR_ERR(pc->base);
31267761Smsmith
31367761Smsmith	pc->clk = devm_clk_get(&pdev->dev, "pwm");
31467761Smsmith	if (IS_ERR(pc->clk)) {
31567761Smsmith		pc->clk = devm_clk_get(&pdev->dev, NULL);
31667761Smsmith		if (IS_ERR(pc->clk))
31767761Smsmith			return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
31867761Smsmith					     "Can't get PWM clk\n");
31967761Smsmith	}
32067761Smsmith
32167761Smsmith	count = of_count_phandle_with_args(pdev->dev.of_node,
32267761Smsmith					   "clocks", "#clock-cells");
323	if (count == 2)
324		pc->pclk = devm_clk_get(&pdev->dev, "pclk");
325	else
326		pc->pclk = pc->clk;
327
328	if (IS_ERR(pc->pclk))
329		return dev_err_probe(&pdev->dev, PTR_ERR(pc->pclk), "Can't get APB clk\n");
330
331	ret = clk_prepare_enable(pc->clk);
332	if (ret)
333		return dev_err_probe(&pdev->dev, ret, "Can't prepare enable PWM clk\n");
334
335	ret = clk_prepare_enable(pc->pclk);
336	if (ret) {
337		dev_err_probe(&pdev->dev, ret, "Can't prepare enable APB clk\n");
338		goto err_clk;
339	}
340
341	platform_set_drvdata(pdev, chip);
342
343	pc->data = device_get_match_data(&pdev->dev);
344	chip->ops = &rockchip_pwm_ops;
345
346	enable_conf = pc->data->enable_conf;
347	ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
348	enabled = (ctrl & enable_conf) == enable_conf;
349
350	ret = pwmchip_add(chip);
351	if (ret < 0) {
352		dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n");
353		goto err_pclk;
354	}
355
356	/* Keep the PWM clk enabled if the PWM appears to be up and running. */
357	if (!enabled)
358		clk_disable(pc->clk);
359
360	clk_disable(pc->pclk);
361
362	return 0;
363
364err_pclk:
365	clk_disable_unprepare(pc->pclk);
366err_clk:
367	clk_disable_unprepare(pc->clk);
368
369	return ret;
370}
371
372static void rockchip_pwm_remove(struct platform_device *pdev)
373{
374	struct pwm_chip *chip = platform_get_drvdata(pdev);
375	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
376
377	pwmchip_remove(chip);
378
379	clk_unprepare(pc->pclk);
380	clk_unprepare(pc->clk);
381}
382
383static struct platform_driver rockchip_pwm_driver = {
384	.driver = {
385		.name = "rockchip-pwm",
386		.of_match_table = rockchip_pwm_dt_ids,
387	},
388	.probe = rockchip_pwm_probe,
389	.remove_new = rockchip_pwm_remove,
390};
391module_platform_driver(rockchip_pwm_driver);
392
393MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
394MODULE_DESCRIPTION("Rockchip SoC PWM driver");
395MODULE_LICENSE("GPL v2");
396