[PATCH 3/5] pwm: sun4i: Introduce (optional) reset support
Andre Przywara
andre.przywara at arm.com
Tue Mar 6 18:07:17 PST 2018
While the PWM IP in the Allwinner H6 SoC is fully compatible to those
used in older SoCs (H3, A64), it features a dedicated reset line which
needs to be de-asserted.
Add support for an optional "resets" DT property in our pwm-sun4i probe
routine, and assert and de-assert the reset line, where needed.
This allows to enable PWM support on the H6.
Signed-off-by: Andre Przywara <andre.przywara at arm.com>
---
drivers/pwm/pwm-sun4i.c | 34 +++++++++++++++++++++++++++-------
1 file changed, 27 insertions(+), 7 deletions(-)
diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c
index 078172dee462..4eefb27fe80b 100644
--- a/drivers/pwm/pwm-sun4i.c
+++ b/drivers/pwm/pwm-sun4i.c
@@ -17,6 +17,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
+#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/time.h>
@@ -79,6 +80,7 @@ struct sun4i_pwm_data {
struct sun4i_pwm_chip {
struct pwm_chip chip;
struct clk *clk;
+ struct reset_control *reset;
void __iomem *base;
spinlock_t ctrl_lock;
const struct sun4i_pwm_data *data;
@@ -206,7 +208,7 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
struct pwm_state cstate;
u32 ctrl;
- int ret;
+ int ret = 0;
unsigned int delay_us;
unsigned long now;
@@ -218,6 +220,18 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
dev_err(chip->dev, "failed to enable PWM clock\n");
return ret;
}
+
+ /* Deassert reset if we have a reset control */
+ if (sun4i_pwm->reset) {
+ ret = reset_control_deassert(sun4i_pwm->reset);
+ if (ret) {
+ dev_err(chip->dev,
+ "Cannot deassert reset control\n");
+ clk_disable_unprepare(sun4i_pwm->clk);
+
+ return ret;
+ }
+ }
}
spin_lock(&sun4i_pwm->ctrl_lock);
@@ -234,7 +248,8 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
dev_err(chip->dev, "period exceeds the maximum value\n");
spin_unlock(&sun4i_pwm->ctrl_lock);
if (!cstate.enabled)
- clk_disable_unprepare(sun4i_pwm->clk);
+ goto out_disable;
+
return ret;
}
@@ -274,10 +289,8 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
if (state->enabled)
return 0;
- if (!sun4i_pwm->needs_delay[pwm->hwpwm]) {
- clk_disable_unprepare(sun4i_pwm->clk);
- return 0;
- }
+ if (!sun4i_pwm->needs_delay[pwm->hwpwm])
+ goto out_disable;
/* We need a full period to elapse before disabling the channel. */
now = jiffies;
@@ -299,9 +312,12 @@ static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
sun4i_pwm_writel(sun4i_pwm, ctrl, PWM_CTRL_REG);
spin_unlock(&sun4i_pwm->ctrl_lock);
+out_disable:
clk_disable_unprepare(sun4i_pwm->clk);
+ if (sun4i_pwm->reset)
+ reset_control_assert(sun4i_pwm->reset);
- return 0;
+ return ret;
}
static const struct pwm_ops sun4i_pwm_ops = {
@@ -370,6 +386,10 @@ static int sun4i_pwm_probe(struct platform_device *pdev)
if (IS_ERR(pwm->clk))
return PTR_ERR(pwm->clk);
+ pwm->reset = devm_reset_control_get_optional(&pdev->dev, NULL);
+ if (IS_ERR(pwm->reset))
+ return PTR_ERR(pwm->reset);
+
pwm->chip.dev = &pdev->dev;
pwm->chip.ops = &sun4i_pwm_ops;
pwm->chip.base = -1;
--
2.14.1
More information about the linux-arm-kernel
mailing list