]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
can: calc_bittiming: add PWM calculation
authorVincent Mailhol <mailhol@kernel.org>
Wed, 26 Nov 2025 10:16:11 +0000 (11:16 +0100)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Wed, 26 Nov 2025 10:20:43 +0000 (11:20 +0100)
Perform the PWM calculation according to CiA recommendations.

Note that for databitrates greater than 5 MBPS, tqmin is less than
CAN_PWM_NS_MAX (which is defined to 200 nano seconds), consequently,
the result of the division:

  DIV_ROUND_UP(xl_ns, CAN_PWM_NS_MAX)

is one and thus the for loop automatically stops on the first
iteration giving a single PWM symbol per bit as expected. Because of
that, there is no actual need for a separate conditional branch for
when the databitrate is greater than 5 MBPS.

Signed-off-by: Vincent Mailhol <mailhol@kernel.org>
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Link: https://patch.msgid.link/20251126-canxl-v8-10-e7e3eb74f889@pengutronix.de
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/dev/calc_bittiming.c
include/linux/can/bittiming.h

index 394d6974f48151230510d7f43c80d75e1429dd37..268ec6fa7c4922528d7ec01e79bafb63c0de0b2a 100644 (file)
@@ -2,6 +2,7 @@
 /* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
  * Copyright (C) 2006 Andrey Volkov, Varma Electronics
  * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2021-2025 Vincent Mailhol <mailhol@kernel.org>
  */
 
 #include <linux/units.h>
@@ -198,3 +199,38 @@ void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
                *ctrlmode |= tdc_auto;
        }
 }
+
+int can_calc_pwm(struct net_device *dev, struct netlink_ext_ack *extack)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
+       struct can_pwm *pwm = &priv->xl.pwm;
+       u32 xl_tqmin = can_bit_time_tqmin(&priv->xl.data_bittiming);
+       u32 xl_ns = can_tqmin_to_ns(xl_tqmin, priv->clock.freq);
+       u32 nom_tqmin = can_bit_time_tqmin(&priv->bittiming);
+       int pwm_per_bit_max = xl_tqmin / (pwm_const->pwms_min + pwm_const->pwml_min);
+       int pwm_per_bit;
+       u32 pwm_tqmin;
+
+       /* For 5 MB/s databitrate or greater, xl_ns < CAN_PWM_NS_MAX
+        * giving us a pwm_per_bit of 1 and the loop immediately breaks
+        */
+       for (pwm_per_bit = DIV_ROUND_UP(xl_ns, CAN_PWM_NS_MAX);
+            pwm_per_bit <= pwm_per_bit_max; pwm_per_bit++)
+               if (xl_tqmin % pwm_per_bit == 0)
+                       break;
+
+       if (pwm_per_bit > pwm_per_bit_max) {
+               NL_SET_ERR_MSG_FMT(extack,
+                                  "Can not divide the XL data phase's bit time: %u tqmin into multiple PWM symbols",
+                                  xl_tqmin);
+               return -EINVAL;
+       }
+
+       pwm_tqmin = xl_tqmin / pwm_per_bit;
+       pwm->pwms = DIV_ROUND_UP_POW2(pwm_tqmin, 4);
+       pwm->pwml = pwm_tqmin - pwm->pwms;
+       pwm->pwmo = nom_tqmin % pwm_tqmin;
+
+       return 0;
+}
index 2504fafc72e4705bcf69ae8dec6530cb57489db6..726d909e87cea1941861b56834f27836db116197 100644 (file)
@@ -180,6 +180,8 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
 void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
                   const struct can_bittiming *dbt,
                   u32 tdc_mask, u32 *ctrlmode, u32 ctrlmode_supported);
+
+int can_calc_pwm(struct net_device *dev, struct netlink_ext_ack *extack);
 #else /* !CONFIG_CAN_CALC_BITTIMING */
 static inline int
 can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
@@ -195,6 +197,14 @@ can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
              u32 tdc_mask, u32 *ctrlmode, u32 ctrlmode_supported)
 {
 }
+
+static inline int
+can_calc_pwm(struct net_device *dev, struct netlink_ext_ack *extack)
+{
+       NL_SET_ERR_MSG(extack,
+                      "bit-timing calculation not available: manually provide PWML and PWMS\n");
+       return -EINVAL;
+}
 #endif /* CONFIG_CAN_CALC_BITTIMING */
 
 void can_sjw_set_default(struct can_bittiming *bt);