18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PWM driver for Rockchip SoCs
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
68c2ecf20Sopenharmony_ci * Copyright (C) 2014 ROCKCHIP, Inc.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/clk.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/of_device.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci#include <linux/pwm.h>
168c2ecf20Sopenharmony_ci#include <linux/time.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define PWM_CTRL_TIMER_EN	(1 << 0)
198c2ecf20Sopenharmony_ci#define PWM_CTRL_OUTPUT_EN	(1 << 3)
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define PWM_ENABLE		(1 << 0)
228c2ecf20Sopenharmony_ci#define PWM_CONTINUOUS		(1 << 1)
238c2ecf20Sopenharmony_ci#define PWM_DUTY_POSITIVE	(1 << 3)
248c2ecf20Sopenharmony_ci#define PWM_DUTY_NEGATIVE	(0 << 3)
258c2ecf20Sopenharmony_ci#define PWM_INACTIVE_NEGATIVE	(0 << 4)
268c2ecf20Sopenharmony_ci#define PWM_INACTIVE_POSITIVE	(1 << 4)
278c2ecf20Sopenharmony_ci#define PWM_POLARITY_MASK	(PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE)
288c2ecf20Sopenharmony_ci#define PWM_OUTPUT_LEFT		(0 << 5)
298c2ecf20Sopenharmony_ci#define PWM_LOCK_EN		(1 << 6)
308c2ecf20Sopenharmony_ci#define PWM_LP_DISABLE		(0 << 8)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct rockchip_pwm_chip {
338c2ecf20Sopenharmony_ci	struct pwm_chip chip;
348c2ecf20Sopenharmony_ci	struct clk *clk;
358c2ecf20Sopenharmony_ci	struct clk *pclk;
368c2ecf20Sopenharmony_ci	const struct rockchip_pwm_data *data;
378c2ecf20Sopenharmony_ci	void __iomem *base;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct rockchip_pwm_regs {
418c2ecf20Sopenharmony_ci	unsigned long duty;
428c2ecf20Sopenharmony_ci	unsigned long period;
438c2ecf20Sopenharmony_ci	unsigned long cntr;
448c2ecf20Sopenharmony_ci	unsigned long ctrl;
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistruct rockchip_pwm_data {
488c2ecf20Sopenharmony_ci	struct rockchip_pwm_regs regs;
498c2ecf20Sopenharmony_ci	unsigned int prescaler;
508c2ecf20Sopenharmony_ci	bool supports_polarity;
518c2ecf20Sopenharmony_ci	bool supports_lock;
528c2ecf20Sopenharmony_ci	u32 enable_conf;
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	return container_of(c, struct rockchip_pwm_chip, chip);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic void rockchip_pwm_get_state(struct pwm_chip *chip,
618c2ecf20Sopenharmony_ci				   struct pwm_device *pwm,
628c2ecf20Sopenharmony_ci				   struct pwm_state *state)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
658c2ecf20Sopenharmony_ci	u32 enable_conf = pc->data->enable_conf;
668c2ecf20Sopenharmony_ci	unsigned long clk_rate;
678c2ecf20Sopenharmony_ci	u64 tmp;
688c2ecf20Sopenharmony_ci	u32 val;
698c2ecf20Sopenharmony_ci	int ret;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	ret = clk_enable(pc->pclk);
728c2ecf20Sopenharmony_ci	if (ret)
738c2ecf20Sopenharmony_ci		return;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	clk_rate = clk_get_rate(pc->clk);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	tmp = readl_relaxed(pc->base + pc->data->regs.period);
788c2ecf20Sopenharmony_ci	tmp *= pc->data->prescaler * NSEC_PER_SEC;
798c2ecf20Sopenharmony_ci	state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	tmp = readl_relaxed(pc->base + pc->data->regs.duty);
828c2ecf20Sopenharmony_ci	tmp *= pc->data->prescaler * NSEC_PER_SEC;
838c2ecf20Sopenharmony_ci	state->duty_cycle =  DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	val = readl_relaxed(pc->base + pc->data->regs.ctrl);
868c2ecf20Sopenharmony_ci	state->enabled = (val & enable_conf) == enable_conf;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (pc->data->supports_polarity && !(val & PWM_DUTY_POSITIVE))
898c2ecf20Sopenharmony_ci		state->polarity = PWM_POLARITY_INVERSED;
908c2ecf20Sopenharmony_ci	else
918c2ecf20Sopenharmony_ci		state->polarity = PWM_POLARITY_NORMAL;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	clk_disable(pc->pclk);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
978c2ecf20Sopenharmony_ci			       const struct pwm_state *state)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
1008c2ecf20Sopenharmony_ci	unsigned long period, duty;
1018c2ecf20Sopenharmony_ci	u64 clk_rate, div;
1028c2ecf20Sopenharmony_ci	u32 ctrl;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	clk_rate = clk_get_rate(pc->clk);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/*
1078c2ecf20Sopenharmony_ci	 * Since period and duty cycle registers have a width of 32
1088c2ecf20Sopenharmony_ci	 * bits, every possible input period can be obtained using the
1098c2ecf20Sopenharmony_ci	 * default prescaler value for all practical clock rate values.
1108c2ecf20Sopenharmony_ci	 */
1118c2ecf20Sopenharmony_ci	div = clk_rate * state->period;
1128c2ecf20Sopenharmony_ci	period = DIV_ROUND_CLOSEST_ULL(div,
1138c2ecf20Sopenharmony_ci				       pc->data->prescaler * NSEC_PER_SEC);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	div = clk_rate * state->duty_cycle;
1168c2ecf20Sopenharmony_ci	duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/*
1198c2ecf20Sopenharmony_ci	 * Lock the period and duty of previous configuration, then
1208c2ecf20Sopenharmony_ci	 * change the duty and period, that would not be effective.
1218c2ecf20Sopenharmony_ci	 */
1228c2ecf20Sopenharmony_ci	ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
1238c2ecf20Sopenharmony_ci	if (pc->data->supports_lock) {
1248c2ecf20Sopenharmony_ci		ctrl |= PWM_LOCK_EN;
1258c2ecf20Sopenharmony_ci		writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl);
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	writel(period, pc->base + pc->data->regs.period);
1298c2ecf20Sopenharmony_ci	writel(duty, pc->base + pc->data->regs.duty);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (pc->data->supports_polarity) {
1328c2ecf20Sopenharmony_ci		ctrl &= ~PWM_POLARITY_MASK;
1338c2ecf20Sopenharmony_ci		if (state->polarity == PWM_POLARITY_INVERSED)
1348c2ecf20Sopenharmony_ci			ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE;
1358c2ecf20Sopenharmony_ci		else
1368c2ecf20Sopenharmony_ci			ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/*
1408c2ecf20Sopenharmony_ci	 * Unlock and set polarity at the same time,
1418c2ecf20Sopenharmony_ci	 * the configuration of duty, period and polarity
1428c2ecf20Sopenharmony_ci	 * would be effective together at next period.
1438c2ecf20Sopenharmony_ci	 */
1448c2ecf20Sopenharmony_ci	if (pc->data->supports_lock)
1458c2ecf20Sopenharmony_ci		ctrl &= ~PWM_LOCK_EN;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	writel(ctrl, pc->base + pc->data->regs.ctrl);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int rockchip_pwm_enable(struct pwm_chip *chip,
1518c2ecf20Sopenharmony_ci			       struct pwm_device *pwm,
1528c2ecf20Sopenharmony_ci			       bool enable)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
1558c2ecf20Sopenharmony_ci	u32 enable_conf = pc->data->enable_conf;
1568c2ecf20Sopenharmony_ci	int ret;
1578c2ecf20Sopenharmony_ci	u32 val;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (enable) {
1608c2ecf20Sopenharmony_ci		ret = clk_enable(pc->clk);
1618c2ecf20Sopenharmony_ci		if (ret)
1628c2ecf20Sopenharmony_ci			return ret;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	val = readl_relaxed(pc->base + pc->data->regs.ctrl);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	if (enable)
1688c2ecf20Sopenharmony_ci		val |= enable_conf;
1698c2ecf20Sopenharmony_ci	else
1708c2ecf20Sopenharmony_ci		val &= ~enable_conf;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	writel_relaxed(val, pc->base + pc->data->regs.ctrl);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (!enable)
1758c2ecf20Sopenharmony_ci		clk_disable(pc->clk);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	return 0;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
1818c2ecf20Sopenharmony_ci			      const struct pwm_state *state)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
1848c2ecf20Sopenharmony_ci	struct pwm_state curstate;
1858c2ecf20Sopenharmony_ci	bool enabled;
1868c2ecf20Sopenharmony_ci	int ret = 0;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	ret = clk_enable(pc->pclk);
1898c2ecf20Sopenharmony_ci	if (ret)
1908c2ecf20Sopenharmony_ci		return ret;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	pwm_get_state(pwm, &curstate);
1938c2ecf20Sopenharmony_ci	enabled = curstate.enabled;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (state->polarity != curstate.polarity && enabled &&
1968c2ecf20Sopenharmony_ci	    !pc->data->supports_lock) {
1978c2ecf20Sopenharmony_ci		ret = rockchip_pwm_enable(chip, pwm, false);
1988c2ecf20Sopenharmony_ci		if (ret)
1998c2ecf20Sopenharmony_ci			goto out;
2008c2ecf20Sopenharmony_ci		enabled = false;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	rockchip_pwm_config(chip, pwm, state);
2048c2ecf20Sopenharmony_ci	if (state->enabled != enabled) {
2058c2ecf20Sopenharmony_ci		ret = rockchip_pwm_enable(chip, pwm, state->enabled);
2068c2ecf20Sopenharmony_ci		if (ret)
2078c2ecf20Sopenharmony_ci			goto out;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ciout:
2118c2ecf20Sopenharmony_ci	clk_disable(pc->pclk);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return ret;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic const struct pwm_ops rockchip_pwm_ops = {
2178c2ecf20Sopenharmony_ci	.get_state = rockchip_pwm_get_state,
2188c2ecf20Sopenharmony_ci	.apply = rockchip_pwm_apply,
2198c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
2208c2ecf20Sopenharmony_ci};
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic const struct rockchip_pwm_data pwm_data_v1 = {
2238c2ecf20Sopenharmony_ci	.regs = {
2248c2ecf20Sopenharmony_ci		.duty = 0x04,
2258c2ecf20Sopenharmony_ci		.period = 0x08,
2268c2ecf20Sopenharmony_ci		.cntr = 0x00,
2278c2ecf20Sopenharmony_ci		.ctrl = 0x0c,
2288c2ecf20Sopenharmony_ci	},
2298c2ecf20Sopenharmony_ci	.prescaler = 2,
2308c2ecf20Sopenharmony_ci	.supports_polarity = false,
2318c2ecf20Sopenharmony_ci	.supports_lock = false,
2328c2ecf20Sopenharmony_ci	.enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN,
2338c2ecf20Sopenharmony_ci};
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic const struct rockchip_pwm_data pwm_data_v2 = {
2368c2ecf20Sopenharmony_ci	.regs = {
2378c2ecf20Sopenharmony_ci		.duty = 0x08,
2388c2ecf20Sopenharmony_ci		.period = 0x04,
2398c2ecf20Sopenharmony_ci		.cntr = 0x00,
2408c2ecf20Sopenharmony_ci		.ctrl = 0x0c,
2418c2ecf20Sopenharmony_ci	},
2428c2ecf20Sopenharmony_ci	.prescaler = 1,
2438c2ecf20Sopenharmony_ci	.supports_polarity = true,
2448c2ecf20Sopenharmony_ci	.supports_lock = false,
2458c2ecf20Sopenharmony_ci	.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
2468c2ecf20Sopenharmony_ci		       PWM_CONTINUOUS,
2478c2ecf20Sopenharmony_ci};
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic const struct rockchip_pwm_data pwm_data_vop = {
2508c2ecf20Sopenharmony_ci	.regs = {
2518c2ecf20Sopenharmony_ci		.duty = 0x08,
2528c2ecf20Sopenharmony_ci		.period = 0x04,
2538c2ecf20Sopenharmony_ci		.cntr = 0x0c,
2548c2ecf20Sopenharmony_ci		.ctrl = 0x00,
2558c2ecf20Sopenharmony_ci	},
2568c2ecf20Sopenharmony_ci	.prescaler = 1,
2578c2ecf20Sopenharmony_ci	.supports_polarity = true,
2588c2ecf20Sopenharmony_ci	.supports_lock = false,
2598c2ecf20Sopenharmony_ci	.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
2608c2ecf20Sopenharmony_ci		       PWM_CONTINUOUS,
2618c2ecf20Sopenharmony_ci};
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_cistatic const struct rockchip_pwm_data pwm_data_v3 = {
2648c2ecf20Sopenharmony_ci	.regs = {
2658c2ecf20Sopenharmony_ci		.duty = 0x08,
2668c2ecf20Sopenharmony_ci		.period = 0x04,
2678c2ecf20Sopenharmony_ci		.cntr = 0x00,
2688c2ecf20Sopenharmony_ci		.ctrl = 0x0c,
2698c2ecf20Sopenharmony_ci	},
2708c2ecf20Sopenharmony_ci	.prescaler = 1,
2718c2ecf20Sopenharmony_ci	.supports_polarity = true,
2728c2ecf20Sopenharmony_ci	.supports_lock = true,
2738c2ecf20Sopenharmony_ci	.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
2748c2ecf20Sopenharmony_ci		       PWM_CONTINUOUS,
2758c2ecf20Sopenharmony_ci};
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic const struct of_device_id rockchip_pwm_dt_ids[] = {
2788c2ecf20Sopenharmony_ci	{ .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1},
2798c2ecf20Sopenharmony_ci	{ .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2},
2808c2ecf20Sopenharmony_ci	{ .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop},
2818c2ecf20Sopenharmony_ci	{ .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3},
2828c2ecf20Sopenharmony_ci	{ /* sentinel */ }
2838c2ecf20Sopenharmony_ci};
2848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic int rockchip_pwm_probe(struct platform_device *pdev)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	const struct of_device_id *id;
2898c2ecf20Sopenharmony_ci	struct rockchip_pwm_chip *pc;
2908c2ecf20Sopenharmony_ci	struct resource *r;
2918c2ecf20Sopenharmony_ci	u32 enable_conf, ctrl;
2928c2ecf20Sopenharmony_ci	bool enabled;
2938c2ecf20Sopenharmony_ci	int ret, count;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev);
2968c2ecf20Sopenharmony_ci	if (!id)
2978c2ecf20Sopenharmony_ci		return -EINVAL;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
3008c2ecf20Sopenharmony_ci	if (!pc)
3018c2ecf20Sopenharmony_ci		return -ENOMEM;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3048c2ecf20Sopenharmony_ci	pc->base = devm_ioremap_resource(&pdev->dev, r);
3058c2ecf20Sopenharmony_ci	if (IS_ERR(pc->base))
3068c2ecf20Sopenharmony_ci		return PTR_ERR(pc->base);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	pc->clk = devm_clk_get(&pdev->dev, "pwm");
3098c2ecf20Sopenharmony_ci	if (IS_ERR(pc->clk)) {
3108c2ecf20Sopenharmony_ci		pc->clk = devm_clk_get(&pdev->dev, NULL);
3118c2ecf20Sopenharmony_ci		if (IS_ERR(pc->clk))
3128c2ecf20Sopenharmony_ci			return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
3138c2ecf20Sopenharmony_ci					     "Can't get bus clk\n");
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	count = of_count_phandle_with_args(pdev->dev.of_node,
3178c2ecf20Sopenharmony_ci					   "clocks", "#clock-cells");
3188c2ecf20Sopenharmony_ci	if (count == 2)
3198c2ecf20Sopenharmony_ci		pc->pclk = devm_clk_get(&pdev->dev, "pclk");
3208c2ecf20Sopenharmony_ci	else
3218c2ecf20Sopenharmony_ci		pc->pclk = pc->clk;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (IS_ERR(pc->pclk)) {
3248c2ecf20Sopenharmony_ci		ret = PTR_ERR(pc->pclk);
3258c2ecf20Sopenharmony_ci		if (ret != -EPROBE_DEFER)
3268c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Can't get APB clk: %d\n", ret);
3278c2ecf20Sopenharmony_ci		return ret;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(pc->clk);
3318c2ecf20Sopenharmony_ci	if (ret) {
3328c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Can't prepare enable bus clk: %d\n", ret);
3338c2ecf20Sopenharmony_ci		return ret;
3348c2ecf20Sopenharmony_ci	}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(pc->pclk);
3378c2ecf20Sopenharmony_ci	if (ret) {
3388c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Can't prepare enable APB clk: %d\n", ret);
3398c2ecf20Sopenharmony_ci		goto err_clk;
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, pc);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	pc->data = id->data;
3458c2ecf20Sopenharmony_ci	pc->chip.dev = &pdev->dev;
3468c2ecf20Sopenharmony_ci	pc->chip.ops = &rockchip_pwm_ops;
3478c2ecf20Sopenharmony_ci	pc->chip.base = -1;
3488c2ecf20Sopenharmony_ci	pc->chip.npwm = 1;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (pc->data->supports_polarity) {
3518c2ecf20Sopenharmony_ci		pc->chip.of_xlate = of_pwm_xlate_with_flags;
3528c2ecf20Sopenharmony_ci		pc->chip.of_pwm_n_cells = 3;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	enable_conf = pc->data->enable_conf;
3568c2ecf20Sopenharmony_ci	ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
3578c2ecf20Sopenharmony_ci	enabled = (ctrl & enable_conf) == enable_conf;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	ret = pwmchip_add(&pc->chip);
3608c2ecf20Sopenharmony_ci	if (ret < 0) {
3618c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
3628c2ecf20Sopenharmony_ci		goto err_pclk;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	/* Keep the PWM clk enabled if the PWM appears to be up and running. */
3668c2ecf20Sopenharmony_ci	if (!enabled)
3678c2ecf20Sopenharmony_ci		clk_disable(pc->clk);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	clk_disable(pc->pclk);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	return 0;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cierr_pclk:
3748c2ecf20Sopenharmony_ci	clk_disable_unprepare(pc->pclk);
3758c2ecf20Sopenharmony_cierr_clk:
3768c2ecf20Sopenharmony_ci	clk_disable_unprepare(pc->clk);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	return ret;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic int rockchip_pwm_remove(struct platform_device *pdev)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct rockchip_pwm_chip *pc = platform_get_drvdata(pdev);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	clk_unprepare(pc->pclk);
3868c2ecf20Sopenharmony_ci	clk_unprepare(pc->clk);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return pwmchip_remove(&pc->chip);
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic struct platform_driver rockchip_pwm_driver = {
3928c2ecf20Sopenharmony_ci	.driver = {
3938c2ecf20Sopenharmony_ci		.name = "rockchip-pwm",
3948c2ecf20Sopenharmony_ci		.of_match_table = rockchip_pwm_dt_ids,
3958c2ecf20Sopenharmony_ci	},
3968c2ecf20Sopenharmony_ci	.probe = rockchip_pwm_probe,
3978c2ecf20Sopenharmony_ci	.remove = rockchip_pwm_remove,
3988c2ecf20Sopenharmony_ci};
3998c2ecf20Sopenharmony_cimodule_platform_driver(rockchip_pwm_driver);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
4028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Rockchip SoC PWM driver");
4038c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
404