2017-01-18 11:08 GMT+01:00 Thierry Reding <thierry.reding@xxxxxxxxx>: > On Thu, Jan 05, 2017 at 10:25:40AM +0100, Benjamin Gaignard wrote: >> This driver adds support for PWM driver on STM32 platform. >> The SoC have multiple instances of the hardware IP and each >> of them could have small differences: number of channels, >> complementary output, auto reload register size... >> >> version 6: >> - change st,breakinput parameter to make it usuable for stm32f7 too. >> >> version 4: >> - detect at probe time hardware capabilities >> - fix comments done on v2 and v3 >> - use PWM atomic ops >> >> version 2: >> - only keep one comptatible >> - use DT parameters to discover hardware block configuration >> >> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@xxxxxx> >> --- >> drivers/pwm/Kconfig | 9 + >> drivers/pwm/Makefile | 1 + >> drivers/pwm/pwm-stm32.c | 434 ++++++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 444 insertions(+) >> create mode 100644 drivers/pwm/pwm-stm32.c >> >> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig >> index f92dd41..88035c0 100644 >> --- a/drivers/pwm/Kconfig >> +++ b/drivers/pwm/Kconfig >> @@ -397,6 +397,15 @@ config PWM_STI >> To compile this driver as a module, choose M here: the module >> will be called pwm-sti. >> >> +config PWM_STM32 >> + tristate "STMicroelectronics STM32 PWM" >> + depends on (ARCH_STM32 && OF && MFD_STM32_TIMERS) || COMPILE_TEST >> + help >> + Generic PWM framework driver for STM32 SoCs. >> + >> + To compile this driver as a module, choose M here: the module >> + will be called pwm-stm32. >> + >> config PWM_STMPE >> bool "STMPE expander PWM export" >> depends on MFD_STMPE >> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile >> index a48bdb5..346a83b 100644 >> --- a/drivers/pwm/Makefile >> +++ b/drivers/pwm/Makefile >> @@ -38,6 +38,7 @@ obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o >> obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o >> obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o >> obj-$(CONFIG_PWM_STI) += pwm-sti.o >> +obj-$(CONFIG_PWM_STM32) += pwm-stm32.o >> obj-$(CONFIG_PWM_STMPE) += pwm-stmpe.o >> obj-$(CONFIG_PWM_SUN4I) += pwm-sun4i.o >> obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o >> diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c >> new file mode 100644 >> index 0000000..fcf0a78 >> --- /dev/null >> +++ b/drivers/pwm/pwm-stm32.c >> @@ -0,0 +1,434 @@ >> +/* >> + * Copyright (C) STMicroelectronics 2016 >> + * >> + * Author: Gerald Baeza <gerald.baeza@xxxxxx> >> + * >> + * License terms: GNU General Public License (GPL), version 2 >> + * >> + * Inspired by timer-stm32.c from Maxime Coquelin >> + * pwm-atmel.c from Bo Shen >> + */ >> + >> +#include <linux/mfd/stm32-timers.h> >> +#include <linux/module.h> >> +#include <linux/platform_device.h> >> +#include <linux/pwm.h> >> +#include <linux/of.h> > > Can you please sort these alphabetically? sure > >> + >> +#define CCMR_CHANNEL_SHIFT 8 >> +#define CCMR_CHANNEL_MASK 0xFF >> +#define MAX_BREAKINPUT 2 > > Okay, this answers my question regarding the st,breakinput property. I > still think it'd be good to have this in the binding documentation just > to avoid having to look at implementation to find out. > >> + >> +struct stm32_pwm { >> + struct pwm_chip chip; >> + struct device *dev; >> + struct clk *clk; >> + struct regmap *regmap; >> + unsigned int caps; > > This seems completely unused? Yes I will remove it > >> + unsigned int npwm; > > It's somewhat redundant to have this here, since the same information is > already contained in struct pwm_chip.npwm. > > Since you use this primarily for detection, how about you make the > stm32_pwm_detect_channels() function return the value and store it in a > local variable in ->probe()? That might be useful also because you > need to check the return value of regmap_update_bits() which technically > could fail. > I will remove npwm field and put the result of stm32_pwm_detect_channels() directly on chip.npwm. regmap functions could failed (even if I haven't experiment that case) but testing all return make the code unreadable so I have decide to not test it.... >> + u32 max_arr; >> + bool have_complementary_output; >> +}; >> + >> +struct stm32_breakinput { >> + u32 index; >> + u32 level; >> + u32 filter; >> +}; >> + >> +static inline struct stm32_pwm *to_stm32_pwm_dev(struct pwm_chip *chip) >> +{ >> + return container_of(chip, struct stm32_pwm, chip); >> +} >> + >> +static u32 active_channels(struct stm32_pwm *dev) >> +{ >> + u32 ccer; >> + >> + regmap_read(dev->regmap, TIM_CCER, &ccer); >> + >> + return ccer & TIM_CCER_CCXE; >> +} > > This looks like something that you could track in software, but this is > probably fine, too. Again, technically regmap_read() could fail, so you > might want to consider adding some code to handle it. In practice it > probably won't, so maybe you don't. TIM_CCER_CCXE is a value that IIO timer can also read (not write) so I have keep the same logic for pwm driver. > >> + >> +static int write_ccrx(struct stm32_pwm *dev, struct pwm_device *pwm, >> + u32 value) >> +{ >> + switch (pwm->hwpwm) { >> + case 0: >> + return regmap_write(dev->regmap, TIM_CCR1, value); >> + case 1: >> + return regmap_write(dev->regmap, TIM_CCR2, value); >> + case 2: >> + return regmap_write(dev->regmap, TIM_CCR3, value); >> + case 3: >> + return regmap_write(dev->regmap, TIM_CCR4, value); >> + } >> + return -EINVAL; >> +} >> + >> +static int stm32_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, >> + int duty_ns, int period_ns) >> +{ >> + struct stm32_pwm *priv = to_stm32_pwm_dev(chip); >> + unsigned long long prd, div, dty; >> + unsigned int prescaler = 0; >> + u32 ccmr, mask, shift; >> + >> + /* Period and prescaler values depends on clock rate */ >> + div = (unsigned long long)clk_get_rate(priv->clk) * period_ns; >> + >> + do_div(div, NSEC_PER_SEC); >> + prd = div; >> + >> + while (div > priv->max_arr) { >> + prescaler++; >> + div = prd; >> + do_div(div, (prescaler + 1)); > > Nit: there's no need for the parentheses here. okay >> + } >> + >> + prd = div; >> + >> + if (prescaler > MAX_TIM_PSC) { >> + dev_err(chip->dev, "prescaler exceeds the maximum value\n"); >> + return -EINVAL; >> + } >> + >> + /* >> + * All channels share the same prescaler and counter so when two >> + * channels are active at the same we can't change them > > Nit: "at the same time"? okay > >> + */ >> + if (active_channels(priv) & ~(1 << pwm->hwpwm * 4)) { >> + u32 psc, arr; >> + >> + regmap_read(priv->regmap, TIM_PSC, &psc); >> + regmap_read(priv->regmap, TIM_ARR, &arr); >> + >> + if ((psc != prescaler) || (arr != prd - 1)) >> + return -EBUSY; >> + } >> + >> + regmap_write(priv->regmap, TIM_PSC, prescaler); >> + regmap_write(priv->regmap, TIM_ARR, prd - 1); >> + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); >> + >> + /* Calculate the duty cycles */ >> + dty = prd * duty_ns; >> + do_div(dty, period_ns); >> + >> + write_ccrx(priv, pwm, dty); >> + >> + /* Configure output mode */ >> + shift = (pwm->hwpwm & 0x1) * CCMR_CHANNEL_SHIFT; >> + ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift; >> + mask = CCMR_CHANNEL_MASK << shift; >> + >> + if (pwm->hwpwm < 2) >> + regmap_update_bits(priv->regmap, TIM_CCMR1, mask, ccmr); >> + else >> + regmap_update_bits(priv->regmap, TIM_CCMR2, mask, ccmr); >> + >> + regmap_update_bits(priv->regmap, TIM_BDTR, >> + TIM_BDTR_MOE | TIM_BDTR_AOE, >> + TIM_BDTR_MOE | TIM_BDTR_AOE); >> + >> + return 0; >> +} >> + >> +static int stm32_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, >> + enum pwm_polarity polarity) >> +{ >> + u32 mask; >> + struct stm32_pwm *priv = to_stm32_pwm_dev(chip); >> + >> + mask = TIM_CCER_CC1P << (pwm->hwpwm * 4); >> + if (priv->have_complementary_output) >> + mask |= TIM_CCER_CC1NP << (pwm->hwpwm * 4); >> + >> + regmap_update_bits(priv->regmap, TIM_CCER, mask, >> + polarity == PWM_POLARITY_NORMAL ? 0 : mask); >> + >> + return 0; >> +} >> + >> +static int stm32_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) >> +{ >> + u32 mask; >> + struct stm32_pwm *priv = to_stm32_pwm_dev(chip); >> + >> + clk_enable(priv->clk); > > This can fail, so its return value should be checked. Also, I don't see > a clk_prepare() anywhere. Is that something that maybe the MFD driver > should be doing? It currently isn't. I will check the return value. You are right clk_prepare() is done in mfd driver when calling devm_regmap_init_mmio_clk() > >> + >> + /* Enable channel */ >> + mask = TIM_CCER_CC1E << (pwm->hwpwm * 4); >> + if (priv->have_complementary_output) >> + mask |= TIM_CCER_CC1NE << (pwm->hwpwm * 4); >> + >> + regmap_update_bits(priv->regmap, TIM_CCER, mask, mask); >> + >> + /* Make sure that registers are updated */ >> + regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG); >> + >> + /* Enable controller */ >> + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN); >> + >> + return 0; >> +} >> + >> +static void stm32_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) >> +{ >> + u32 mask; >> + struct stm32_pwm *priv = to_stm32_pwm_dev(chip); >> + >> + /* Disable channel */ >> + mask = TIM_CCER_CC1E << (pwm->hwpwm * 4); >> + if (priv->have_complementary_output) >> + mask |= TIM_CCER_CC1NE << (pwm->hwpwm * 4); >> + >> + regmap_update_bits(priv->regmap, TIM_CCER, mask, 0); >> + >> + /* When all channels are disabled, we can disable the controller */ >> + if (!active_channels(priv)) >> + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0); >> + >> + clk_disable(priv->clk); >> +} >> + >> +static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, >> + struct pwm_state *state) >> +{ >> + struct pwm_state curstate; >> + bool enabled; >> + int ret; >> + >> + pwm_get_state(pwm, &curstate); >> + enabled = curstate.enabled; > > There should be no need to do this in drivers. pwm_get_state() is for > PWM API users. Drivers can directly dereference pwm->state. ok > >> + >> + if (enabled && !state->enabled) { >> + stm32_pwm_disable(chip, pwm); >> + return 0; >> + } >> + >> + if (state->polarity != curstate.polarity && enabled) >> + stm32_pwm_set_polarity(chip, pwm, state->polarity); > > So that's kind of a violation of atomic API semantics. The above means > that if you have a PWM in the following state: > > enabled: no > polarity: normal > > and want to set this: > > enabled: yes > polarity: inversed > > then you will ignore the new polarity setting. What's the reason for > "&& enabled) in the conditional above? There is no reason, I will remove it. >> + >> + ret = stm32_pwm_config(chip, pwm, state->duty_cycle, state->period); >> + if (ret) >> + return ret; >> + >> + if (!enabled && state->enabled) >> + ret = stm32_pwm_enable(chip, pwm); >> + >> + return ret; >> +} > > Would it be possible to merge stm32_pwm_disable(), stm32_pwm_enable(), > stm32_pwm_set_polarity() and stm32_pwm_config() into stm32_pwm_apply()? > Part of the reason for the atomic API was to make it easier to write > these drivers, but your implementation effectively copies what the > transitional helpers do. > > It might not make a difference technically in your case, but I think > it'd make the implementation more compact and set a better example for > future reference. hmm... it will create a fat function with lot of where enabling/disabling/configuration will be mixed I'm really not convince that will more compact and readable. > >> + >> +static const struct pwm_ops stm32pwm_ops = { >> + .owner = THIS_MODULE, >> + .apply = stm32_pwm_apply, >> +}; >> + >> +static int stm32_pwm_set_breakinput(struct stm32_pwm *priv, >> + int level, int filter) >> +{ >> + u32 bdtr = TIM_BDTR_BKE; >> + >> + if (level) >> + bdtr |= TIM_BDTR_BKP; >> + >> + bdtr |= (filter & TIM_BDTR_BKF_MASK) << TIM_BDTR_BKF_SHIFT; >> + >> + regmap_update_bits(priv->regmap, >> + TIM_BDTR, TIM_BDTR_BKE | TIM_BDTR_BKP | TIM_BDTR_BKF, >> + bdtr); >> + >> + regmap_read(priv->regmap, TIM_BDTR, &bdtr); >> + >> + return (bdtr & TIM_BDTR_BKE) ? 0 : -EINVAL; >> +} >> + >> +static int stm32_pwm_set_breakinput2(struct stm32_pwm *priv, >> + int level, int filter) >> +{ >> + u32 bdtr = TIM_BDTR_BK2E; >> + >> + if (level) >> + bdtr |= TIM_BDTR_BK2P; >> + >> + bdtr |= (filter & TIM_BDTR_BKF_MASK) << TIM_BDTR_BK2F_SHIFT; >> + >> + regmap_update_bits(priv->regmap, >> + TIM_BDTR, TIM_BDTR_BK2E | >> + TIM_BDTR_BK2P | >> + TIM_BDTR_BK2F, >> + bdtr); >> + >> + regmap_read(priv->regmap, TIM_BDTR, &bdtr); >> + >> + return (bdtr & TIM_BDTR_BK2E) ? 0 : -EINVAL; >> +} > > As far as I can tell the only difference here is the various bit > positions. Can you collapse the above two functions and add a new > parameter to unify some code? Yes it is all about bit shifting, I had try unify those two functions with index has additional parameter but it just add if() before each lines so no real benefit for code size. > >> + >> +static int stm32_pwm_apply_breakinputs(struct stm32_pwm *priv, >> + struct device_node *np) >> +{ >> + struct stm32_breakinput breakinput[MAX_BREAKINPUT]; >> + int nb, ret, i, array_size; >> + >> + nb = of_property_count_elems_of_size(np, "st,breakinput", >> + sizeof(struct stm32_breakinput)); >> + >> + /* >> + * Because "st,breakinput" parameter is optional do not make probe >> + * failed if it doesn't exist. >> + */ >> + if (nb <= 0) >> + return 0; >> + >> + if (nb > MAX_BREAKINPUT) >> + return -EINVAL; >> + >> + array_size = nb * sizeof(struct stm32_breakinput) / sizeof(u32); >> + ret = of_property_read_u32_array(np, "st,breakinput", >> + &breakinput[0].index, array_size); > > Maybe (u32 *)breakinput? That would make it more resilient against > changes in ordering of fields in the struct. Granted, that's not likely > to change, but I think it's a good idea in general to write code in a > way that's safe in a more general case. That way if somebody ever were > to copy from your code and then decide to reorder fields in their code > things wouldn't fall apart. Yes it is not suppose to change but I will use (u32 *)breakinput. >> + if (ret) >> + return ret; >> + >> + for (i = 0; i < nb && !ret; i++) { >> + switch (breakinput[i].index) { >> + case 0: >> + { >> + ret = stm32_pwm_set_breakinput(priv, >> + breakinput[i].level, >> + breakinput[i].filter); >> + break; >> + } > > Curly braces are unnecessary here. removed > >> + case 1: >> + { >> + ret = stm32_pwm_set_breakinput2(priv, >> + breakinput[i].level, >> + breakinput[i].filter); >> + >> + break; >> + } >> + default: >> + { >> + ret = -EINVAL; >> + break; >> + } >> + } >> + } >> + >> + return ret; >> +} >> + >> +static void stm32_pwm_detect_complementary(struct stm32_pwm *priv) >> +{ >> + u32 ccer; >> + >> + /* >> + * If complementary bit doesn't exist writing 1 will have no >> + * effect so we can detect it. >> + */ >> + regmap_update_bits(priv->regmap, >> + TIM_CCER, TIM_CCER_CC1NE, TIM_CCER_CC1NE); >> + regmap_read(priv->regmap, TIM_CCER, &ccer); >> + regmap_update_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE, 0); > > This is strange: why are we disabling outputs here? Shouldn't the last > line here undo the first instead? Yes it should TIM_CCER_CC1NE not TIM_CCER_CCXE, I will fix it, thanks > >> + >> + priv->have_complementary_output = (ccer != 0); >> +} >> + >> +static void stm32_pwm_detect_channels(struct stm32_pwm *priv) >> +{ >> + u32 ccer; >> + >> + /* >> + * If channels enable bits don't exist writing 1 will have no >> + * effect so we can detect and count them. >> + */ >> + regmap_update_bits(priv->regmap, >> + TIM_CCER, TIM_CCER_CCXE, TIM_CCER_CCXE); >> + regmap_read(priv->regmap, TIM_CCER, &ccer); >> + regmap_update_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE, 0); > > Does this have the potential to glitch? I suspect that the clock may not > be on at this point and therefore no PWM outputs will be generated, but > is that guaranteed to always be the case? Set TIM_CCER_CCXE isn't enough to enable PWM generation, TIM_CR1_CEN in TIM_CR1 register must also to set so no risk of glitch here > > Thierry -- Benjamin Gaignard Graphic Study Group Linaro.org │ Open source software for ARM SoCs Follow Linaro: Facebook | Twitter | Blog -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html