Use the DTS property #pwm-cells to determine if the PWM controller needs to enable the WDT reload feature, which changes the duty cycle to a preprogrammed value after a WDT/EXTRST#. When #pwm-cells = <4>, the feature will be enabled, and the PWM consumer can use the 4th argument to set the reload duty cycle [0-255]. Signed-off-by: Billy Tsai <billy_tsai@xxxxxxxxxxxxxx> Change-Id: Ided520f73220581e3b37819a106ec81ebf9bb5a6 --- drivers/hwmon/aspeed-g6-pwm-tach.c | 49 ++++++++++++++++++++++++++++++ drivers/pwm/core.c | 6 ++-- include/linux/pwm.h | 10 ++++++ 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-tach.c index 75eadda738ab..df47f9aa8ee6 100644 --- a/drivers/hwmon/aspeed-g6-pwm-tach.c +++ b/drivers/hwmon/aspeed-g6-pwm-tach.c @@ -56,6 +56,7 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pwm.h> +#include <dt-bindings/pwm/pwm.h> #include <linux/reset.h> #include <linux/sysfs.h> @@ -452,6 +453,51 @@ static void aspeed_pwm_tach_reset_assert(void *data) reset_control_assert(rst); } +static void aspeed_pwm_set_wdt_reload(struct pwm_chip *chip, + struct pwm_device *pwm, + u64 reload_duty_cycle) +{ + struct aspeed_pwm_tach_data *priv = aspeed_pwm_chip_to_data(chip); + u32 hwpwm = pwm->hwpwm, val; + + val = readl(priv->base + PWM_ASPEED_DUTY_CYCLE(hwpwm)); + val &= ~PWM_ASPEED_DUTY_CYCLE_POINT_AS_WDT; + val |= FIELD_PREP(PWM_ASPEED_DUTY_CYCLE_POINT_AS_WDT, + reload_duty_cycle); + writel(val, priv->base + PWM_ASPEED_DUTY_CYCLE(hwpwm)); + + val = readl(priv->base + PWM_ASPEED_CTRL(hwpwm)); + val |= PWM_ASPEED_CTRL_DUTY_LOAD_AS_WDT_ENABLE; + writel(val, priv->base + PWM_ASPEED_CTRL(hwpwm)); +} + +static struct pwm_device * +aspeed_pwm_xlate(struct pwm_chip *chip, const struct of_phandle_args *args) +{ + struct pwm_device *pwm; + + /* flags in the fourth cell are optional */ + if (args->args_count < 3) + return ERR_PTR(-EINVAL); + + if (args->args[0] >= chip->npwm) + return ERR_PTR(-EINVAL); + + pwm = pwm_request_from_chip(chip, args->args[0], NULL); + if (IS_ERR(pwm)) + return pwm; + + pwm->args.period = args->args[1]; + pwm->args.polarity = PWM_POLARITY_NORMAL; + if (args->args[2] & PWM_POLARITY_INVERTED) + pwm->args.polarity = PWM_POLARITY_INVERSED; + + if (args->args_count > 3 && args->args[3] < U8_MAX) + aspeed_pwm_set_wdt_reload(chip, pwm, args->args[3]); + + return pwm; +} + static int aspeed_pwm_tach_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev, *hwmon; @@ -493,6 +539,9 @@ static int aspeed_pwm_tach_probe(struct platform_device *pdev) pwmchip_set_drvdata(chip, priv); chip->ops = &aspeed_pwm_ops; + if (IS_ENABLED(CONFIG_OF)) + chip->of_xlate = aspeed_pwm_xlate; + ret = devm_pwmchip_add(dev, chip); if (ret) return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 6e752e148b98..8251f7b361ab 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -422,9 +422,8 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) * chip. A negative error code is returned if the index is not valid for the * specified PWM chip or if the PWM device cannot be requested. */ -static struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, - unsigned int index, - const char *label) +struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, + unsigned int index, const char *label) { struct pwm_device *pwm; int err; @@ -442,6 +441,7 @@ static struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, return pwm; } +EXPORT_SYMBOL_GPL(pwm_request_from_chip); struct pwm_device * of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args) diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 8acd60b53f58..95ae885f65c3 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -405,6 +405,8 @@ void pwmchip_remove(struct pwm_chip *chip); int __devm_pwmchip_add(struct device *dev, struct pwm_chip *chip, struct module *owner); #define devm_pwmchip_add(dev, chip) __devm_pwmchip_add(dev, chip, THIS_MODULE) +struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, + unsigned int index, const char *label); struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *chip, const struct of_phandle_args *args); struct pwm_device *of_pwm_single_xlate(struct pwm_chip *chip, @@ -504,6 +506,14 @@ static inline void pwm_put(struct pwm_device *pwm) might_sleep(); } +static inline struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, + unsigned int index, + const char *label) +{ + might_sleep(); + return ERR_PTR(-ENODEV); +} + static inline struct pwm_device *devm_pwm_get(struct device *dev, const char *consumer) { -- 2.25.1