dpll: add Embedded SYNC feature for a pin
authorArkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Thu, 22 Aug 2024 22:25:12 +0000 (00:25 +0200)
committerJakub Kicinski <kuba@kernel.org>
Tue, 27 Aug 2024 02:21:14 +0000 (19:21 -0700)
Implement and document new pin attributes for providing Embedded SYNC
capabilities to the DPLL subsystem users through a netlink pin-get
do/dump messages. Allow the user to set Embedded SYNC frequency with
pin-set do netlink message.

Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalewski@intel.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Link: https://patch.msgid.link/20240822222513.255179-2-arkadiusz.kubalewski@intel.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Documentation/driver-api/dpll.rst
Documentation/netlink/specs/dpll.yaml
drivers/dpll/dpll_netlink.c
drivers/dpll/dpll_nl.c
include/linux/dpll.h
include/uapi/linux/dpll.h

index ea8d16600e16a8530b7e633368bb53b75e878c15..e6855cd37e8525a8f78350be41d943379579b344 100644 (file)
@@ -214,6 +214,27 @@ offset values are fractional with 3-digit decimal places and shell be
 divided with ``DPLL_PIN_PHASE_OFFSET_DIVIDER`` to get integer part and
 modulo divided to get fractional part.
 
+Embedded SYNC
+=============
+
+Device may provide ability to use Embedded SYNC feature. It allows
+to embed additional SYNC signal into the base frequency of a pin - a one
+special pulse of base frequency signal every time SYNC signal pulse
+happens. The user can configure the frequency of Embedded SYNC.
+The Embedded SYNC capability is always related to a given base frequency
+and HW capabilities. The user is provided a range of Embedded SYNC
+frequencies supported, depending on current base frequency configured for
+the pin.
+
+  ========================================= =================================
+  ``DPLL_A_PIN_ESYNC_FREQUENCY``            current Embedded SYNC frequency
+  ``DPLL_A_PIN_ESYNC_FREQUENCY_SUPPORTED``  nest available Embedded SYNC
+                                            frequency ranges
+    ``DPLL_A_PIN_FREQUENCY_MIN``            attr minimum value of frequency
+    ``DPLL_A_PIN_FREQUENCY_MAX``            attr maximum value of frequency
+  ``DPLL_A_PIN_ESYNC_PULSE``                pulse type of Embedded SYNC
+  ========================================= =================================
+
 Configuration commands group
 ============================
 
index 94132d30e0e03506bb287918a9a6a2ac3b682a94..f2894ca35de8403b8f6c94ff0dab27470e2bab79 100644 (file)
@@ -345,6 +345,26 @@ attribute-sets:
           Value is in PPM (parts per million).
           This may be implemented for example for pin of type
           PIN_TYPE_SYNCE_ETH_PORT.
+      -
+        name: esync-frequency
+        type: u64
+        doc: |
+          Frequency of Embedded SYNC signal. If provided, the pin is configured
+          with a SYNC signal embedded into its base clock frequency.
+      -
+        name: esync-frequency-supported
+        type: nest
+        multi-attr: true
+        nested-attributes: frequency-range
+        doc: |
+          If provided a pin is capable of embedding a SYNC signal (within given
+          range) into its base frequency signal.
+      -
+        name: esync-pulse
+        type: u32
+        doc: |
+          A ratio of high to low state of a SYNC signal pulse embedded
+          into base clock frequency. Value is in percents.
   -
     name: pin-parent-device
     subset-of: pin
@@ -510,6 +530,9 @@ operations:
             - phase-adjust-max
             - phase-adjust
             - fractional-frequency-offset
+            - esync-frequency
+            - esync-frequency-supported
+            - esync-pulse
 
       dump:
         request:
@@ -536,6 +559,7 @@ operations:
             - parent-device
             - parent-pin
             - phase-adjust
+            - esync-frequency
     -
       name: pin-create-ntf
       doc: Notification about pin appearing
index 98e6ad8528d37ccc8169f545d745bda5fa004f69..fc0280dcddd107e2731c10c1e6fad23d6b0bdeeb 100644 (file)
@@ -342,6 +342,51 @@ dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
        return 0;
 }
 
+static int
+dpll_msg_add_pin_esync(struct sk_buff *msg, struct dpll_pin *pin,
+                      struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
+{
+       const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
+       struct dpll_device *dpll = ref->dpll;
+       struct dpll_pin_esync esync;
+       struct nlattr *nest;
+       int ret, i;
+
+       if (!ops->esync_get)
+               return 0;
+       ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+                            dpll_priv(dpll), &esync, extack);
+       if (ret == -EOPNOTSUPP)
+               return 0;
+       else if (ret)
+               return ret;
+       if (nla_put_64bit(msg, DPLL_A_PIN_ESYNC_FREQUENCY, sizeof(esync.freq),
+                         &esync.freq, DPLL_A_PIN_PAD))
+               return -EMSGSIZE;
+       if (nla_put_u32(msg, DPLL_A_PIN_ESYNC_PULSE, esync.pulse))
+               return -EMSGSIZE;
+       for (i = 0; i < esync.range_num; i++) {
+               nest = nla_nest_start(msg,
+                                     DPLL_A_PIN_ESYNC_FREQUENCY_SUPPORTED);
+               if (!nest)
+                       return -EMSGSIZE;
+               if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MIN,
+                                 sizeof(esync.range[i].min),
+                                 &esync.range[i].min, DPLL_A_PIN_PAD))
+                       goto nest_cancel;
+               if (nla_put_64bit(msg, DPLL_A_PIN_FREQUENCY_MAX,
+                                 sizeof(esync.range[i].max),
+                                 &esync.range[i].max, DPLL_A_PIN_PAD))
+                       goto nest_cancel;
+               nla_nest_end(msg, nest);
+       }
+       return 0;
+
+nest_cancel:
+       nla_nest_cancel(msg, nest);
+       return -EMSGSIZE;
+}
+
 static bool dpll_pin_is_freq_supported(struct dpll_pin *pin, u32 freq)
 {
        int fs;
@@ -481,6 +526,9 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
        if (ret)
                return ret;
        ret = dpll_msg_add_ffo(msg, pin, ref, extack);
+       if (ret)
+               return ret;
+       ret = dpll_msg_add_pin_esync(msg, pin, ref, extack);
        if (ret)
                return ret;
        if (xa_empty(&pin->parent_refs))
@@ -738,6 +786,83 @@ rollback:
        return ret;
 }
 
+static int
+dpll_pin_esync_set(struct dpll_pin *pin, struct nlattr *a,
+                  struct netlink_ext_ack *extack)
+{
+       struct dpll_pin_ref *ref, *failed;
+       const struct dpll_pin_ops *ops;
+       struct dpll_pin_esync esync;
+       u64 freq = nla_get_u64(a);
+       struct dpll_device *dpll;
+       bool supported = false;
+       unsigned long i;
+       int ret;
+
+       xa_for_each(&pin->dpll_refs, i, ref) {
+               ops = dpll_pin_ops(ref);
+               if (!ops->esync_set || !ops->esync_get) {
+                       NL_SET_ERR_MSG(extack,
+                                      "embedded sync feature is not supported by this device");
+                       return -EOPNOTSUPP;
+               }
+       }
+       ref = dpll_xa_ref_dpll_first(&pin->dpll_refs);
+       ops = dpll_pin_ops(ref);
+       dpll = ref->dpll;
+       ret = ops->esync_get(pin, dpll_pin_on_dpll_priv(dpll, pin), dpll,
+                            dpll_priv(dpll), &esync, extack);
+       if (ret) {
+               NL_SET_ERR_MSG(extack, "unable to get current embedded sync frequency value");
+               return ret;
+       }
+       if (freq == esync.freq)
+               return 0;
+       for (i = 0; i < esync.range_num; i++)
+               if (freq <= esync.range[i].max && freq >= esync.range[i].min)
+                       supported = true;
+       if (!supported) {
+               NL_SET_ERR_MSG_ATTR(extack, a,
+                                   "requested embedded sync frequency value is not supported by this device");
+               return -EINVAL;
+       }
+
+       xa_for_each(&pin->dpll_refs, i, ref) {
+               void *pin_dpll_priv;
+
+               ops = dpll_pin_ops(ref);
+               dpll = ref->dpll;
+               pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);
+               ret = ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),
+                                     freq, extack);
+               if (ret) {
+                       failed = ref;
+                       NL_SET_ERR_MSG_FMT(extack,
+                                          "embedded sync frequency set failed for dpll_id: %u",
+                                          dpll->id);
+                       goto rollback;
+               }
+       }
+       __dpll_pin_change_ntf(pin);
+
+       return 0;
+
+rollback:
+       xa_for_each(&pin->dpll_refs, i, ref) {
+               void *pin_dpll_priv;
+
+               if (ref == failed)
+                       break;
+               ops = dpll_pin_ops(ref);
+               dpll = ref->dpll;
+               pin_dpll_priv = dpll_pin_on_dpll_priv(dpll, pin);
+               if (ops->esync_set(pin, pin_dpll_priv, dpll, dpll_priv(dpll),
+                                  esync.freq, extack))
+                       NL_SET_ERR_MSG(extack, "set embedded sync frequency rollback failed");
+       }
+       return ret;
+}
+
 static int
 dpll_pin_on_pin_state_set(struct dpll_pin *pin, u32 parent_idx,
                          enum dpll_pin_state state,
@@ -1039,6 +1164,11 @@ dpll_pin_set_from_nlattr(struct dpll_pin *pin, struct genl_info *info)
                        if (ret)
                                return ret;
                        break;
+               case DPLL_A_PIN_ESYNC_FREQUENCY:
+                       ret = dpll_pin_esync_set(pin, a, info->extack);
+                       if (ret)
+                               return ret;
+                       break;
                }
        }
 
index 1e95f5397cfce65270fbc88d8916a24386258047..fe9b6893d261473e062b24fbbd3a182fb3393312 100644 (file)
@@ -62,7 +62,7 @@ static const struct nla_policy dpll_pin_get_dump_nl_policy[DPLL_A_PIN_ID + 1] =
 };
 
 /* DPLL_CMD_PIN_SET - do */
-static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PHASE_ADJUST + 1] = {
+static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_ESYNC_FREQUENCY + 1] = {
        [DPLL_A_PIN_ID] = { .type = NLA_U32, },
        [DPLL_A_PIN_FREQUENCY] = { .type = NLA_U64, },
        [DPLL_A_PIN_DIRECTION] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
@@ -71,6 +71,7 @@ static const struct nla_policy dpll_pin_set_nl_policy[DPLL_A_PIN_PHASE_ADJUST +
        [DPLL_A_PIN_PARENT_DEVICE] = NLA_POLICY_NESTED(dpll_pin_parent_device_nl_policy),
        [DPLL_A_PIN_PARENT_PIN] = NLA_POLICY_NESTED(dpll_pin_parent_pin_nl_policy),
        [DPLL_A_PIN_PHASE_ADJUST] = { .type = NLA_S32, },
+       [DPLL_A_PIN_ESYNC_FREQUENCY] = { .type = NLA_U64, },
 };
 
 /* Ops table for dpll */
@@ -138,7 +139,7 @@ static const struct genl_split_ops dpll_nl_ops[] = {
                .doit           = dpll_nl_pin_set_doit,
                .post_doit      = dpll_pin_post_doit,
                .policy         = dpll_pin_set_nl_policy,
-               .maxattr        = DPLL_A_PIN_PHASE_ADJUST,
+               .maxattr        = DPLL_A_PIN_ESYNC_FREQUENCY,
                .flags          = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
        },
 };
index d275736230b3b1955e343b991a7734ed48dc78b8..81f7b623d0ba673f95188efe69d18ad85dbbdfa0 100644 (file)
@@ -15,6 +15,7 @@
 
 struct dpll_device;
 struct dpll_pin;
+struct dpll_pin_esync;
 
 struct dpll_device_ops {
        int (*mode_get)(const struct dpll_device *dpll, void *dpll_priv,
@@ -83,6 +84,13 @@ struct dpll_pin_ops {
        int (*ffo_get)(const struct dpll_pin *pin, void *pin_priv,
                       const struct dpll_device *dpll, void *dpll_priv,
                       s64 *ffo, struct netlink_ext_ack *extack);
+       int (*esync_set)(const struct dpll_pin *pin, void *pin_priv,
+                        const struct dpll_device *dpll, void *dpll_priv,
+                        u64 freq, struct netlink_ext_ack *extack);
+       int (*esync_get)(const struct dpll_pin *pin, void *pin_priv,
+                        const struct dpll_device *dpll, void *dpll_priv,
+                        struct dpll_pin_esync *esync,
+                        struct netlink_ext_ack *extack);
 };
 
 struct dpll_pin_frequency {
@@ -111,6 +119,13 @@ struct dpll_pin_phase_adjust_range {
        s32 max;
 };
 
+struct dpll_pin_esync {
+       u64 freq;
+       const struct dpll_pin_frequency *range;
+       u8 range_num;
+       u8 pulse;
+};
+
 struct dpll_pin_properties {
        const char *board_label;
        const char *panel_label;
index 0c13d7f1a1bc3b7c77847fd36d83c15f3c2a1962..b0654ade7b7ebab6c89e181cc9634d255de7be40 100644 (file)
@@ -210,6 +210,9 @@ enum dpll_a_pin {
        DPLL_A_PIN_PHASE_ADJUST,
        DPLL_A_PIN_PHASE_OFFSET,
        DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET,
+       DPLL_A_PIN_ESYNC_FREQUENCY,
+       DPLL_A_PIN_ESYNC_FREQUENCY_SUPPORTED,
+       DPLL_A_PIN_ESYNC_PULSE,
 
        __DPLL_A_PIN_MAX,
        DPLL_A_PIN_MAX = (__DPLL_A_PIN_MAX - 1)