// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2025, STMicroelectronics - All Rights Reserved * Author: Cheick Traore * * Originally based on the Linux kernel v6.10 drivers/pwm/pwm-stm32.c. */ #include #include #include #include #include #include #include #define CCMR_CHANNEL_SHIFT 8 #define CCMR_CHANNEL_MASK 0xFF struct stm32_pwm_priv { bool have_complementary_output; bool invert_polarity; }; static u32 active_channels(struct stm32_timers_plat *plat) { return readl(plat->base + TIM_CCER) & TIM_CCER_CCXE; } static int stm32_pwm_set_config(struct udevice *dev, uint channel, uint period_ns, uint duty_ns) { struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev)); struct stm32_timers_priv *priv = dev_get_priv(dev_get_parent(dev)); unsigned long long prd, div, dty; unsigned int prescaler = 0; u32 ccmr, mask, shift; if (duty_ns > period_ns) return -EINVAL; /* * Period and prescaler values depends on clock rate * First we need to find the minimal value for prescaler such that * * period_ns * clkrate * ------------------------------ < max_arr + 1 * NSEC_PER_SEC * (prescaler + 1) * * This equation is equivalent to * * period_ns * clkrate * ---------------------------- < prescaler + 1 * NSEC_PER_SEC * (max_arr + 1) * * Using integer division and knowing that the right hand side is * integer, this is further equivalent to * * (period_ns * clkrate) // (NSEC_PER_SEC * (max_arr + 1)) ≤ prescaler */ div = (unsigned long long)priv->rate * period_ns; do_div(div, NSEC_PER_SEC); prd = div; do_div(div, priv->max_arr + 1); prescaler = div; if (prescaler > MAX_TIM_PSC) return -EINVAL; do_div(prd, prescaler + 1); if (!prd) return -EINVAL; /* * All channels share the same prescaler and counter so when two * channels are active at the same time we can't change them */ if (active_channels(plat) & ~(1 << channel * 4)) { u32 psc, arr; psc = readl(plat->base + TIM_PSC); arr = readl(plat->base + TIM_ARR); if (psc != prescaler || arr != prd - 1) return -EBUSY; } writel(prescaler, plat->base + TIM_PSC); writel(prd - 1, plat->base + TIM_ARR); setbits_le32(plat->base + TIM_CR1, TIM_CR1_ARPE); /* Calculate the duty cycles */ dty = prd * duty_ns; do_div(dty, period_ns); writel(dty, plat->base + TIM_CCRx(channel + 1)); /* Configure output mode */ shift = (channel & 0x1) * CCMR_CHANNEL_SHIFT; ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift; mask = CCMR_CHANNEL_MASK << shift; if (channel < 2) clrsetbits_le32(plat->base + TIM_CCMR1, mask, ccmr); else clrsetbits_le32(plat->base + TIM_CCMR2, mask, ccmr); setbits_le32(plat->base + TIM_BDTR, TIM_BDTR_MOE); return 0; } static int stm32_pwm_set_enable(struct udevice *dev, uint channel, bool enable) { struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev)); struct stm32_pwm_priv *priv = dev_get_priv(dev); u32 mask; /* Enable channel */ mask = TIM_CCER_CC1E << (channel * 4); if (priv->have_complementary_output) mask |= TIM_CCER_CC1NE << (channel * 4); if (enable) { setbits_le32(plat->base + TIM_CCER, mask); /* Make sure that registers are updated */ setbits_le32(plat->base + TIM_EGR, TIM_EGR_UG); /* Enable controller */ setbits_le32(plat->base + TIM_CR1, TIM_CR1_CEN); } else { clrbits_le32(plat->base + TIM_CCER, mask); /* When all channels are disabled, we can disable the controller */ if (!active_channels(plat)) clrbits_le32(plat->base + TIM_CR1, TIM_CR1_CEN); } return 0; } static int stm32_pwm_set_invert(struct udevice *dev, uint channel, bool polarity) { struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev)); struct stm32_pwm_priv *priv = dev_get_priv(dev); u32 mask; mask = TIM_CCER_CC1P << (channel * 4); if (priv->have_complementary_output) mask |= TIM_CCER_CC1NP << (channel * 4); clrsetbits_le32(plat->base + TIM_CCER, mask, polarity ? mask : 0); return 0; } static void stm32_pwm_detect_complementary(struct udevice *dev) { struct stm32_timers_plat *plat = dev_get_plat(dev_get_parent(dev)); struct stm32_pwm_priv *priv = dev_get_priv(dev); u32 ccer; /* * If complementary bit doesn't exist writing 1 will have no * effect so we can detect it. */ setbits_le32(plat->base + TIM_CCER, TIM_CCER_CC1NE); ccer = readl(plat->base + TIM_CCER); clrbits_le32(plat->base + TIM_CCER, TIM_CCER_CC1NE); priv->have_complementary_output = (ccer != 0); } static int stm32_pwm_probe(struct udevice *dev) { struct stm32_timers_priv *timer = dev_get_priv(dev_get_parent(dev)); if (timer->rate > 1000000000) { dev_err(dev, "Clock freq too high (%lu)\n", timer->rate); return -EINVAL; } stm32_pwm_detect_complementary(dev); return 0; } static const struct pwm_ops stm32_pwm_ops = { .set_config = stm32_pwm_set_config, .set_enable = stm32_pwm_set_enable, .set_invert = stm32_pwm_set_invert, }; static const struct udevice_id stm32_pwm_ids[] = { { .compatible = "st,stm32-pwm" }, { } }; U_BOOT_DRIVER(stm32_pwm) = { .name = "stm32_pwm", .id = UCLASS_PWM, .of_match = stm32_pwm_ids, .ops = &stm32_pwm_ops, .probe = stm32_pwm_probe, .priv_auto = sizeof(struct stm32_pwm_priv), };