diff options
Diffstat (limited to 'drivers/pwm/pwm-lpc18xx-sct.c')
| -rw-r--r-- | drivers/pwm/pwm-lpc18xx-sct.c | 230 |
1 files changed, 100 insertions, 130 deletions
diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index 475918d9f543..1e614b2a0227 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP LPC18xx State Configurable Timer - Pulse Width Modulator driver * * Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * * Notes * ===== * NXP LPC18xx provides a State Configurable Timer (SCT) which can be configured @@ -25,6 +22,7 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pwm.h> @@ -79,6 +77,8 @@ #define LPC18XX_PWM_EVENT_PERIOD 0 #define LPC18XX_PWM_EVENT_MAX 16 +#define LPC18XX_NUM_PWMS 16 + /* SCT conflict resolution */ enum lpc18xx_pwm_res_action { LPC18XX_PWM_RES_NONE, @@ -92,24 +92,21 @@ struct lpc18xx_pwm_data { }; struct lpc18xx_pwm_chip { - struct device *dev; - struct pwm_chip chip; void __iomem *base; struct clk *pwm_clk; unsigned long clk_rate; unsigned int period_ns; unsigned int min_period_ns; - unsigned int max_period_ns; + u64 max_period_ns; unsigned int period_event; unsigned long event_map; - struct mutex res_lock; - struct mutex period_lock; + struct lpc18xx_pwm_data channeldata[LPC18XX_NUM_PWMS]; }; static inline struct lpc18xx_pwm_chip * to_lpc18xx_pwm_chip(struct pwm_chip *chip) { - return container_of(chip, struct lpc18xx_pwm_chip, chip); + return pwmchip_get_drvdata(chip); } static inline void lpc18xx_pwm_writel(struct lpc18xx_pwm_chip *lpc18xx_pwm, @@ -130,8 +127,6 @@ static void lpc18xx_pwm_set_conflict_res(struct lpc18xx_pwm_chip *lpc18xx_pwm, { u32 val; - mutex_lock(&lpc18xx_pwm->res_lock); - /* * Simultaneous set and clear may happen on an output, that is the case * when duty_ns == period_ns. LPC18xx SCT allows to set a conflict @@ -141,60 +136,64 @@ static void lpc18xx_pwm_set_conflict_res(struct lpc18xx_pwm_chip *lpc18xx_pwm, val &= ~LPC18XX_PWM_RES_MASK(pwm->hwpwm); val |= LPC18XX_PWM_RES(pwm->hwpwm, action); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_RES_BASE, val); - - mutex_unlock(&lpc18xx_pwm->res_lock); } -static void lpc18xx_pwm_config_period(struct pwm_chip *chip, int period_ns) +static void lpc18xx_pwm_config_period(struct pwm_chip *chip, u64 period_ns) { struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); - u64 val; + u32 val; - val = (u64)period_ns * lpc18xx_pwm->clk_rate; - do_div(val, NSEC_PER_SEC); + /* + * With clk_rate < NSEC_PER_SEC this cannot overflow. + * With period_ns < max_period_ns this also fits into an u32. + * As period_ns >= min_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, lpc18xx_pwm->clk_rate); + * we have val >= 1. + */ + val = mul_u64_u64_div_u64(period_ns, lpc18xx_pwm->clk_rate, NSEC_PER_SEC); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_MATCH(lpc18xx_pwm->period_event), - (u32)val - 1); + val - 1); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_MATCHREL(lpc18xx_pwm->period_event), - (u32)val - 1); + val - 1); } static void lpc18xx_pwm_config_duty(struct pwm_chip *chip, - struct pwm_device *pwm, int duty_ns) + struct pwm_device *pwm, u64 duty_ns) { struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); - struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); - u64 val; + struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm]; + u32 val; - val = (u64)duty_ns * lpc18xx_pwm->clk_rate; - do_div(val, NSEC_PER_SEC); + /* + * With clk_rate <= NSEC_PER_SEC this cannot overflow. + * With duty_ns <= period_ns < max_period_ns this also fits into an u32. + */ + val = mul_u64_u64_div_u64(duty_ns, lpc18xx_pwm->clk_rate, NSEC_PER_SEC); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_MATCH(lpc18xx_data->duty_event), - (u32)val); + val); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_MATCHREL(lpc18xx_data->duty_event), - (u32)val); + val); } static int lpc18xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); - int requested_events, i; + int requested_events; if (period_ns < lpc18xx_pwm->min_period_ns || period_ns > lpc18xx_pwm->max_period_ns) { - dev_err(chip->dev, "period %d not in range\n", period_ns); + dev_err(pwmchip_parent(chip), "period %d not in range\n", period_ns); return -ERANGE; } - mutex_lock(&lpc18xx_pwm->period_lock); - requested_events = bitmap_weight(&lpc18xx_pwm->event_map, LPC18XX_PWM_EVENT_MAX); @@ -205,38 +204,26 @@ static int lpc18xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, */ if (requested_events > 2 && lpc18xx_pwm->period_ns != period_ns && lpc18xx_pwm->period_ns) { - dev_err(chip->dev, "conflicting period requested for PWM %u\n", + dev_err(pwmchip_parent(chip), "conflicting period requested for PWM %u\n", pwm->hwpwm); - mutex_unlock(&lpc18xx_pwm->period_lock); return -EBUSY; } if ((requested_events <= 2 && lpc18xx_pwm->period_ns != period_ns) || !lpc18xx_pwm->period_ns) { lpc18xx_pwm->period_ns = period_ns; - for (i = 0; i < chip->npwm; i++) - pwm_set_period(&chip->pwms[i], period_ns); lpc18xx_pwm_config_period(chip, period_ns); } - mutex_unlock(&lpc18xx_pwm->period_lock); - lpc18xx_pwm_config_duty(chip, pwm, duty_ns); return 0; } -static int lpc18xx_pwm_set_polarity(struct pwm_chip *chip, - struct pwm_device *pwm, - enum pwm_polarity polarity) -{ - return 0; -} - -static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) +static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity) { struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); - struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); + struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm]; enum lpc18xx_pwm_res_action res_action; unsigned int set_event, clear_event; @@ -249,7 +236,7 @@ static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) LPC18XX_PWM_EVSTATEMSK(lpc18xx_data->duty_event), LPC18XX_PWM_EVSTATEMSK_ALL); - if (pwm_get_polarity(pwm) == PWM_POLARITY_NORMAL) { + if (polarity == PWM_POLARITY_NORMAL) { set_event = lpc18xx_pwm->period_event; clear_event = lpc18xx_data->duty_event; res_action = LPC18XX_PWM_RES_SET; @@ -271,7 +258,7 @@ static int lpc18xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) static void lpc18xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); - struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); + struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm]; lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_EVCTRL(lpc18xx_data->duty_event), 0); @@ -282,17 +269,17 @@ static void lpc18xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) static int lpc18xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) { struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); - struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); + struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm]; unsigned long event; event = find_first_zero_bit(&lpc18xx_pwm->event_map, LPC18XX_PWM_EVENT_MAX); if (event >= LPC18XX_PWM_EVENT_MAX) { - dev_err(lpc18xx_pwm->dev, + dev_err(pwmchip_parent(chip), "maximum number of simultaneous channels reached\n"); return -EBUSY; - }; + } set_bit(event, &lpc18xx_pwm->event_map); lpc18xx_data->duty_event = event; @@ -303,19 +290,42 @@ static int lpc18xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) static void lpc18xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); - struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); + struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm]; clear_bit(lpc18xx_data->duty_event, &lpc18xx_pwm->event_map); } +static int lpc18xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + int err; + bool enabled = pwm->state.enabled; + + if (state->polarity != pwm->state.polarity && pwm->state.enabled) { + lpc18xx_pwm_disable(chip, pwm); + enabled = false; + } + + if (!state->enabled) { + if (enabled) + lpc18xx_pwm_disable(chip, pwm); + + return 0; + } + + err = lpc18xx_pwm_config(chip, pwm, state->duty_cycle, state->period); + if (err) + return err; + + if (!enabled) + err = lpc18xx_pwm_enable(chip, pwm, state->polarity); + + return err; +} static const struct pwm_ops lpc18xx_pwm_ops = { - .config = lpc18xx_pwm_config, - .set_polarity = lpc18xx_pwm_set_polarity, - .enable = lpc18xx_pwm_enable, - .disable = lpc18xx_pwm_disable, + .apply = lpc18xx_pwm_apply, .request = lpc18xx_pwm_request, .free = lpc18xx_pwm_free, - .owner = THIS_MODULE, }; static const struct of_device_id lpc18xx_pwm_of_match[] = { @@ -326,59 +336,43 @@ MODULE_DEVICE_TABLE(of, lpc18xx_pwm_of_match); static int lpc18xx_pwm_probe(struct platform_device *pdev) { + struct pwm_chip *chip; struct lpc18xx_pwm_chip *lpc18xx_pwm; - struct pwm_device *pwm; - struct resource *res; - int ret, i; + int ret; u64 val; - lpc18xx_pwm = devm_kzalloc(&pdev->dev, sizeof(*lpc18xx_pwm), - GFP_KERNEL); - if (!lpc18xx_pwm) - return -ENOMEM; - - lpc18xx_pwm->dev = &pdev->dev; + chip = devm_pwmchip_alloc(&pdev->dev, LPC18XX_NUM_PWMS, sizeof(*lpc18xx_pwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - lpc18xx_pwm->base = devm_ioremap_resource(&pdev->dev, res); + lpc18xx_pwm->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(lpc18xx_pwm->base)) return PTR_ERR(lpc18xx_pwm->base); - lpc18xx_pwm->pwm_clk = devm_clk_get(&pdev->dev, "pwm"); - if (IS_ERR(lpc18xx_pwm->pwm_clk)) { - dev_err(&pdev->dev, "failed to get pwm clock\n"); - return PTR_ERR(lpc18xx_pwm->pwm_clk); - } - - ret = clk_prepare_enable(lpc18xx_pwm->pwm_clk); - if (ret < 0) { - dev_err(&pdev->dev, "could not prepare or enable pwm clock\n"); - return ret; - } + lpc18xx_pwm->pwm_clk = devm_clk_get_enabled(&pdev->dev, "pwm"); + if (IS_ERR(lpc18xx_pwm->pwm_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(lpc18xx_pwm->pwm_clk), + "failed to get pwm clock\n"); lpc18xx_pwm->clk_rate = clk_get_rate(lpc18xx_pwm->pwm_clk); - if (!lpc18xx_pwm->clk_rate) { - dev_err(&pdev->dev, "pwm clock has no frequency\n"); - ret = -EINVAL; - goto disable_pwmclk; - } + if (!lpc18xx_pwm->clk_rate) + return dev_err_probe(&pdev->dev, + -EINVAL, "pwm clock has no frequency\n"); - mutex_init(&lpc18xx_pwm->res_lock); - mutex_init(&lpc18xx_pwm->period_lock); + /* + * If clkrate is too fast, the calculations in .apply() might overflow. + */ + if (lpc18xx_pwm->clk_rate > NSEC_PER_SEC) + return dev_err_probe(&pdev->dev, -EINVAL, "pwm clock to fast\n"); - val = (u64)NSEC_PER_SEC * LPC18XX_PWM_TIMER_MAX; - do_div(val, lpc18xx_pwm->clk_rate); - lpc18xx_pwm->max_period_ns = val; + lpc18xx_pwm->max_period_ns = + mul_u64_u64_div_u64(NSEC_PER_SEC, LPC18XX_PWM_TIMER_MAX, lpc18xx_pwm->clk_rate); lpc18xx_pwm->min_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, lpc18xx_pwm->clk_rate); - lpc18xx_pwm->chip.dev = &pdev->dev; - lpc18xx_pwm->chip.ops = &lpc18xx_pwm_ops; - lpc18xx_pwm->chip.base = -1; - lpc18xx_pwm->chip.npwm = 16; - lpc18xx_pwm->chip.of_xlate = of_pwm_xlate_with_flags; - lpc18xx_pwm->chip.of_pwm_n_cells = 3; + chip->ops = &lpc18xx_pwm_ops; /* SCT counter must be in unify (32 bit) mode */ lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CONFIG, @@ -403,29 +397,6 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev) lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_LIMIT, BIT(lpc18xx_pwm->period_event)); - ret = pwmchip_add(&lpc18xx_pwm->chip); - if (ret < 0) { - dev_err(&pdev->dev, "pwmchip_add failed: %d\n", ret); - goto disable_pwmclk; - } - - for (i = 0; i < lpc18xx_pwm->chip.npwm; i++) { - struct lpc18xx_pwm_data *data; - - pwm = &lpc18xx_pwm->chip.pwms[i]; - - data = devm_kzalloc(lpc18xx_pwm->dev, sizeof(*data), - GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto remove_pwmchip; - } - - pwm_set_chip_data(pwm, data); - } - - platform_set_drvdata(pdev, lpc18xx_pwm); - val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL); val &= ~LPC18XX_PWM_BIDIR; val &= ~LPC18XX_PWM_CTRL_HALT; @@ -433,27 +404,26 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev) val |= LPC18XX_PWM_PRE(0); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL, val); - return 0; + ret = pwmchip_add(chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "pwmchip_add failed\n"); -remove_pwmchip: - pwmchip_remove(&lpc18xx_pwm->chip); -disable_pwmclk: - clk_disable_unprepare(lpc18xx_pwm->pwm_clk); - return ret; + platform_set_drvdata(pdev, chip); + + return 0; } -static int lpc18xx_pwm_remove(struct platform_device *pdev) +static void lpc18xx_pwm_remove(struct platform_device *pdev) { - struct lpc18xx_pwm_chip *lpc18xx_pwm = platform_get_drvdata(pdev); + struct pwm_chip *chip = platform_get_drvdata(pdev); + struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); u32 val; + pwmchip_remove(chip); + val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_CTRL); lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CTRL, val | LPC18XX_PWM_CTRL_HALT); - - clk_disable_unprepare(lpc18xx_pwm->pwm_clk); - - return pwmchip_remove(&lpc18xx_pwm->chip); } static struct platform_driver lpc18xx_pwm_driver = { |
