Add the RHEL 687.14.1..687.15.1 backports (1270-1284) from centos-stream-9 and
upstream stable, on top of 687.13.1. The dpll/zl3073x and ice RSS-queue series are
consolidated (they carry RHEL kABI wrapping and RHEL-only files). The mlx5 kabi
removal (RHEL-181822) is applied via updated Module.kabi_{aarch64,s390x,x86_64}.
Bump pkgrelease and specrelease to 687.15.1.
3226 lines
96 KiB
Diff
3226 lines
96 KiB
Diff
Subject: [PATCH] dpll/zl3073x series (RHEL-167273/167833/172938; RH_KABI + RHEL chan.c/.h)
|
|
# AlmaLinux: reconstructed (687.13.1->687.15.1); includes RHEL kABI / RHEL-only files
|
|
|
|
diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
|
|
index 83118c7..93c191b 100644
|
|
--- a/Documentation/driver-api/dpll.rst
|
|
+++ b/Documentation/driver-api/dpll.rst
|
|
@@ -250,6 +250,24 @@ in the ``DPLL_A_PIN_PHASE_OFFSET`` attribute.
|
|
``DPLL_A_PHASE_OFFSET_MONITOR`` attr state of a feature
|
|
=============================== ========================
|
|
|
|
+Frequency monitor
|
|
+=================
|
|
+
|
|
+Some DPLL devices may offer the capability to measure the actual
|
|
+frequency of all available input pins. The attribute and current feature state
|
|
+shall be included in the response message of the ``DPLL_CMD_DEVICE_GET``
|
|
+command for supported DPLL devices. In such cases, users can also control
|
|
+the feature using the ``DPLL_CMD_DEVICE_SET`` command by setting the
|
|
+``enum dpll_feature_state`` values for the attribute.
|
|
+Once enabled the measured input frequency for each input pin shall be
|
|
+returned in the ``DPLL_A_PIN_MEASURED_FREQUENCY`` attribute. The value
|
|
+is in millihertz (mHz), using ``DPLL_PIN_MEASURED_FREQUENCY_DIVIDER``
|
|
+as the divider.
|
|
+
|
|
+ =============================== ========================
|
|
+ ``DPLL_A_FREQUENCY_MONITOR`` attr state of a feature
|
|
+ =============================== ========================
|
|
+
|
|
Embedded SYNC
|
|
=============
|
|
|
|
@@ -411,6 +429,8 @@ according to attribute purpose.
|
|
``DPLL_A_PIN_STATE`` attr state of pin on the parent
|
|
pin
|
|
``DPLL_A_PIN_CAPABILITIES`` attr bitmask of pin capabilities
|
|
+ ``DPLL_A_PIN_MEASURED_FREQUENCY`` attr measured frequency of
|
|
+ an input pin in mHz
|
|
==================================== ==================================
|
|
|
|
==================================== =================================
|
|
diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml
|
|
index ed58662..cb5da35 100644
|
|
--- a/Documentation/netlink/specs/dpll.yaml
|
|
+++ b/Documentation/netlink/specs/dpll.yaml
|
|
@@ -240,6 +240,20 @@ definitions:
|
|
integer part of a measured phase offset value.
|
|
Value of (DPLL_A_PHASE_OFFSET % DPLL_PHASE_OFFSET_DIVIDER) is a
|
|
fractional part of a measured phase offset value.
|
|
+ -
|
|
+ type: const
|
|
+ name: pin-measured-frequency-divider
|
|
+ value: 1000
|
|
+ doc: |
|
|
+ pin measured frequency divider allows userspace to calculate
|
|
+ a value of measured input frequency as a fractional value with
|
|
+ three digit decimal precision (millihertz).
|
|
+ Value of (DPLL_A_PIN_MEASURED_FREQUENCY /
|
|
+ DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is an integer part of
|
|
+ a measured frequency value.
|
|
+ Value of (DPLL_A_PIN_MEASURED_FREQUENCY %
|
|
+ DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part of
|
|
+ a measured frequency value.
|
|
-
|
|
type: enum
|
|
name: feature-state
|
|
@@ -319,6 +333,13 @@ attribute-sets:
|
|
name: phase-offset-avg-factor
|
|
type: u32
|
|
doc: Averaging factor applied to calculation of reported phase offset.
|
|
+ -
|
|
+ name: frequency-monitor
|
|
+ type: u32
|
|
+ enum: feature-state
|
|
+ doc: Current or desired state of the frequency monitor feature.
|
|
+ If enabled, dpll device shall measure all currently available
|
|
+ inputs for their actual input frequency.
|
|
-
|
|
name: pin
|
|
enum-name: dpll_a_pin
|
|
@@ -456,6 +477,17 @@ attribute-sets:
|
|
Value is in PPT (parts per trillion, 10^-12).
|
|
Note: This attribute provides higher resolution than the standard
|
|
fractional-frequency-offset (which is in PPM).
|
|
+ -
|
|
+ name: measured-frequency
|
|
+ type: u64
|
|
+ doc: |
|
|
+ The measured frequency of the input pin in millihertz (mHz).
|
|
+ Value of (DPLL_A_PIN_MEASURED_FREQUENCY /
|
|
+ DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is an integer part (Hz)
|
|
+ of a measured frequency value.
|
|
+ Value of (DPLL_A_PIN_MEASURED_FREQUENCY %
|
|
+ DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part
|
|
+ of a measured frequency value.
|
|
|
|
-
|
|
name: pin-parent-device
|
|
@@ -544,6 +576,7 @@ operations:
|
|
- type
|
|
- phase-offset-monitor
|
|
- phase-offset-avg-factor
|
|
+ - frequency-monitor
|
|
|
|
dump:
|
|
reply: *dev-attrs
|
|
@@ -563,6 +596,7 @@ operations:
|
|
- mode
|
|
- phase-offset-monitor
|
|
- phase-offset-avg-factor
|
|
+ - frequency-monitor
|
|
-
|
|
name: device-create-ntf
|
|
doc: Notification about device appearing
|
|
@@ -643,6 +677,7 @@ operations:
|
|
- esync-frequency-supported
|
|
- esync-pulse
|
|
- reference-sync
|
|
+ - measured-frequency
|
|
|
|
dump:
|
|
request:
|
|
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
|
|
index 842f733..84e1b63 100644
|
|
--- a/drivers/dpll/dpll_core.c
|
|
+++ b/drivers/dpll/dpll_core.c
|
|
@@ -880,7 +880,10 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
|
|
|
|
if (WARN_ON(!ops) ||
|
|
WARN_ON(!ops->state_on_dpll_get) ||
|
|
- WARN_ON(!ops->direction_get))
|
|
+ WARN_ON(!ops->direction_get) ||
|
|
+ WARN_ON(ops->measured_freq_get &&
|
|
+ (!dpll_device_ops(dpll)->freq_monitor_get ||
|
|
+ !dpll_device_ops(dpll)->freq_monitor_set)))
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&dpll_lock);
|
|
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
|
|
index 95ae786..0ff1658 100644
|
|
--- a/drivers/dpll/dpll_netlink.c
|
|
+++ b/drivers/dpll/dpll_netlink.c
|
|
@@ -175,6 +175,26 @@ dpll_msg_add_phase_offset_monitor(struct sk_buff *msg, struct dpll_device *dpll,
|
|
return 0;
|
|
}
|
|
|
|
+static int
|
|
+dpll_msg_add_freq_monitor(struct sk_buff *msg, struct dpll_device *dpll,
|
|
+ struct netlink_ext_ack *extack)
|
|
+{
|
|
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
|
|
+ enum dpll_feature_state state;
|
|
+ int ret;
|
|
+
|
|
+ if (ops->freq_monitor_set && ops->freq_monitor_get) {
|
|
+ ret = ops->freq_monitor_get(dpll, dpll_priv(dpll),
|
|
+ &state, extack);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ if (nla_put_u32(msg, DPLL_A_FREQUENCY_MONITOR, state))
|
|
+ return -EMSGSIZE;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int
|
|
dpll_msg_add_phase_offset_avg_factor(struct sk_buff *msg,
|
|
struct dpll_device *dpll,
|
|
@@ -400,6 +420,38 @@ static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin,
|
|
ffo);
|
|
}
|
|
|
|
+static int dpll_msg_add_measured_freq(struct sk_buff *msg, struct dpll_pin *pin,
|
|
+ struct dpll_pin_ref *ref,
|
|
+ struct netlink_ext_ack *extack)
|
|
+{
|
|
+ const struct dpll_device_ops *dev_ops = dpll_device_ops(ref->dpll);
|
|
+ const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
|
|
+ struct dpll_device *dpll = ref->dpll;
|
|
+ enum dpll_feature_state state;
|
|
+ u64 measured_freq;
|
|
+ int ret;
|
|
+
|
|
+ if (!ops->measured_freq_get)
|
|
+ return 0;
|
|
+ ret = dev_ops->freq_monitor_get(dpll, dpll_priv(dpll),
|
|
+ &state, extack);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ if (state == DPLL_FEATURE_STATE_DISABLE)
|
|
+ return 0;
|
|
+ ret = ops->measured_freq_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
|
|
+ dpll, dpll_priv(dpll), &measured_freq,
|
|
+ extack);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ if (nla_put_64bit(msg, DPLL_A_PIN_MEASURED_FREQUENCY,
|
|
+ sizeof(measured_freq), &measured_freq,
|
|
+ DPLL_A_PIN_PAD))
|
|
+ return -EMSGSIZE;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int
|
|
dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
|
|
struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
|
|
@@ -670,6 +722,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_measured_freq(msg, pin, ref, extack);
|
|
if (ret)
|
|
return ret;
|
|
ret = dpll_msg_add_pin_esync(msg, pin, ref, extack);
|
|
@@ -722,6 +777,9 @@ dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
|
|
if (ret)
|
|
return ret;
|
|
ret = dpll_msg_add_phase_offset_avg_factor(msg, dpll, extack);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ ret = dpll_msg_add_freq_monitor(msg, dpll, extack);
|
|
if (ret)
|
|
return ret;
|
|
|
|
@@ -958,6 +1016,32 @@ dpll_phase_offset_avg_factor_set(struct dpll_device *dpll, struct nlattr *a,
|
|
extack);
|
|
}
|
|
|
|
+static int
|
|
+dpll_freq_monitor_set(struct dpll_device *dpll, struct nlattr *a,
|
|
+ struct netlink_ext_ack *extack)
|
|
+{
|
|
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
|
|
+ enum dpll_feature_state state = nla_get_u32(a), old_state;
|
|
+ int ret;
|
|
+
|
|
+ if (!(ops->freq_monitor_set && ops->freq_monitor_get)) {
|
|
+ NL_SET_ERR_MSG_ATTR(extack, a,
|
|
+ "dpll device not capable of frequency monitor");
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+ ret = ops->freq_monitor_get(dpll, dpll_priv(dpll), &old_state,
|
|
+ extack);
|
|
+ if (ret) {
|
|
+ NL_SET_ERR_MSG(extack,
|
|
+ "unable to get current state of frequency monitor");
|
|
+ return ret;
|
|
+ }
|
|
+ if (state == old_state)
|
|
+ return 0;
|
|
+
|
|
+ return ops->freq_monitor_set(dpll, dpll_priv(dpll), state, extack);
|
|
+}
|
|
+
|
|
static int
|
|
dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
|
|
struct netlink_ext_ack *extack)
|
|
@@ -1888,6 +1972,12 @@ dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
|
|
if (ret)
|
|
return ret;
|
|
break;
|
|
+ case DPLL_A_FREQUENCY_MONITOR:
|
|
+ ret = dpll_freq_monitor_set(dpll, a,
|
|
+ info->extack);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ break;
|
|
}
|
|
}
|
|
|
|
diff --git a/drivers/dpll/dpll_nl.c b/drivers/dpll/dpll_nl.c
|
|
index 3fb64aa..da92aa8 100644
|
|
--- a/drivers/dpll/dpll_nl.c
|
|
+++ b/drivers/dpll/dpll_nl.c
|
|
@@ -42,11 +42,12 @@ static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_ID + 1] = {
|
|
};
|
|
|
|
/* DPLL_CMD_DEVICE_SET - do */
|
|
-static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_PHASE_OFFSET_AVG_FACTOR + 1] = {
|
|
+static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_FREQUENCY_MONITOR + 1] = {
|
|
[DPLL_A_ID] = { .type = NLA_U32, },
|
|
[DPLL_A_MODE] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
|
|
[DPLL_A_PHASE_OFFSET_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
|
|
[DPLL_A_PHASE_OFFSET_AVG_FACTOR] = { .type = NLA_U32, },
|
|
+ [DPLL_A_FREQUENCY_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
|
|
};
|
|
|
|
/* DPLL_CMD_PIN_ID_GET - do */
|
|
@@ -114,7 +115,7 @@ static const struct genl_split_ops dpll_nl_ops[] = {
|
|
.doit = dpll_nl_device_set_doit,
|
|
.post_doit = dpll_post_doit,
|
|
.policy = dpll_device_set_nl_policy,
|
|
- .maxattr = DPLL_A_PHASE_OFFSET_AVG_FACTOR,
|
|
+ .maxattr = DPLL_A_FREQUENCY_MONITOR,
|
|
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
|
},
|
|
{
|
|
diff --git a/drivers/dpll/zl3073x/Makefile b/drivers/dpll/zl3073x/Makefile
|
|
index bd324c7..906ec3f 100644
|
|
--- a/drivers/dpll/zl3073x/Makefile
|
|
+++ b/drivers/dpll/zl3073x/Makefile
|
|
@@ -1,8 +1,8 @@
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
|
|
obj-$(CONFIG_ZL3073X) += zl3073x.o
|
|
-zl3073x-objs := core.o devlink.o dpll.o flash.o fw.o \
|
|
- out.o prop.o ref.o synth.o
|
|
+zl3073x-objs := chan.o core.o devlink.o dpll.o \
|
|
+ flash.o fw.o out.o prop.o ref.o synth.o
|
|
|
|
obj-$(CONFIG_ZL3073X_I2C) += zl3073x_i2c.o
|
|
zl3073x_i2c-objs := i2c.o
|
|
diff --git a/drivers/dpll/zl3073x/chan.c b/drivers/dpll/zl3073x/chan.c
|
|
new file mode 100644
|
|
index 0000000..2f48ca2
|
|
--- /dev/null
|
|
+++ b/drivers/dpll/zl3073x/chan.c
|
|
@@ -0,0 +1,165 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-only
|
|
+
|
|
+#include <linux/cleanup.h>
|
|
+#include <linux/dev_printk.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/types.h>
|
|
+
|
|
+#include "chan.h"
|
|
+#include "core.h"
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_state_update - update DPLL channel status from HW
|
|
+ * @zldev: pointer to zl3073x_dev structure
|
|
+ * @index: DPLL channel index
|
|
+ *
|
|
+ * Return: 0 on success, <0 on error
|
|
+ */
|
|
+int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index)
|
|
+{
|
|
+ struct zl3073x_chan *chan = &zldev->chan[index];
|
|
+ int rc;
|
|
+
|
|
+ rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(index),
|
|
+ &chan->mon_status);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ return zl3073x_read_u8(zldev, ZL_REG_DPLL_REFSEL_STATUS(index),
|
|
+ &chan->refsel_status);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_state_fetch - fetch DPLL channel state from hardware
|
|
+ * @zldev: pointer to zl3073x_dev structure
|
|
+ * @index: DPLL channel index to fetch state for
|
|
+ *
|
|
+ * Reads the mode_refsel register and reference priority registers for
|
|
+ * the given DPLL channel and stores the raw values for later use.
|
|
+ *
|
|
+ * Return: 0 on success, <0 on error
|
|
+ */
|
|
+int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
|
+{
|
|
+ struct zl3073x_chan *chan = &zldev->chan[index];
|
|
+ int rc, i;
|
|
+
|
|
+ rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
|
|
+ &chan->mode_refsel);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ dev_dbg(zldev->dev, "DPLL%u mode: %u, ref: %u\n", index,
|
|
+ zl3073x_chan_mode_get(chan), zl3073x_chan_ref_get(chan));
|
|
+
|
|
+ rc = zl3073x_chan_state_update(zldev, index);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ dev_dbg(zldev->dev,
|
|
+ "DPLL%u lock_state: %u, ho: %u, sel_state: %u, sel_ref: %u\n",
|
|
+ index, zl3073x_chan_lock_state_get(chan),
|
|
+ zl3073x_chan_is_ho_ready(chan) ? 1 : 0,
|
|
+ zl3073x_chan_refsel_state_get(chan),
|
|
+ zl3073x_chan_refsel_ref_get(chan));
|
|
+
|
|
+ guard(mutex)(&zldev->multiop_lock);
|
|
+
|
|
+ /* Read DPLL configuration from mailbox */
|
|
+ rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
|
|
+ ZL_REG_DPLL_MB_MASK, BIT(index));
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ /* Read reference priority registers */
|
|
+ for (i = 0; i < ARRAY_SIZE(chan->ref_prio); i++) {
|
|
+ rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(i),
|
|
+ &chan->ref_prio[i]);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_state_get - get current DPLL channel state
|
|
+ * @zldev: pointer to zl3073x_dev structure
|
|
+ * @index: DPLL channel index to get state for
|
|
+ *
|
|
+ * Return: pointer to given DPLL channel state
|
|
+ */
|
|
+const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
|
|
+ u8 index)
|
|
+{
|
|
+ return &zldev->chan[index];
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_state_set - commit DPLL channel state changes to hardware
|
|
+ * @zldev: pointer to zl3073x_dev structure
|
|
+ * @index: DPLL channel index to set state for
|
|
+ * @chan: desired channel state
|
|
+ *
|
|
+ * Skips the HW write if the configuration is unchanged, and otherwise
|
|
+ * writes only the changed registers to hardware. The mode_refsel register
|
|
+ * is written directly, while the reference priority registers are written
|
|
+ * via the DPLL mailbox interface.
|
|
+ *
|
|
+ * Return: 0 on success, <0 on HW error
|
|
+ */
|
|
+int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
|
|
+ const struct zl3073x_chan *chan)
|
|
+{
|
|
+ struct zl3073x_chan *dchan = &zldev->chan[index];
|
|
+ int rc, i;
|
|
+
|
|
+ /* Skip HW write if configuration hasn't changed */
|
|
+ if (!memcmp(&dchan->cfg, &chan->cfg, sizeof(chan->cfg)))
|
|
+ return 0;
|
|
+
|
|
+ /* Direct register write for mode_refsel */
|
|
+ if (dchan->mode_refsel != chan->mode_refsel) {
|
|
+ rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
|
|
+ chan->mode_refsel);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+ dchan->mode_refsel = chan->mode_refsel;
|
|
+ }
|
|
+
|
|
+ /* Mailbox write for ref_prio if changed */
|
|
+ if (!memcmp(dchan->ref_prio, chan->ref_prio, sizeof(chan->ref_prio))) {
|
|
+ dchan->cfg = chan->cfg;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ guard(mutex)(&zldev->multiop_lock);
|
|
+
|
|
+ /* Read DPLL configuration into mailbox */
|
|
+ rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
|
|
+ ZL_REG_DPLL_MB_MASK, BIT(index));
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ /* Update changed ref_prio registers */
|
|
+ for (i = 0; i < ARRAY_SIZE(chan->ref_prio); i++) {
|
|
+ if (dchan->ref_prio[i] != chan->ref_prio[i]) {
|
|
+ rc = zl3073x_write_u8(zldev,
|
|
+ ZL_REG_DPLL_REF_PRIO(i),
|
|
+ chan->ref_prio[i]);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Commit DPLL configuration */
|
|
+ rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR,
|
|
+ ZL_REG_DPLL_MB_MASK, BIT(index));
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ /* After successful write store new state */
|
|
+ dchan->cfg = chan->cfg;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/dpll/zl3073x/chan.h b/drivers/dpll/zl3073x/chan.h
|
|
new file mode 100644
|
|
index 0000000..481da21
|
|
--- /dev/null
|
|
+++ b/drivers/dpll/zl3073x/chan.h
|
|
@@ -0,0 +1,174 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-only */
|
|
+
|
|
+#ifndef _ZL3073X_CHAN_H
|
|
+#define _ZL3073X_CHAN_H
|
|
+
|
|
+#include <linux/bitfield.h>
|
|
+#include <linux/stddef.h>
|
|
+#include <linux/types.h>
|
|
+
|
|
+#include "regs.h"
|
|
+
|
|
+struct zl3073x_dev;
|
|
+
|
|
+/**
|
|
+ * struct zl3073x_chan - DPLL channel state
|
|
+ * @mode_refsel: mode and reference selection register value
|
|
+ * @ref_prio: reference priority registers (4 bits per ref, P/N packed)
|
|
+ * @mon_status: monitor status register value
|
|
+ * @refsel_status: reference selection status register value
|
|
+ */
|
|
+struct zl3073x_chan {
|
|
+ struct_group(cfg,
|
|
+ u8 mode_refsel;
|
|
+ u8 ref_prio[ZL3073X_NUM_REFS / 2];
|
|
+ );
|
|
+ struct_group(stat,
|
|
+ u8 mon_status;
|
|
+ u8 refsel_status;
|
|
+ );
|
|
+};
|
|
+
|
|
+int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
|
+const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
|
|
+ u8 index);
|
|
+int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
|
|
+ const struct zl3073x_chan *chan);
|
|
+
|
|
+int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index);
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_mode_get - get DPLL channel operating mode
|
|
+ * @chan: pointer to channel state
|
|
+ *
|
|
+ * Return: reference selection mode of the given DPLL channel
|
|
+ */
|
|
+static inline u8 zl3073x_chan_mode_get(const struct zl3073x_chan *chan)
|
|
+{
|
|
+ return FIELD_GET(ZL_DPLL_MODE_REFSEL_MODE, chan->mode_refsel);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_ref_get - get manually selected reference
|
|
+ * @chan: pointer to channel state
|
|
+ *
|
|
+ * Return: reference selected in forced reference lock mode
|
|
+ */
|
|
+static inline u8 zl3073x_chan_ref_get(const struct zl3073x_chan *chan)
|
|
+{
|
|
+ return FIELD_GET(ZL_DPLL_MODE_REFSEL_REF, chan->mode_refsel);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_mode_set - set DPLL channel operating mode
|
|
+ * @chan: pointer to channel state
|
|
+ * @mode: mode to set
|
|
+ */
|
|
+static inline void zl3073x_chan_mode_set(struct zl3073x_chan *chan, u8 mode)
|
|
+{
|
|
+ FIELD_MODIFY(ZL_DPLL_MODE_REFSEL_MODE, &chan->mode_refsel, mode);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_ref_set - set manually selected reference
|
|
+ * @chan: pointer to channel state
|
|
+ * @ref: reference to set
|
|
+ */
|
|
+static inline void zl3073x_chan_ref_set(struct zl3073x_chan *chan, u8 ref)
|
|
+{
|
|
+ FIELD_MODIFY(ZL_DPLL_MODE_REFSEL_REF, &chan->mode_refsel, ref);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_ref_prio_get - get reference priority
|
|
+ * @chan: pointer to channel state
|
|
+ * @ref: input reference index
|
|
+ *
|
|
+ * Return: priority of the given reference <0, 15>
|
|
+ */
|
|
+static inline u8
|
|
+zl3073x_chan_ref_prio_get(const struct zl3073x_chan *chan, u8 ref)
|
|
+{
|
|
+ u8 val = chan->ref_prio[ref / 2];
|
|
+
|
|
+ if (!(ref & 1))
|
|
+ return FIELD_GET(ZL_DPLL_REF_PRIO_REF_P, val);
|
|
+ else
|
|
+ return FIELD_GET(ZL_DPLL_REF_PRIO_REF_N, val);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_ref_prio_set - set reference priority
|
|
+ * @chan: pointer to channel state
|
|
+ * @ref: input reference index
|
|
+ * @prio: priority to set
|
|
+ */
|
|
+static inline void
|
|
+zl3073x_chan_ref_prio_set(struct zl3073x_chan *chan, u8 ref, u8 prio)
|
|
+{
|
|
+ u8 *val = &chan->ref_prio[ref / 2];
|
|
+
|
|
+ if (!(ref & 1))
|
|
+ FIELD_MODIFY(ZL_DPLL_REF_PRIO_REF_P, val, prio);
|
|
+ else
|
|
+ FIELD_MODIFY(ZL_DPLL_REF_PRIO_REF_N, val, prio);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_ref_is_selectable - check if reference is selectable
|
|
+ * @chan: pointer to channel state
|
|
+ * @ref: input reference index
|
|
+ *
|
|
+ * Return: true if the reference priority is not NONE, false otherwise
|
|
+ */
|
|
+static inline bool
|
|
+zl3073x_chan_ref_is_selectable(const struct zl3073x_chan *chan, u8 ref)
|
|
+{
|
|
+ return zl3073x_chan_ref_prio_get(chan, ref) != ZL_DPLL_REF_PRIO_NONE;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_lock_state_get - get DPLL channel lock state
|
|
+ * @chan: pointer to channel state
|
|
+ *
|
|
+ * Return: lock state of the given DPLL channel
|
|
+ */
|
|
+static inline u8 zl3073x_chan_lock_state_get(const struct zl3073x_chan *chan)
|
|
+{
|
|
+ return FIELD_GET(ZL_DPLL_MON_STATUS_STATE, chan->mon_status);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_is_ho_ready - check if holdover is ready
|
|
+ * @chan: pointer to channel state
|
|
+ *
|
|
+ * Return: true if holdover is ready, false otherwise
|
|
+ */
|
|
+static inline bool zl3073x_chan_is_ho_ready(const struct zl3073x_chan *chan)
|
|
+{
|
|
+ return !!FIELD_GET(ZL_DPLL_MON_STATUS_HO_READY, chan->mon_status);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_refsel_state_get - get reference selection state
|
|
+ * @chan: pointer to channel state
|
|
+ *
|
|
+ * Return: reference selection state of the given DPLL channel
|
|
+ */
|
|
+static inline u8 zl3073x_chan_refsel_state_get(const struct zl3073x_chan *chan)
|
|
+{
|
|
+ return FIELD_GET(ZL_DPLL_REFSEL_STATUS_STATE, chan->refsel_status);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_chan_refsel_ref_get - get currently selected reference in auto mode
|
|
+ * @chan: pointer to channel state
|
|
+ *
|
|
+ * Return: reference selected by the DPLL in automatic mode
|
|
+ */
|
|
+static inline u8 zl3073x_chan_refsel_ref_get(const struct zl3073x_chan *chan)
|
|
+{
|
|
+ return FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, chan->refsel_status);
|
|
+}
|
|
+
|
|
+#endif /* _ZL3073X_CHAN_H */
|
|
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
|
|
index 179fff5..024f0a2 100644
|
|
--- a/drivers/dpll/zl3073x/core.c
|
|
+++ b/drivers/dpll/zl3073x/core.c
|
|
@@ -20,79 +20,30 @@
|
|
#include "dpll.h"
|
|
#include "regs.h"
|
|
|
|
-/* Chip IDs for zl30731 */
|
|
-static const u16 zl30731_ids[] = {
|
|
- 0x0E93,
|
|
- 0x1E93,
|
|
- 0x2E93,
|
|
+#define ZL_CHIP_INFO(_id, _nchannels, _flags) \
|
|
+ { .id = (_id), .num_channels = (_nchannels), .flags = (_flags) }
|
|
+
|
|
+static const struct zl3073x_chip_info zl3073x_chip_ids[] = {
|
|
+ ZL_CHIP_INFO(0x0E30, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
|
+ ZL_CHIP_INFO(0x0E93, 1, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
|
+ ZL_CHIP_INFO(0x0E94, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
|
+ ZL_CHIP_INFO(0x0E95, 3, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
|
+ ZL_CHIP_INFO(0x0E96, 4, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
|
+ ZL_CHIP_INFO(0x0E97, 5, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
|
+ ZL_CHIP_INFO(0x1E93, 1, ZL3073X_FLAG_DIE_TEMP),
|
|
+ ZL_CHIP_INFO(0x1E94, 2, ZL3073X_FLAG_DIE_TEMP),
|
|
+ ZL_CHIP_INFO(0x1E95, 3, ZL3073X_FLAG_DIE_TEMP),
|
|
+ ZL_CHIP_INFO(0x1E96, 4, ZL3073X_FLAG_DIE_TEMP),
|
|
+ ZL_CHIP_INFO(0x1E97, 5, ZL3073X_FLAG_DIE_TEMP),
|
|
+ ZL_CHIP_INFO(0x1F60, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
|
+ ZL_CHIP_INFO(0x2E93, 1, ZL3073X_FLAG_DIE_TEMP),
|
|
+ ZL_CHIP_INFO(0x2E94, 2, ZL3073X_FLAG_DIE_TEMP),
|
|
+ ZL_CHIP_INFO(0x2E95, 3, ZL3073X_FLAG_DIE_TEMP),
|
|
+ ZL_CHIP_INFO(0x2E96, 4, ZL3073X_FLAG_DIE_TEMP),
|
|
+ ZL_CHIP_INFO(0x2E97, 5, ZL3073X_FLAG_DIE_TEMP),
|
|
+ ZL_CHIP_INFO(0x3FC4, 2, ZL3073X_FLAG_DIE_TEMP),
|
|
};
|
|
|
|
-const struct zl3073x_chip_info zl30731_chip_info = {
|
|
- .ids = zl30731_ids,
|
|
- .num_ids = ARRAY_SIZE(zl30731_ids),
|
|
- .num_channels = 1,
|
|
-};
|
|
-EXPORT_SYMBOL_NS_GPL(zl30731_chip_info, ZL3073X);
|
|
-
|
|
-/* Chip IDs for zl30732 */
|
|
-static const u16 zl30732_ids[] = {
|
|
- 0x0E30,
|
|
- 0x0E94,
|
|
- 0x1E94,
|
|
- 0x1F60,
|
|
- 0x2E94,
|
|
- 0x3FC4,
|
|
-};
|
|
-
|
|
-const struct zl3073x_chip_info zl30732_chip_info = {
|
|
- .ids = zl30732_ids,
|
|
- .num_ids = ARRAY_SIZE(zl30732_ids),
|
|
- .num_channels = 2,
|
|
-};
|
|
-EXPORT_SYMBOL_NS_GPL(zl30732_chip_info, ZL3073X);
|
|
-
|
|
-/* Chip IDs for zl30733 */
|
|
-static const u16 zl30733_ids[] = {
|
|
- 0x0E95,
|
|
- 0x1E95,
|
|
- 0x2E95,
|
|
-};
|
|
-
|
|
-const struct zl3073x_chip_info zl30733_chip_info = {
|
|
- .ids = zl30733_ids,
|
|
- .num_ids = ARRAY_SIZE(zl30733_ids),
|
|
- .num_channels = 3,
|
|
-};
|
|
-EXPORT_SYMBOL_NS_GPL(zl30733_chip_info, ZL3073X);
|
|
-
|
|
-/* Chip IDs for zl30734 */
|
|
-static const u16 zl30734_ids[] = {
|
|
- 0x0E96,
|
|
- 0x1E96,
|
|
- 0x2E96,
|
|
-};
|
|
-
|
|
-const struct zl3073x_chip_info zl30734_chip_info = {
|
|
- .ids = zl30734_ids,
|
|
- .num_ids = ARRAY_SIZE(zl30734_ids),
|
|
- .num_channels = 4,
|
|
-};
|
|
-EXPORT_SYMBOL_NS_GPL(zl30734_chip_info, ZL3073X);
|
|
-
|
|
-/* Chip IDs for zl30735 */
|
|
-static const u16 zl30735_ids[] = {
|
|
- 0x0E97,
|
|
- 0x1E97,
|
|
- 0x2E97,
|
|
-};
|
|
-
|
|
-const struct zl3073x_chip_info zl30735_chip_info = {
|
|
- .ids = zl30735_ids,
|
|
- .num_ids = ARRAY_SIZE(zl30735_ids),
|
|
- .num_channels = 5,
|
|
-};
|
|
-EXPORT_SYMBOL_NS_GPL(zl30735_chip_info, ZL3073X);
|
|
-
|
|
#define ZL_RANGE_OFFSET 0x80
|
|
#define ZL_PAGE_SIZE 0x80
|
|
#define ZL_NUM_PAGES 256
|
|
@@ -588,17 +539,26 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
|
|
}
|
|
}
|
|
|
|
+ for (i = 0; i < zldev->info->num_channels; i++) {
|
|
+ rc = zl3073x_chan_state_fetch(zldev, i);
|
|
+ if (rc) {
|
|
+ dev_err(zldev->dev,
|
|
+ "Failed to fetch channel state: %pe\n",
|
|
+ ERR_PTR(rc));
|
|
+ return rc;
|
|
+ }
|
|
+ }
|
|
+
|
|
return rc;
|
|
}
|
|
|
|
static void
|
|
-zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
|
|
+zl3073x_dev_ref_states_update(struct zl3073x_dev *zldev)
|
|
{
|
|
int i, rc;
|
|
|
|
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
|
|
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(i),
|
|
- &zldev->ref[i].mon_status);
|
|
+ rc = zl3073x_ref_state_update(zldev, i);
|
|
if (rc)
|
|
dev_warn(zldev->dev,
|
|
"Failed to get REF%u status: %pe\n", i,
|
|
@@ -606,6 +566,20 @@ zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
|
|
}
|
|
}
|
|
|
|
+static void
|
|
+zl3073x_dev_chan_states_update(struct zl3073x_dev *zldev)
|
|
+{
|
|
+ int i, rc;
|
|
+
|
|
+ for (i = 0; i < zldev->info->num_channels; i++) {
|
|
+ rc = zl3073x_chan_state_update(zldev, i);
|
|
+ if (rc)
|
|
+ dev_warn(zldev->dev,
|
|
+ "Failed to get DPLL%u state: %pe\n", i,
|
|
+ ERR_PTR(rc));
|
|
+ }
|
|
+}
|
|
+
|
|
/**
|
|
* zl3073x_ref_phase_offsets_update - update reference phase offsets
|
|
* @zldev: pointer to zl3073x_dev structure
|
|
@@ -658,22 +632,21 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
|
|
}
|
|
|
|
/**
|
|
- * zl3073x_ref_ffo_update - update reference fractional frequency offsets
|
|
+ * zl3073x_ref_freq_meas_latch - latch reference frequency measurements
|
|
* @zldev: pointer to zl3073x_dev structure
|
|
+ * @type: measurement type (ZL_REF_FREQ_MEAS_CTRL_*)
|
|
*
|
|
- * The function asks device to update fractional frequency offsets latch
|
|
- * registers the latest measured values, reads and stores them into
|
|
+ * The function waits for the previous measurement to finish, selects all
|
|
+ * references and requests a new measurement of the given type.
|
|
*
|
|
* Return: 0 on success, <0 on error
|
|
*/
|
|
static int
|
|
-zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
|
+zl3073x_ref_freq_meas_latch(struct zl3073x_dev *zldev, u8 type)
|
|
{
|
|
- int i, rc;
|
|
+ int rc;
|
|
|
|
- /* Per datasheet we have to wait for 'ref_freq_meas_ctrl' to be zero
|
|
- * to ensure that the measured data are coherent.
|
|
- */
|
|
+ /* Wait for previous measurement to finish */
|
|
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
|
ZL_REF_FREQ_MEAS_CTRL);
|
|
if (rc)
|
|
@@ -689,15 +662,64 @@ zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
|
if (rc)
|
|
return rc;
|
|
|
|
- /* Request frequency offset measurement */
|
|
- rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
|
- ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
|
|
+ /* Request measurement */
|
|
+ rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, type);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Wait for finish */
|
|
- rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
|
- ZL_REF_FREQ_MEAS_CTRL);
|
|
+ return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
|
+ ZL_REF_FREQ_MEAS_CTRL);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_ref_freq_meas_update - update measured input reference frequencies
|
|
+ * @zldev: pointer to zl3073x_dev structure
|
|
+ *
|
|
+ * The function asks device to latch measured input reference frequencies
|
|
+ * and stores the results in the ref state.
|
|
+ *
|
|
+ * Return: 0 on success, <0 on error
|
|
+ */
|
|
+static int
|
|
+zl3073x_ref_freq_meas_update(struct zl3073x_dev *zldev)
|
|
+{
|
|
+ int i, rc;
|
|
+
|
|
+ rc = zl3073x_ref_freq_meas_latch(zldev, ZL_REF_FREQ_MEAS_CTRL_REF_FREQ);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ /* Read measured frequencies in Hz (unsigned 32-bit, LSB = 1 Hz) */
|
|
+ for (i = 0; i < ZL3073X_NUM_REFS; i++) {
|
|
+ u32 value;
|
|
+
|
|
+ rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ zldev->ref[i].meas_freq = value;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_ref_ffo_update - update reference fractional frequency offsets
|
|
+ * @zldev: pointer to zl3073x_dev structure
|
|
+ *
|
|
+ * The function asks device to latch the latest measured fractional
|
|
+ * frequency offset values, reads and stores them into the ref state.
|
|
+ *
|
|
+ * Return: 0 on success, <0 on error
|
|
+ */
|
|
+static int
|
|
+zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
|
+{
|
|
+ int i, rc;
|
|
+
|
|
+ rc = zl3073x_ref_freq_meas_latch(zldev,
|
|
+ ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
|
|
if (rc)
|
|
return rc;
|
|
|
|
@@ -728,8 +750,11 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
|
|
struct zl3073x_dpll *zldpll;
|
|
int rc;
|
|
|
|
- /* Update input references status */
|
|
- zl3073x_dev_ref_status_update(zldev);
|
|
+ /* Update input references' states */
|
|
+ zl3073x_dev_ref_states_update(zldev);
|
|
+
|
|
+ /* Update DPLL channels' states */
|
|
+ zl3073x_dev_chan_states_update(zldev);
|
|
|
|
/* Update DPLL-to-connected-ref phase offsets registers */
|
|
rc = zl3073x_ref_phase_offsets_update(zldev, -1);
|
|
@@ -737,6 +762,20 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
|
|
dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n",
|
|
ERR_PTR(rc));
|
|
|
|
+ /* Update measured input reference frequencies if any DPLL has
|
|
+ * frequency monitoring enabled.
|
|
+ */
|
|
+ list_for_each_entry(zldpll, &zldev->dplls, list) {
|
|
+ if (zldpll->freq_monitor) {
|
|
+ rc = zl3073x_ref_freq_meas_update(zldev);
|
|
+ if (rc)
|
|
+ dev_warn(zldev->dev,
|
|
+ "Failed to update measured frequencies: %pe\n",
|
|
+ ERR_PTR(rc));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
/* Update references' fractional frequency offsets */
|
|
rc = zl3073x_ref_ffo_update(zldev);
|
|
if (rc)
|
|
@@ -766,8 +805,7 @@ int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor)
|
|
value = (factor + 1) & 0x0f;
|
|
|
|
/* Update phase measurement control register */
|
|
- dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
|
|
- dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, value);
|
|
+ FIELD_MODIFY(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, &dpll_meas_ctrl, value);
|
|
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, dpll_meas_ctrl);
|
|
if (rc)
|
|
return rc;
|
|
@@ -942,7 +980,7 @@ static void zl3073x_dev_dpll_fini(void *ptr)
|
|
}
|
|
|
|
static int
|
|
-zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
|
|
+zl3073x_devm_dpll_init(struct zl3073x_dev *zldev)
|
|
{
|
|
struct kthread_worker *kworker;
|
|
struct zl3073x_dpll *zldpll;
|
|
@@ -952,7 +990,7 @@ zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
|
|
INIT_LIST_HEAD(&zldev->dplls);
|
|
|
|
/* Allocate all DPLLs */
|
|
- for (i = 0; i < num_dplls; i++) {
|
|
+ for (i = 0; i < zldev->info->num_channels; i++) {
|
|
zldpll = zl3073x_dpll_alloc(zldev, i);
|
|
if (IS_ERR(zldpll)) {
|
|
dev_err_probe(zldev->dev, PTR_ERR(zldpll),
|
|
@@ -994,14 +1032,12 @@ zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
|
|
/**
|
|
* zl3073x_dev_probe - initialize zl3073x device
|
|
* @zldev: pointer to zl3073x device
|
|
- * @chip_info: chip info based on compatible
|
|
*
|
|
* Common initialization of zl3073x device structure.
|
|
*
|
|
* Returns: 0 on success, <0 on error
|
|
*/
|
|
-int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
|
- const struct zl3073x_chip_info *chip_info)
|
|
+int zl3073x_dev_probe(struct zl3073x_dev *zldev)
|
|
{
|
|
u16 id, revision, fw_ver;
|
|
unsigned int i;
|
|
@@ -1013,18 +1049,17 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
|
if (rc)
|
|
return rc;
|
|
|
|
- /* Check it matches */
|
|
- for (i = 0; i < chip_info->num_ids; i++) {
|
|
- if (id == chip_info->ids[i])
|
|
+ /* Detect chip variant */
|
|
+ for (i = 0; i < ARRAY_SIZE(zl3073x_chip_ids); i++) {
|
|
+ if (zl3073x_chip_ids[i].id == id)
|
|
break;
|
|
}
|
|
|
|
- if (i == chip_info->num_ids) {
|
|
+ if (i == ARRAY_SIZE(zl3073x_chip_ids))
|
|
return dev_err_probe(zldev->dev, -ENODEV,
|
|
- "Unknown or non-match chip ID: 0x%0x\n",
|
|
- id);
|
|
- }
|
|
- zldev->chip_id = id;
|
|
+ "Unknown chip ID: 0x%04x\n", id);
|
|
+
|
|
+ zldev->info = &zl3073x_chip_ids[i];
|
|
|
|
/* Read revision, firmware version and custom config version */
|
|
rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);
|
|
@@ -1063,7 +1098,7 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
|
"Failed to initialize mutex\n");
|
|
|
|
/* Register DPLL channels */
|
|
- rc = zl3073x_devm_dpll_init(zldev, chip_info->num_channels);
|
|
+ rc = zl3073x_devm_dpll_init(zldev);
|
|
if (rc)
|
|
return rc;
|
|
|
|
diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h
|
|
index fd2af3c..9944062 100644
|
|
--- a/drivers/dpll/zl3073x/core.h
|
|
+++ b/drivers/dpll/zl3073x/core.h
|
|
@@ -9,6 +9,7 @@
|
|
#include <linux/mutex.h>
|
|
#include <linux/types.h>
|
|
|
|
+#include "chan.h"
|
|
#include "out.h"
|
|
#include "ref.h"
|
|
#include "regs.h"
|
|
@@ -18,27 +19,39 @@ struct device;
|
|
struct regmap;
|
|
struct zl3073x_dpll;
|
|
|
|
-/*
|
|
- * Hardware limits for ZL3073x chip family
|
|
+
|
|
+enum zl3073x_flags {
|
|
+ ZL3073X_FLAG_REF_PHASE_COMP_32_BIT,
|
|
+ ZL3073X_FLAG_DIE_TEMP_BIT,
|
|
+ ZL3073X_FLAGS_NBITS /* must be last */
|
|
+};
|
|
+
|
|
+#define __ZL3073X_FLAG(name) BIT(ZL3073X_FLAG_ ## name ## _BIT)
|
|
+#define ZL3073X_FLAG_REF_PHASE_COMP_32 __ZL3073X_FLAG(REF_PHASE_COMP_32)
|
|
+#define ZL3073X_FLAG_DIE_TEMP __ZL3073X_FLAG(DIE_TEMP)
|
|
+
|
|
+/**
|
|
+ * struct zl3073x_chip_info - chip variant identification
|
|
+ * @id: chip ID
|
|
+ * @num_channels: number of DPLL channels supported by this variant
|
|
+ * @flags: chip variant flags
|
|
*/
|
|
-#define ZL3073X_MAX_CHANNELS 5
|
|
-#define ZL3073X_NUM_REFS 10
|
|
-#define ZL3073X_NUM_OUTS 10
|
|
-#define ZL3073X_NUM_SYNTHS 5
|
|
-#define ZL3073X_NUM_INPUT_PINS ZL3073X_NUM_REFS
|
|
-#define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2)
|
|
-#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \
|
|
- ZL3073X_NUM_OUTPUT_PINS)
|
|
+struct zl3073x_chip_info {
|
|
+ u16 id;
|
|
+ u8 num_channels;
|
|
+ unsigned long flags;
|
|
+};
|
|
|
|
/**
|
|
* struct zl3073x_dev - zl3073x device
|
|
* @dev: pointer to device
|
|
* @regmap: regmap to access device registers
|
|
+ * @info: detected chip info
|
|
* @multiop_lock: to serialize multiple register operations
|
|
- * @chip_id: chip ID read from hardware
|
|
* @ref: array of input references' invariants
|
|
* @out: array of outs' invariants
|
|
* @synth: array of synths' invariants
|
|
+ * @chan: array of DPLL channels' state
|
|
* @dplls: list of DPLLs
|
|
* @kworker: thread for periodic work
|
|
* @work: periodic work
|
|
@@ -46,15 +59,16 @@ struct zl3073x_dpll;
|
|
* @phase_avg_factor: phase offset measurement averaging factor
|
|
*/
|
|
struct zl3073x_dev {
|
|
- struct device *dev;
|
|
- struct regmap *regmap;
|
|
- struct mutex multiop_lock;
|
|
- u16 chip_id;
|
|
+ struct device *dev;
|
|
+ struct regmap *regmap;
|
|
+ const struct zl3073x_chip_info *info;
|
|
+ struct mutex multiop_lock;
|
|
|
|
/* Invariants */
|
|
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
|
|
struct zl3073x_out out[ZL3073X_NUM_OUTS];
|
|
struct zl3073x_synth synth[ZL3073X_NUM_SYNTHS];
|
|
+ struct zl3073x_chan chan[ZL3073X_MAX_CHANNELS];
|
|
|
|
/* DPLL channels */
|
|
struct list_head dplls;
|
|
@@ -68,22 +82,10 @@ struct zl3073x_dev {
|
|
u8 phase_avg_factor;
|
|
};
|
|
|
|
-struct zl3073x_chip_info {
|
|
- const u16 *ids;
|
|
- size_t num_ids;
|
|
- int num_channels;
|
|
-};
|
|
-
|
|
-extern const struct zl3073x_chip_info zl30731_chip_info;
|
|
-extern const struct zl3073x_chip_info zl30732_chip_info;
|
|
-extern const struct zl3073x_chip_info zl30733_chip_info;
|
|
-extern const struct zl3073x_chip_info zl30734_chip_info;
|
|
-extern const struct zl3073x_chip_info zl30735_chip_info;
|
|
extern const struct regmap_config zl3073x_regmap_config;
|
|
|
|
struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev);
|
|
-int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
|
- const struct zl3073x_chip_info *chip_info);
|
|
+int zl3073x_dev_probe(struct zl3073x_dev *zldev);
|
|
|
|
int zl3073x_dev_start(struct zl3073x_dev *zldev, bool full);
|
|
void zl3073x_dev_stop(struct zl3073x_dev *zldev);
|
|
@@ -158,18 +160,7 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel);
|
|
static inline bool
|
|
zl3073x_dev_is_ref_phase_comp_32bit(struct zl3073x_dev *zldev)
|
|
{
|
|
- switch (zldev->chip_id) {
|
|
- case 0x0E30:
|
|
- case 0x0E93:
|
|
- case 0x0E94:
|
|
- case 0x0E95:
|
|
- case 0x0E96:
|
|
- case 0x0E97:
|
|
- case 0x1F60:
|
|
- return true;
|
|
- default:
|
|
- return false;
|
|
- }
|
|
+ return zldev->info->flags & ZL3073X_FLAG_REF_PHASE_COMP_32;
|
|
}
|
|
|
|
static inline bool
|
|
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
|
index 8ffbede..55195dc 100644
|
|
--- a/drivers/dpll/zl3073x/dpll.c
|
|
+++ b/drivers/dpll/zl3073x/dpll.c
|
|
@@ -13,6 +13,7 @@
|
|
#include <linux/module.h>
|
|
#include <linux/netlink.h>
|
|
#include <linux/platform_device.h>
|
|
+#include <linux/property.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sprintf.h>
|
|
|
|
@@ -30,32 +31,34 @@
|
|
* @dpll: DPLL the pin is registered to
|
|
* @dpll_pin: pointer to registered dpll_pin
|
|
* @tracker: tracking object for the acquired reference
|
|
+ * @fwnode: firmware node handle
|
|
* @label: package label
|
|
* @dir: pin direction
|
|
* @id: pin id
|
|
* @prio: pin priority <0, 14>
|
|
- * @selectable: pin is selectable in automatic mode
|
|
* @esync_control: embedded sync is controllable
|
|
* @phase_gran: phase adjustment granularity
|
|
* @pin_state: last saved pin state
|
|
* @phase_offset: last saved pin phase offset
|
|
* @freq_offset: last saved fractional frequency offset
|
|
+ * @measured_freq: last saved measured frequency
|
|
*/
|
|
struct zl3073x_dpll_pin {
|
|
struct list_head list;
|
|
struct zl3073x_dpll *dpll;
|
|
struct dpll_pin *dpll_pin;
|
|
dpll_tracker tracker;
|
|
+ struct fwnode_handle *fwnode;
|
|
char label[8];
|
|
enum dpll_pin_direction dir;
|
|
u8 id;
|
|
u8 prio;
|
|
- bool selectable;
|
|
bool esync_control;
|
|
s32 phase_gran;
|
|
enum dpll_pin_state pin_state;
|
|
s64 phase_offset;
|
|
s64 freq_offset;
|
|
+ u32 measured_freq;
|
|
};
|
|
|
|
/*
|
|
@@ -133,7 +136,13 @@ zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin,
|
|
ref_id = zl3073x_input_pin_ref_get(pin->id);
|
|
ref = zl3073x_ref_state_get(zldev, ref_id);
|
|
|
|
- switch (FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref->sync_ctrl)) {
|
|
+ if (!pin->esync_control || zl3073x_ref_freq_get(ref) <= 1)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ esync->range = esync_freq_ranges;
|
|
+ esync->range_num = ARRAY_SIZE(esync_freq_ranges);
|
|
+
|
|
+ switch (zl3073x_ref_sync_mode_get(ref)) {
|
|
case ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75:
|
|
esync->freq = ref->esync_n_div == ZL_REF_ESYNC_DIV_1HZ ? 1 : 0;
|
|
esync->pulse = 25;
|
|
@@ -144,17 +153,6 @@ zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin,
|
|
break;
|
|
}
|
|
|
|
- /* If the pin supports esync control expose its range but only
|
|
- * if the current reference frequency is > 1 Hz.
|
|
- */
|
|
- if (pin->esync_control && zl3073x_ref_freq_get(ref) > 1) {
|
|
- esync->range = esync_freq_ranges;
|
|
- esync->range_num = ARRAY_SIZE(esync_freq_ranges);
|
|
- } else {
|
|
- esync->range = NULL;
|
|
- esync->range_num = 0;
|
|
- }
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
@@ -180,8 +178,7 @@ zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin,
|
|
else
|
|
sync_mode = ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75;
|
|
|
|
- ref.sync_ctrl &= ~ZL_REF_SYNC_CTRL_MODE;
|
|
- ref.sync_ctrl |= FIELD_PREP(ZL_REF_SYNC_CTRL_MODE, sync_mode);
|
|
+ zl3073x_ref_sync_mode_set(&ref, sync_mode);
|
|
|
|
if (freq) {
|
|
/* 1 Hz is only supported frequency now */
|
|
@@ -192,6 +189,109 @@ zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin,
|
|
return zl3073x_ref_state_set(zldev, ref_id, &ref);
|
|
}
|
|
|
|
+static int
|
|
+zl3073x_dpll_input_pin_ref_sync_get(const struct dpll_pin *dpll_pin,
|
|
+ void *pin_priv,
|
|
+ const struct dpll_pin *ref_sync_pin,
|
|
+ void *ref_sync_pin_priv,
|
|
+ enum dpll_pin_state *state,
|
|
+ struct netlink_ext_ack *extack)
|
|
+{
|
|
+ struct zl3073x_dpll_pin *sync_pin = ref_sync_pin_priv;
|
|
+ struct zl3073x_dpll_pin *pin = pin_priv;
|
|
+ struct zl3073x_dpll *zldpll = pin->dpll;
|
|
+ struct zl3073x_dev *zldev = zldpll->dev;
|
|
+ const struct zl3073x_ref *ref;
|
|
+ u8 ref_id, mode, pair;
|
|
+
|
|
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
|
|
+ ref = zl3073x_ref_state_get(zldev, ref_id);
|
|
+ mode = zl3073x_ref_sync_mode_get(ref);
|
|
+ pair = zl3073x_ref_sync_pair_get(ref);
|
|
+
|
|
+ if (mode == ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR &&
|
|
+ pair == zl3073x_input_pin_ref_get(sync_pin->id))
|
|
+ *state = DPLL_PIN_STATE_CONNECTED;
|
|
+ else
|
|
+ *state = DPLL_PIN_STATE_DISCONNECTED;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+zl3073x_dpll_input_pin_ref_sync_set(const struct dpll_pin *dpll_pin,
|
|
+ void *pin_priv,
|
|
+ const struct dpll_pin *ref_sync_pin,
|
|
+ void *ref_sync_pin_priv,
|
|
+ const enum dpll_pin_state state,
|
|
+ struct netlink_ext_ack *extack)
|
|
+{
|
|
+ struct zl3073x_dpll_pin *sync_pin = ref_sync_pin_priv;
|
|
+ struct zl3073x_dpll_pin *pin = pin_priv;
|
|
+ struct zl3073x_dpll *zldpll = pin->dpll;
|
|
+ struct zl3073x_dev *zldev = zldpll->dev;
|
|
+ u8 mode, ref_id, sync_ref_id;
|
|
+ struct zl3073x_chan chan;
|
|
+ struct zl3073x_ref ref;
|
|
+ int rc;
|
|
+
|
|
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
|
|
+ sync_ref_id = zl3073x_input_pin_ref_get(sync_pin->id);
|
|
+ ref = *zl3073x_ref_state_get(zldev, ref_id);
|
|
+
|
|
+ if (state == DPLL_PIN_STATE_CONNECTED) {
|
|
+ const struct zl3073x_ref *sync_ref;
|
|
+ u32 ref_freq, sync_freq;
|
|
+
|
|
+ sync_ref = zl3073x_ref_state_get(zldev, sync_ref_id);
|
|
+ ref_freq = zl3073x_ref_freq_get(&ref);
|
|
+ sync_freq = zl3073x_ref_freq_get(sync_ref);
|
|
+
|
|
+ /* Sync signal must be 8 kHz or less and clock reference
|
|
+ * must be 1 kHz or more and higher than the sync signal.
|
|
+ */
|
|
+ if (sync_freq > 8000) {
|
|
+ NL_SET_ERR_MSG(extack,
|
|
+ "sync frequency must be 8 kHz or less");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (ref_freq < 1000) {
|
|
+ NL_SET_ERR_MSG(extack,
|
|
+ "clock frequency must be 1 kHz or more");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ if (ref_freq <= sync_freq) {
|
|
+ NL_SET_ERR_MSG(extack,
|
|
+ "clock frequency must be higher than sync frequency");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ zl3073x_ref_sync_pair_set(&ref, sync_ref_id);
|
|
+ mode = ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR;
|
|
+ } else {
|
|
+ mode = ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF;
|
|
+ }
|
|
+
|
|
+ zl3073x_ref_sync_mode_set(&ref, mode);
|
|
+
|
|
+ rc = zl3073x_ref_state_set(zldev, ref_id, &ref);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ /* Exclude sync source from automatic reference selection by setting
|
|
+ * its priority to NONE. On disconnect the priority is left as NONE
|
|
+ * and the user must explicitly make the pin selectable again.
|
|
+ */
|
|
+ if (state == DPLL_PIN_STATE_CONNECTED) {
|
|
+ chan = *zl3073x_chan_state_get(zldev, zldpll->id);
|
|
+ zl3073x_chan_ref_prio_set(&chan, sync_ref_id,
|
|
+ ZL_DPLL_REF_PRIO_NONE);
|
|
+ return zl3073x_chan_state_set(zldev, zldpll->id, &chan);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int
|
|
zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv,
|
|
const struct dpll_device *dpll, void *dpll_priv,
|
|
@@ -204,6 +304,21 @@ zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv,
|
|
return 0;
|
|
}
|
|
|
|
+static int
|
|
+zl3073x_dpll_input_pin_measured_freq_get(const struct dpll_pin *dpll_pin,
|
|
+ void *pin_priv,
|
|
+ const struct dpll_device *dpll,
|
|
+ void *dpll_priv, u64 *measured_freq,
|
|
+ struct netlink_ext_ack *extack)
|
|
+{
|
|
+ struct zl3073x_dpll_pin *pin = pin_priv;
|
|
+
|
|
+ *measured_freq = pin->measured_freq;
|
|
+ *measured_freq *= DPLL_PIN_MEASURED_FREQUENCY_DIVIDER;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int
|
|
zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin,
|
|
void *pin_priv,
|
|
@@ -245,156 +360,27 @@ zl3073x_dpll_input_pin_frequency_set(const struct dpll_pin *dpll_pin,
|
|
return zl3073x_ref_state_set(zldev, ref_id, &ref);
|
|
}
|
|
|
|
-/**
|
|
- * zl3073x_dpll_selected_ref_get - get currently selected reference
|
|
- * @zldpll: pointer to zl3073x_dpll
|
|
- * @ref: place to store selected reference
|
|
- *
|
|
- * Check for currently selected reference the DPLL should be locked to
|
|
- * and stores its index to given @ref.
|
|
- *
|
|
- * Return: 0 on success, <0 on error
|
|
- */
|
|
-static int
|
|
-zl3073x_dpll_selected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
|
|
-{
|
|
- struct zl3073x_dev *zldev = zldpll->dev;
|
|
- u8 state, value;
|
|
- int rc;
|
|
-
|
|
- switch (zldpll->refsel_mode) {
|
|
- case ZL_DPLL_MODE_REFSEL_MODE_AUTO:
|
|
- /* For automatic mode read refsel_status register */
|
|
- rc = zl3073x_read_u8(zldev,
|
|
- ZL_REG_DPLL_REFSEL_STATUS(zldpll->id),
|
|
- &value);
|
|
- if (rc)
|
|
- return rc;
|
|
-
|
|
- /* Extract reference state */
|
|
- state = FIELD_GET(ZL_DPLL_REFSEL_STATUS_STATE, value);
|
|
-
|
|
- /* Return the reference only if the DPLL is locked to it */
|
|
- if (state == ZL_DPLL_REFSEL_STATUS_STATE_LOCK)
|
|
- *ref = FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, value);
|
|
- else
|
|
- *ref = ZL3073X_DPLL_REF_NONE;
|
|
- break;
|
|
- case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
|
|
- /* For manual mode return stored value */
|
|
- *ref = zldpll->forced_ref;
|
|
- break;
|
|
- default:
|
|
- /* For other modes like NCO, freerun... there is no input ref */
|
|
- *ref = ZL3073X_DPLL_REF_NONE;
|
|
- break;
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-/**
|
|
- * zl3073x_dpll_selected_ref_set - select reference in manual mode
|
|
- * @zldpll: pointer to zl3073x_dpll
|
|
- * @ref: input reference to be selected
|
|
- *
|
|
- * Selects the given reference for the DPLL channel it should be
|
|
- * locked to.
|
|
- *
|
|
- * Return: 0 on success, <0 on error
|
|
- */
|
|
-static int
|
|
-zl3073x_dpll_selected_ref_set(struct zl3073x_dpll *zldpll, u8 ref)
|
|
-{
|
|
- struct zl3073x_dev *zldev = zldpll->dev;
|
|
- u8 mode, mode_refsel;
|
|
- int rc;
|
|
-
|
|
- mode = zldpll->refsel_mode;
|
|
-
|
|
- switch (mode) {
|
|
- case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
|
|
- /* Manual mode with ref selected */
|
|
- if (ref == ZL3073X_DPLL_REF_NONE) {
|
|
- switch (zldpll->lock_status) {
|
|
- case DPLL_LOCK_STATUS_LOCKED_HO_ACQ:
|
|
- case DPLL_LOCK_STATUS_HOLDOVER:
|
|
- /* Switch to forced holdover */
|
|
- mode = ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER;
|
|
- break;
|
|
- default:
|
|
- /* Switch to freerun */
|
|
- mode = ZL_DPLL_MODE_REFSEL_MODE_FREERUN;
|
|
- break;
|
|
- }
|
|
- /* Keep selected reference */
|
|
- ref = zldpll->forced_ref;
|
|
- } else if (ref == zldpll->forced_ref) {
|
|
- /* No register update - same mode and same ref */
|
|
- return 0;
|
|
- }
|
|
- break;
|
|
- case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
|
|
- case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER:
|
|
- /* Manual mode without no ref */
|
|
- if (ref == ZL3073X_DPLL_REF_NONE)
|
|
- /* No register update - keep current mode */
|
|
- return 0;
|
|
-
|
|
- /* Switch to reflock mode and update ref selection */
|
|
- mode = ZL_DPLL_MODE_REFSEL_MODE_REFLOCK;
|
|
- break;
|
|
- default:
|
|
- /* For other modes like automatic or NCO ref cannot be selected
|
|
- * manually
|
|
- */
|
|
- return -EOPNOTSUPP;
|
|
- }
|
|
-
|
|
- /* Build mode_refsel value */
|
|
- mode_refsel = FIELD_PREP(ZL_DPLL_MODE_REFSEL_MODE, mode) |
|
|
- FIELD_PREP(ZL_DPLL_MODE_REFSEL_REF, ref);
|
|
-
|
|
- /* Update dpll_mode_refsel register */
|
|
- rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(zldpll->id),
|
|
- mode_refsel);
|
|
- if (rc)
|
|
- return rc;
|
|
-
|
|
- /* Store new mode and forced reference */
|
|
- zldpll->refsel_mode = mode;
|
|
- zldpll->forced_ref = ref;
|
|
-
|
|
- return rc;
|
|
-}
|
|
-
|
|
/**
|
|
* zl3073x_dpll_connected_ref_get - get currently connected reference
|
|
* @zldpll: pointer to zl3073x_dpll
|
|
- * @ref: place to store selected reference
|
|
*
|
|
- * Looks for currently connected the DPLL is locked to and stores its index
|
|
- * to given @ref.
|
|
+ * Looks for currently connected reference the DPLL is locked to.
|
|
*
|
|
- * Return: 0 on success, <0 on error
|
|
+ * Return: reference index if locked, ZL3073X_DPLL_REF_NONE otherwise
|
|
*/
|
|
-static int
|
|
-zl3073x_dpll_connected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
|
|
+static u8
|
|
+zl3073x_dpll_connected_ref_get(struct zl3073x_dpll *zldpll)
|
|
{
|
|
- struct zl3073x_dev *zldev = zldpll->dev;
|
|
- int rc;
|
|
-
|
|
- /* Get currently selected input reference */
|
|
- rc = zl3073x_dpll_selected_ref_get(zldpll, ref);
|
|
- if (rc)
|
|
- return rc;
|
|
+ const struct zl3073x_chan *chan = zl3073x_chan_state_get(zldpll->dev,
|
|
+ zldpll->id);
|
|
+ u8 state;
|
|
|
|
- /* If the monitor indicates an error nothing is connected */
|
|
- if (ZL3073X_DPLL_REF_IS_VALID(*ref) &&
|
|
- !zl3073x_dev_ref_is_status_ok(zldev, *ref))
|
|
- *ref = ZL3073X_DPLL_REF_NONE;
|
|
+ /* A reference is connected only when the DPLL is locked to it */
|
|
+ state = zl3073x_chan_refsel_state_get(chan);
|
|
+ if (state == ZL_DPLL_REFSEL_STATUS_STATE_LOCK)
|
|
+ return zl3073x_chan_refsel_ref_get(chan);
|
|
|
|
- return 0;
|
|
+ return ZL3073X_DPLL_REF_NONE;
|
|
}
|
|
|
|
static int
|
|
@@ -410,12 +396,9 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,
|
|
const struct zl3073x_ref *ref;
|
|
u8 conn_id, ref_id;
|
|
s64 ref_phase;
|
|
- int rc;
|
|
|
|
/* Get currently connected reference */
|
|
- rc = zl3073x_dpll_connected_ref_get(zldpll, &conn_id);
|
|
- if (rc)
|
|
- return rc;
|
|
+ conn_id = zl3073x_dpll_connected_ref_get(zldpll);
|
|
|
|
/* Report phase offset only for currently connected pin if the phase
|
|
* monitor feature is disabled and only if the input pin signal is
|
|
@@ -453,7 +436,7 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,
|
|
|
|
*phase_offset = ref_phase * DPLL_PHASE_OFFSET_DIVIDER;
|
|
|
|
- return rc;
|
|
+ return 0;
|
|
}
|
|
|
|
static int
|
|
@@ -516,98 +499,6 @@ zl3073x_dpll_input_pin_phase_adjust_set(const struct dpll_pin *dpll_pin,
|
|
return zl3073x_ref_state_set(zldev, ref_id, &ref);
|
|
}
|
|
|
|
-/**
|
|
- * zl3073x_dpll_ref_prio_get - get priority for given input pin
|
|
- * @pin: pointer to pin
|
|
- * @prio: place to store priority
|
|
- *
|
|
- * Reads current priority for the given input pin and stores the value
|
|
- * to @prio.
|
|
- *
|
|
- * Return: 0 on success, <0 on error
|
|
- */
|
|
-static int
|
|
-zl3073x_dpll_ref_prio_get(struct zl3073x_dpll_pin *pin, u8 *prio)
|
|
-{
|
|
- struct zl3073x_dpll *zldpll = pin->dpll;
|
|
- struct zl3073x_dev *zldev = zldpll->dev;
|
|
- u8 ref, ref_prio;
|
|
- int rc;
|
|
-
|
|
- guard(mutex)(&zldev->multiop_lock);
|
|
-
|
|
- /* Read DPLL configuration */
|
|
- rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
|
|
- ZL_REG_DPLL_MB_MASK, BIT(zldpll->id));
|
|
- if (rc)
|
|
- return rc;
|
|
-
|
|
- /* Read reference priority - one value for P&N pins (4 bits/pin) */
|
|
- ref = zl3073x_input_pin_ref_get(pin->id);
|
|
- rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2),
|
|
- &ref_prio);
|
|
- if (rc)
|
|
- return rc;
|
|
-
|
|
- /* Select nibble according pin type */
|
|
- if (zl3073x_dpll_is_p_pin(pin))
|
|
- *prio = FIELD_GET(ZL_DPLL_REF_PRIO_REF_P, ref_prio);
|
|
- else
|
|
- *prio = FIELD_GET(ZL_DPLL_REF_PRIO_REF_N, ref_prio);
|
|
-
|
|
- return rc;
|
|
-}
|
|
-
|
|
-/**
|
|
- * zl3073x_dpll_ref_prio_set - set priority for given input pin
|
|
- * @pin: pointer to pin
|
|
- * @prio: place to store priority
|
|
- *
|
|
- * Sets priority for the given input pin.
|
|
- *
|
|
- * Return: 0 on success, <0 on error
|
|
- */
|
|
-static int
|
|
-zl3073x_dpll_ref_prio_set(struct zl3073x_dpll_pin *pin, u8 prio)
|
|
-{
|
|
- struct zl3073x_dpll *zldpll = pin->dpll;
|
|
- struct zl3073x_dev *zldev = zldpll->dev;
|
|
- u8 ref, ref_prio;
|
|
- int rc;
|
|
-
|
|
- guard(mutex)(&zldev->multiop_lock);
|
|
-
|
|
- /* Read DPLL configuration into mailbox */
|
|
- rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
|
|
- ZL_REG_DPLL_MB_MASK, BIT(zldpll->id));
|
|
- if (rc)
|
|
- return rc;
|
|
-
|
|
- /* Read reference priority - one value shared between P&N pins */
|
|
- ref = zl3073x_input_pin_ref_get(pin->id);
|
|
- rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2), &ref_prio);
|
|
- if (rc)
|
|
- return rc;
|
|
-
|
|
- /* Update nibble according pin type */
|
|
- if (zl3073x_dpll_is_p_pin(pin)) {
|
|
- ref_prio &= ~ZL_DPLL_REF_PRIO_REF_P;
|
|
- ref_prio |= FIELD_PREP(ZL_DPLL_REF_PRIO_REF_P, prio);
|
|
- } else {
|
|
- ref_prio &= ~ZL_DPLL_REF_PRIO_REF_N;
|
|
- ref_prio |= FIELD_PREP(ZL_DPLL_REF_PRIO_REF_N, prio);
|
|
- }
|
|
-
|
|
- /* Update reference priority */
|
|
- rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2), ref_prio);
|
|
- if (rc)
|
|
- return rc;
|
|
-
|
|
- /* Commit configuration */
|
|
- return zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR,
|
|
- ZL_REG_DPLL_MB_MASK, BIT(zldpll->id));
|
|
-}
|
|
-
|
|
/**
|
|
* zl3073x_dpll_ref_state_get - get status for given input pin
|
|
* @pin: pointer to pin
|
|
@@ -624,17 +515,14 @@ zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin,
|
|
{
|
|
struct zl3073x_dpll *zldpll = pin->dpll;
|
|
struct zl3073x_dev *zldev = zldpll->dev;
|
|
- u8 ref, ref_conn;
|
|
- int rc;
|
|
+ const struct zl3073x_chan *chan;
|
|
+ u8 ref;
|
|
|
|
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
|
ref = zl3073x_input_pin_ref_get(pin->id);
|
|
|
|
- /* Get currently connected reference */
|
|
- rc = zl3073x_dpll_connected_ref_get(zldpll, &ref_conn);
|
|
- if (rc)
|
|
- return rc;
|
|
-
|
|
- if (ref == ref_conn) {
|
|
+ /* Check if the pin reference is connected */
|
|
+ if (ref == zl3073x_dpll_connected_ref_get(zldpll)) {
|
|
*state = DPLL_PIN_STATE_CONNECTED;
|
|
return 0;
|
|
}
|
|
@@ -643,8 +531,9 @@ zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin,
|
|
* selectable and its monitor does not report any error then report
|
|
* pin as selectable.
|
|
*/
|
|
- if (zldpll->refsel_mode == ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
|
|
- zl3073x_dev_ref_is_status_ok(zldev, ref) && pin->selectable) {
|
|
+ if (zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
|
|
+ zl3073x_dev_ref_is_status_ok(zldev, ref) &&
|
|
+ zl3073x_chan_ref_is_selectable(chan, ref)) {
|
|
*state = DPLL_PIN_STATE_SELECTABLE;
|
|
return 0;
|
|
}
|
|
@@ -678,68 +567,81 @@ zl3073x_dpll_input_pin_state_on_dpll_set(const struct dpll_pin *dpll_pin,
|
|
{
|
|
struct zl3073x_dpll *zldpll = dpll_priv;
|
|
struct zl3073x_dpll_pin *pin = pin_priv;
|
|
- u8 new_ref;
|
|
+ struct zl3073x_chan chan;
|
|
+ u8 mode, ref;
|
|
int rc;
|
|
|
|
- switch (zldpll->refsel_mode) {
|
|
+ chan = *zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
|
+ ref = zl3073x_input_pin_ref_get(pin->id);
|
|
+ mode = zl3073x_chan_mode_get(&chan);
|
|
+
|
|
+ switch (mode) {
|
|
case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
|
|
- case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
|
|
- case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER:
|
|
if (state == DPLL_PIN_STATE_CONNECTED) {
|
|
/* Choose the pin as new selected reference */
|
|
- new_ref = zl3073x_input_pin_ref_get(pin->id);
|
|
+ zl3073x_chan_ref_set(&chan, ref);
|
|
} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
|
|
- /* No reference */
|
|
- new_ref = ZL3073X_DPLL_REF_NONE;
|
|
+ /* Choose new mode based on lock status */
|
|
+ switch (zldpll->lock_status) {
|
|
+ case DPLL_LOCK_STATUS_LOCKED_HO_ACQ:
|
|
+ case DPLL_LOCK_STATUS_HOLDOVER:
|
|
+ mode = ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER;
|
|
+ break;
|
|
+ default:
|
|
+ mode = ZL_DPLL_MODE_REFSEL_MODE_FREERUN;
|
|
+ break;
|
|
+ }
|
|
+ zl3073x_chan_mode_set(&chan, mode);
|
|
} else {
|
|
- NL_SET_ERR_MSG_MOD(extack,
|
|
- "Invalid pin state for manual mode");
|
|
- return -EINVAL;
|
|
+ goto invalid_state;
|
|
+ }
|
|
+ break;
|
|
+ case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
|
|
+ case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER:
|
|
+ if (state == DPLL_PIN_STATE_CONNECTED) {
|
|
+ /* Choose the pin as new selected reference */
|
|
+ zl3073x_chan_ref_set(&chan, ref);
|
|
+ /* Switch to reflock mode */
|
|
+ zl3073x_chan_mode_set(&chan,
|
|
+ ZL_DPLL_MODE_REFSEL_MODE_REFLOCK);
|
|
+ } else if (state != DPLL_PIN_STATE_DISCONNECTED) {
|
|
+ goto invalid_state;
|
|
}
|
|
-
|
|
- rc = zl3073x_dpll_selected_ref_set(zldpll, new_ref);
|
|
break;
|
|
-
|
|
case ZL_DPLL_MODE_REFSEL_MODE_AUTO:
|
|
if (state == DPLL_PIN_STATE_SELECTABLE) {
|
|
- if (pin->selectable)
|
|
+ if (zl3073x_chan_ref_is_selectable(&chan, ref))
|
|
return 0; /* Pin is already selectable */
|
|
|
|
/* Restore pin priority in HW */
|
|
- rc = zl3073x_dpll_ref_prio_set(pin, pin->prio);
|
|
- if (rc)
|
|
- return rc;
|
|
-
|
|
- /* Mark pin as selectable */
|
|
- pin->selectable = true;
|
|
+ zl3073x_chan_ref_prio_set(&chan, ref, pin->prio);
|
|
} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
|
|
- if (!pin->selectable)
|
|
+ if (!zl3073x_chan_ref_is_selectable(&chan, ref))
|
|
return 0; /* Pin is already disconnected */
|
|
|
|
/* Set pin priority to none in HW */
|
|
- rc = zl3073x_dpll_ref_prio_set(pin,
|
|
- ZL_DPLL_REF_PRIO_NONE);
|
|
- if (rc)
|
|
- return rc;
|
|
-
|
|
- /* Mark pin as non-selectable */
|
|
- pin->selectable = false;
|
|
+ zl3073x_chan_ref_prio_set(&chan, ref,
|
|
+ ZL_DPLL_REF_PRIO_NONE);
|
|
} else {
|
|
- NL_SET_ERR_MSG(extack,
|
|
- "Invalid pin state for automatic mode");
|
|
- return -EINVAL;
|
|
+ goto invalid_state;
|
|
}
|
|
break;
|
|
-
|
|
default:
|
|
/* In other modes we cannot change input reference */
|
|
NL_SET_ERR_MSG(extack,
|
|
"Pin state cannot be changed in current mode");
|
|
- rc = -EOPNOTSUPP;
|
|
- break;
|
|
+ return -EOPNOTSUPP;
|
|
}
|
|
|
|
- return rc;
|
|
+ /* Commit DPLL channel changes */
|
|
+ rc = zl3073x_chan_state_set(zldpll->dev, zldpll->id, &chan);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ return 0;
|
|
+invalid_state:
|
|
+ NL_SET_ERR_MSG_MOD(extack, "Invalid pin state for this device mode");
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
static int
|
|
@@ -759,15 +661,21 @@ zl3073x_dpll_input_pin_prio_set(const struct dpll_pin *dpll_pin, void *pin_priv,
|
|
const struct dpll_device *dpll, void *dpll_priv,
|
|
u32 prio, struct netlink_ext_ack *extack)
|
|
{
|
|
+ struct zl3073x_dpll *zldpll = dpll_priv;
|
|
struct zl3073x_dpll_pin *pin = pin_priv;
|
|
+ struct zl3073x_chan chan;
|
|
+ u8 ref;
|
|
int rc;
|
|
|
|
if (prio > ZL_DPLL_REF_PRIO_MAX)
|
|
return -EINVAL;
|
|
|
|
/* If the pin is selectable then update HW registers */
|
|
- if (pin->selectable) {
|
|
- rc = zl3073x_dpll_ref_prio_set(pin, prio);
|
|
+ chan = *zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
|
+ ref = zl3073x_input_pin_ref_get(pin->id);
|
|
+ if (zl3073x_chan_ref_is_selectable(&chan, ref)) {
|
|
+ zl3073x_chan_ref_prio_set(&chan, ref, prio);
|
|
+ rc = zl3073x_chan_state_set(zldpll->dev, zldpll->id, &chan);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
@@ -791,8 +699,8 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
|
|
struct zl3073x_dpll_pin *pin = pin_priv;
|
|
const struct zl3073x_synth *synth;
|
|
const struct zl3073x_out *out;
|
|
- u8 clock_type, out_id;
|
|
- u32 synth_freq;
|
|
+ u32 synth_freq, out_freq;
|
|
+ u8 out_id;
|
|
|
|
out_id = zl3073x_output_pin_out_get(pin->id);
|
|
out = zl3073x_out_state_get(zldev, out_id);
|
|
@@ -801,29 +709,30 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
|
|
* for N-division is also used for the esync divider so both cannot
|
|
* be used.
|
|
*/
|
|
- switch (zl3073x_out_signal_format_get(out)) {
|
|
- case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
|
|
- case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
|
|
+ if (zl3073x_out_is_ndiv(out))
|
|
return -EOPNOTSUPP;
|
|
- default:
|
|
- break;
|
|
- }
|
|
|
|
/* Get attached synth frequency */
|
|
synth = zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(out));
|
|
synth_freq = zl3073x_synth_freq_get(synth);
|
|
+ out_freq = synth_freq / out->div;
|
|
+
|
|
+ if (!pin->esync_control || out_freq <= 1)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ esync->range = esync_freq_ranges;
|
|
+ esync->range_num = ARRAY_SIZE(esync_freq_ranges);
|
|
|
|
- clock_type = FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, out->mode);
|
|
- if (clock_type != ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
|
|
+ if (zl3073x_out_clock_type_get(out) != ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
|
|
/* No need to read esync data if it is not enabled */
|
|
esync->freq = 0;
|
|
esync->pulse = 0;
|
|
|
|
- goto finish;
|
|
+ return 0;
|
|
}
|
|
|
|
/* Compute esync frequency */
|
|
- esync->freq = synth_freq / out->div / out->esync_n_period;
|
|
+ esync->freq = out_freq / out->esync_n_period;
|
|
|
|
/* By comparing the esync_pulse_width to the half of the pulse width
|
|
* the esync pulse percentage can be determined.
|
|
@@ -832,18 +741,6 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
|
|
*/
|
|
esync->pulse = (50 * out->esync_n_width) / out->div;
|
|
|
|
-finish:
|
|
- /* Set supported esync ranges if the pin supports esync control and
|
|
- * if the output frequency is > 1 Hz.
|
|
- */
|
|
- if (pin->esync_control && (synth_freq / out->div) > 1) {
|
|
- esync->range = esync_freq_ranges;
|
|
- esync->range_num = ARRAY_SIZE(esync_freq_ranges);
|
|
- } else {
|
|
- esync->range = NULL;
|
|
- esync->range_num = 0;
|
|
- }
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
@@ -859,8 +756,8 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
|
|
struct zl3073x_dpll_pin *pin = pin_priv;
|
|
const struct zl3073x_synth *synth;
|
|
struct zl3073x_out out;
|
|
- u8 clock_type, out_id;
|
|
u32 synth_freq;
|
|
+ u8 out_id;
|
|
|
|
out_id = zl3073x_output_pin_out_get(pin->id);
|
|
out = *zl3073x_out_state_get(zldev, out_id);
|
|
@@ -869,23 +766,16 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
|
|
* for N-division is also used for the esync divider so both cannot
|
|
* be used.
|
|
*/
|
|
- switch (zl3073x_out_signal_format_get(&out)) {
|
|
- case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
|
|
- case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
|
|
+ if (zl3073x_out_is_ndiv(&out))
|
|
return -EOPNOTSUPP;
|
|
- default:
|
|
- break;
|
|
- }
|
|
|
|
- /* Select clock type */
|
|
+ /* Update clock type in output mode */
|
|
if (freq)
|
|
- clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC;
|
|
+ zl3073x_out_clock_type_set(&out,
|
|
+ ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC);
|
|
else
|
|
- clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL;
|
|
-
|
|
- /* Update clock type in output mode */
|
|
- out.mode &= ~ZL_OUTPUT_MODE_CLOCK_TYPE;
|
|
- out.mode |= FIELD_PREP(ZL_OUTPUT_MODE_CLOCK_TYPE, clock_type);
|
|
+ zl3073x_out_clock_type_set(&out,
|
|
+ ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL);
|
|
|
|
/* If esync is being disabled just write mailbox and finish */
|
|
if (!freq)
|
|
@@ -937,9 +827,9 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
|
|
struct zl3073x_dev *zldev = zldpll->dev;
|
|
struct zl3073x_dpll_pin *pin = pin_priv;
|
|
const struct zl3073x_synth *synth;
|
|
- u8 out_id, signal_format;
|
|
u32 new_div, synth_freq;
|
|
struct zl3073x_out out;
|
|
+ u8 out_id;
|
|
|
|
out_id = zl3073x_output_pin_out_get(pin->id);
|
|
out = *zl3073x_out_state_get(zldev, out_id);
|
|
@@ -949,12 +839,8 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
|
|
synth_freq = zl3073x_synth_freq_get(synth);
|
|
new_div = synth_freq / (u32)frequency;
|
|
|
|
- /* Get used signal format for the given output */
|
|
- signal_format = zl3073x_out_signal_format_get(&out);
|
|
-
|
|
/* Check signal format */
|
|
- if (signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV &&
|
|
- signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV) {
|
|
+ if (!zl3073x_out_is_ndiv(&out)) {
|
|
/* For non N-divided signal formats the frequency is computed
|
|
* as division of synth frequency and output divisor.
|
|
*/
|
|
@@ -1065,6 +951,25 @@ zl3073x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin,
|
|
return 0;
|
|
}
|
|
|
|
+static int
|
|
+zl3073x_dpll_temp_get(const struct dpll_device *dpll, void *dpll_priv,
|
|
+ s32 *temp, struct netlink_ext_ack *extack)
|
|
+{
|
|
+ struct zl3073x_dpll *zldpll = dpll_priv;
|
|
+ struct zl3073x_dev *zldev = zldpll->dev;
|
|
+ u16 val;
|
|
+ int rc;
|
|
+
|
|
+ rc = zl3073x_read_u16(zldev, ZL_REG_DIE_TEMP_STATUS, &val);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
+ /* Register value is in units of 0.1 C, convert to millidegrees */
|
|
+ *temp = (s16)val * 100;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int
|
|
zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
|
|
enum dpll_lock_status *status,
|
|
@@ -1072,11 +977,11 @@ zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct zl3073x_dpll *zldpll = dpll_priv;
|
|
- struct zl3073x_dev *zldev = zldpll->dev;
|
|
- u8 mon_status, state;
|
|
- int rc;
|
|
+ const struct zl3073x_chan *chan;
|
|
+
|
|
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
|
|
|
- switch (zldpll->refsel_mode) {
|
|
+ switch (zl3073x_chan_mode_get(chan)) {
|
|
case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
|
|
case ZL_DPLL_MODE_REFSEL_MODE_NCO:
|
|
/* In FREERUN and NCO modes the DPLL is always unlocked */
|
|
@@ -1087,16 +992,9 @@ zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
|
|
break;
|
|
}
|
|
|
|
- /* Read DPLL monitor status */
|
|
- rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(zldpll->id),
|
|
- &mon_status);
|
|
- if (rc)
|
|
- return rc;
|
|
- state = FIELD_GET(ZL_DPLL_MON_STATUS_STATE, mon_status);
|
|
-
|
|
- switch (state) {
|
|
+ switch (zl3073x_chan_lock_state_get(chan)) {
|
|
case ZL_DPLL_MON_STATUS_STATE_LOCK:
|
|
- if (FIELD_GET(ZL_DPLL_MON_STATUS_HO_READY, mon_status))
|
|
+ if (zl3073x_chan_is_ho_ready(chan))
|
|
*status = DPLL_LOCK_STATUS_LOCKED_HO_ACQ;
|
|
else
|
|
*status = DPLL_LOCK_STATUS_LOCKED;
|
|
@@ -1106,8 +1004,9 @@ zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
|
|
*status = DPLL_LOCK_STATUS_HOLDOVER;
|
|
break;
|
|
default:
|
|
- dev_warn(zldev->dev, "Unknown DPLL monitor status: 0x%02x\n",
|
|
- mon_status);
|
|
+ dev_warn(zldpll->dev->dev,
|
|
+ "Unknown DPLL monitor status: 0x%02x\n",
|
|
+ chan->mon_status);
|
|
*status = DPLL_LOCK_STATUS_UNLOCKED;
|
|
break;
|
|
}
|
|
@@ -1121,13 +1020,16 @@ zl3073x_dpll_supported_modes_get(const struct dpll_device *dpll,
|
|
struct netlink_ext_ack *extack)
|
|
{
|
|
struct zl3073x_dpll *zldpll = dpll_priv;
|
|
+ const struct zl3073x_chan *chan;
|
|
+
|
|
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
|
|
|
/* We support switching between automatic and manual mode, except in
|
|
* a case where the DPLL channel is configured to run in NCO mode.
|
|
* In this case, report only the manual mode to which the NCO is mapped
|
|
* as the only supported one.
|
|
*/
|
|
- if (zldpll->refsel_mode != ZL_DPLL_MODE_REFSEL_MODE_NCO)
|
|
+ if (zl3073x_chan_mode_get(chan) != ZL_DPLL_MODE_REFSEL_MODE_NCO)
|
|
__set_bit(DPLL_MODE_AUTOMATIC, modes);
|
|
|
|
__set_bit(DPLL_MODE_MANUAL, modes);
|
|
@@ -1140,8 +1042,11 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
|
|
enum dpll_mode *mode, struct netlink_ext_ack *extack)
|
|
{
|
|
struct zl3073x_dpll *zldpll = dpll_priv;
|
|
+ const struct zl3073x_chan *chan;
|
|
|
|
- switch (zldpll->refsel_mode) {
|
|
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
|
+
|
|
+ switch (zl3073x_chan_mode_get(chan)) {
|
|
case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
|
|
case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER:
|
|
case ZL_DPLL_MODE_REFSEL_MODE_NCO:
|
|
@@ -1220,14 +1125,12 @@ zl3073x_dpll_mode_set(const struct dpll_device *dpll, void *dpll_priv,
|
|
enum dpll_mode mode, struct netlink_ext_ack *extack)
|
|
{
|
|
struct zl3073x_dpll *zldpll = dpll_priv;
|
|
- u8 hw_mode, mode_refsel, ref;
|
|
+ struct zl3073x_chan chan;
|
|
+ u8 hw_mode, ref;
|
|
int rc;
|
|
|
|
- rc = zl3073x_dpll_selected_ref_get(zldpll, &ref);
|
|
- if (rc) {
|
|
- NL_SET_ERR_MSG_MOD(extack, "failed to get selected reference");
|
|
- return rc;
|
|
- }
|
|
+ chan = *zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
|
+ ref = zl3073x_chan_refsel_ref_get(&chan);
|
|
|
|
if (mode == DPLL_MODE_MANUAL) {
|
|
/* We are switching from automatic to manual mode:
|
|
@@ -1250,44 +1153,32 @@ zl3073x_dpll_mode_set(const struct dpll_device *dpll, void *dpll_priv,
|
|
* it is selectable after switch to automatic mode
|
|
* - switch to automatic mode
|
|
*/
|
|
- struct zl3073x_dpll_pin *pin;
|
|
-
|
|
- pin = zl3073x_dpll_pin_get_by_ref(zldpll, ref);
|
|
- if (pin && !pin->selectable) {
|
|
- /* Restore pin priority in HW */
|
|
- rc = zl3073x_dpll_ref_prio_set(pin, pin->prio);
|
|
- if (rc) {
|
|
- NL_SET_ERR_MSG_MOD(extack,
|
|
- "failed to restore pin priority");
|
|
- return rc;
|
|
+ if (ZL3073X_DPLL_REF_IS_VALID(ref) &&
|
|
+ !zl3073x_chan_ref_is_selectable(&chan, ref)) {
|
|
+ struct zl3073x_dpll_pin *pin;
|
|
+
|
|
+ pin = zl3073x_dpll_pin_get_by_ref(zldpll, ref);
|
|
+ if (pin) {
|
|
+ /* Restore pin priority in HW */
|
|
+ zl3073x_chan_ref_prio_set(&chan, ref,
|
|
+ pin->prio);
|
|
}
|
|
-
|
|
- pin->selectable = true;
|
|
}
|
|
|
|
hw_mode = ZL_DPLL_MODE_REFSEL_MODE_AUTO;
|
|
}
|
|
|
|
- /* Build mode_refsel value */
|
|
- mode_refsel = FIELD_PREP(ZL_DPLL_MODE_REFSEL_MODE, hw_mode);
|
|
-
|
|
+ zl3073x_chan_mode_set(&chan, hw_mode);
|
|
if (ZL3073X_DPLL_REF_IS_VALID(ref))
|
|
- mode_refsel |= FIELD_PREP(ZL_DPLL_MODE_REFSEL_REF, ref);
|
|
+ zl3073x_chan_ref_set(&chan, ref);
|
|
|
|
- /* Update dpll_mode_refsel register */
|
|
- rc = zl3073x_write_u8(zldpll->dev, ZL_REG_DPLL_MODE_REFSEL(zldpll->id),
|
|
- mode_refsel);
|
|
+ rc = zl3073x_chan_state_set(zldpll->dev, zldpll->id, &chan);
|
|
if (rc) {
|
|
NL_SET_ERR_MSG_MOD(extack,
|
|
"failed to set reference selection mode");
|
|
return rc;
|
|
}
|
|
|
|
- zldpll->refsel_mode = hw_mode;
|
|
-
|
|
- if (ZL3073X_DPLL_REF_IS_VALID(ref))
|
|
- zldpll->forced_ref = ref;
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
@@ -1320,6 +1211,35 @@ zl3073x_dpll_phase_offset_monitor_set(const struct dpll_device *dpll,
|
|
return 0;
|
|
}
|
|
|
|
+static int
|
|
+zl3073x_dpll_freq_monitor_get(const struct dpll_device *dpll,
|
|
+ void *dpll_priv,
|
|
+ enum dpll_feature_state *state,
|
|
+ struct netlink_ext_ack *extack)
|
|
+{
|
|
+ struct zl3073x_dpll *zldpll = dpll_priv;
|
|
+
|
|
+ if (zldpll->freq_monitor)
|
|
+ *state = DPLL_FEATURE_STATE_ENABLE;
|
|
+ else
|
|
+ *state = DPLL_FEATURE_STATE_DISABLE;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+zl3073x_dpll_freq_monitor_set(const struct dpll_device *dpll,
|
|
+ void *dpll_priv,
|
|
+ enum dpll_feature_state state,
|
|
+ struct netlink_ext_ack *extack)
|
|
+{
|
|
+ struct zl3073x_dpll *zldpll = dpll_priv;
|
|
+
|
|
+ zldpll->freq_monitor = (state == DPLL_FEATURE_STATE_ENABLE);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
|
|
.direction_get = zl3073x_dpll_pin_direction_get,
|
|
.esync_get = zl3073x_dpll_input_pin_esync_get,
|
|
@@ -1327,11 +1247,14 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
|
|
.ffo_get = zl3073x_dpll_input_pin_ffo_get,
|
|
.frequency_get = zl3073x_dpll_input_pin_frequency_get,
|
|
.frequency_set = zl3073x_dpll_input_pin_frequency_set,
|
|
+ .measured_freq_get = zl3073x_dpll_input_pin_measured_freq_get,
|
|
.phase_offset_get = zl3073x_dpll_input_pin_phase_offset_get,
|
|
.phase_adjust_get = zl3073x_dpll_input_pin_phase_adjust_get,
|
|
.phase_adjust_set = zl3073x_dpll_input_pin_phase_adjust_set,
|
|
.prio_get = zl3073x_dpll_input_pin_prio_get,
|
|
.prio_set = zl3073x_dpll_input_pin_prio_set,
|
|
+ .ref_sync_get = zl3073x_dpll_input_pin_ref_sync_get,
|
|
+ .ref_sync_set = zl3073x_dpll_input_pin_ref_sync_set,
|
|
.state_on_dpll_get = zl3073x_dpll_input_pin_state_on_dpll_get,
|
|
.state_on_dpll_set = zl3073x_dpll_input_pin_state_on_dpll_set,
|
|
};
|
|
@@ -1355,6 +1278,8 @@ static const struct dpll_device_ops zl3073x_dpll_device_ops = {
|
|
.phase_offset_avg_factor_set = zl3073x_dpll_phase_offset_avg_factor_set,
|
|
.phase_offset_monitor_get = zl3073x_dpll_phase_offset_monitor_get,
|
|
.phase_offset_monitor_set = zl3073x_dpll_phase_offset_monitor_set,
|
|
+ .freq_monitor_get = zl3073x_dpll_freq_monitor_get,
|
|
+ .freq_monitor_set = zl3073x_dpll_freq_monitor_set,
|
|
.supported_modes_get = zl3073x_dpll_supported_modes_get,
|
|
};
|
|
|
|
@@ -1422,24 +1347,25 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
|
|
if (IS_ERR(props))
|
|
return PTR_ERR(props);
|
|
|
|
- /* Save package label, esync capability and phase adjust granularity */
|
|
+ /* Save package label, fwnode, esync capability and phase adjust
|
|
+ * granularity.
|
|
+ */
|
|
strscpy(pin->label, props->package_label);
|
|
+ pin->fwnode = fwnode_handle_get(props->fwnode);
|
|
pin->esync_control = props->esync_control;
|
|
pin->phase_gran = props->dpll_props.phase_gran;
|
|
|
|
if (zl3073x_dpll_is_input_pin(pin)) {
|
|
- rc = zl3073x_dpll_ref_prio_get(pin, &pin->prio);
|
|
- if (rc)
|
|
- goto err_prio_get;
|
|
+ const struct zl3073x_chan *chan;
|
|
+ u8 ref;
|
|
|
|
- if (pin->prio == ZL_DPLL_REF_PRIO_NONE) {
|
|
- /* Clamp prio to max value & mark pin non-selectable */
|
|
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
|
+ ref = zl3073x_input_pin_ref_get(pin->id);
|
|
+ pin->prio = zl3073x_chan_ref_prio_get(chan, ref);
|
|
+
|
|
+ if (pin->prio == ZL_DPLL_REF_PRIO_NONE)
|
|
+ /* Clamp prio to max value */
|
|
pin->prio = ZL_DPLL_REF_PRIO_MAX;
|
|
- pin->selectable = false;
|
|
- } else {
|
|
- /* Mark pin as selectable */
|
|
- pin->selectable = true;
|
|
- }
|
|
}
|
|
|
|
/* Create or get existing DPLL pin */
|
|
@@ -1468,9 +1394,10 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
|
|
|
|
err_register:
|
|
dpll_pin_put(pin->dpll_pin, &pin->tracker);
|
|
-err_prio_get:
|
|
pin->dpll_pin = NULL;
|
|
err_pin_get:
|
|
+ fwnode_handle_put(pin->fwnode);
|
|
+ pin->fwnode = NULL;
|
|
zl3073x_pin_props_put(props);
|
|
|
|
return rc;
|
|
@@ -1500,6 +1427,9 @@ zl3073x_dpll_pin_unregister(struct zl3073x_dpll_pin *pin)
|
|
|
|
dpll_pin_put(pin->dpll_pin, &pin->tracker);
|
|
pin->dpll_pin = NULL;
|
|
+
|
|
+ fwnode_handle_put(pin->fwnode);
|
|
+ pin->fwnode = NULL;
|
|
}
|
|
|
|
/**
|
|
@@ -1540,15 +1470,18 @@ zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll *zldpll,
|
|
enum dpll_pin_direction dir, u8 index)
|
|
{
|
|
struct zl3073x_dev *zldev = zldpll->dev;
|
|
+ const struct zl3073x_chan *chan;
|
|
bool is_diff, is_enabled;
|
|
const char *name;
|
|
|
|
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
|
+
|
|
if (dir == DPLL_PIN_DIRECTION_INPUT) {
|
|
u8 ref_id = zl3073x_input_pin_ref_get(index);
|
|
const struct zl3073x_ref *ref;
|
|
|
|
/* Skip the pin if the DPLL is running in NCO mode */
|
|
- if (zldpll->refsel_mode == ZL_DPLL_MODE_REFSEL_MODE_NCO)
|
|
+ if (zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_NCO)
|
|
return false;
|
|
|
|
name = "REF";
|
|
@@ -1656,20 +1589,11 @@ static int
|
|
zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
|
|
{
|
|
struct zl3073x_dev *zldev = zldpll->dev;
|
|
- u8 dpll_mode_refsel;
|
|
int rc;
|
|
|
|
- /* Read DPLL mode and forcibly selected reference */
|
|
- rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(zldpll->id),
|
|
- &dpll_mode_refsel);
|
|
- if (rc)
|
|
- return rc;
|
|
-
|
|
- /* Extract mode and selected input reference */
|
|
- zldpll->refsel_mode = FIELD_GET(ZL_DPLL_MODE_REFSEL_MODE,
|
|
- dpll_mode_refsel);
|
|
- zldpll->forced_ref = FIELD_GET(ZL_DPLL_MODE_REFSEL_REF,
|
|
- dpll_mode_refsel);
|
|
+ zldpll->ops = zl3073x_dpll_device_ops;
|
|
+ if (zldev->info->flags & ZL3073X_FLAG_DIE_TEMP)
|
|
+ zldpll->ops.temp_get = zl3073x_dpll_temp_get;
|
|
|
|
zldpll->dpll_dev = dpll_device_get(zldev->clock_id, zldpll->id,
|
|
THIS_MODULE, &zldpll->tracker);
|
|
@@ -1682,7 +1606,7 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
|
|
|
|
rc = dpll_device_register(zldpll->dpll_dev,
|
|
zl3073x_prop_dpll_type_get(zldev, zldpll->id),
|
|
- &zl3073x_dpll_device_ops, zldpll);
|
|
+ &zldpll->ops, zldpll);
|
|
if (rc) {
|
|
dpll_device_put(zldpll->dpll_dev, &zldpll->tracker);
|
|
zldpll->dpll_dev = NULL;
|
|
@@ -1705,8 +1629,7 @@ zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll)
|
|
|
|
cancel_work_sync(&zldpll->change_work);
|
|
|
|
- dpll_device_unregister(zldpll->dpll_dev, &zl3073x_dpll_device_ops,
|
|
- zldpll);
|
|
+ dpll_device_unregister(zldpll->dpll_dev, &zldpll->ops, zldpll);
|
|
dpll_device_put(zldpll->dpll_dev, &zldpll->tracker);
|
|
zldpll->dpll_dev = NULL;
|
|
}
|
|
@@ -1786,6 +1709,7 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)
|
|
struct zl3073x_dev *zldev = zldpll->dev;
|
|
const struct zl3073x_ref *ref;
|
|
u8 ref_id;
|
|
+ s64 ffo;
|
|
|
|
/* Get reference monitor status */
|
|
ref_id = zl3073x_input_pin_ref_get(pin->id);
|
|
@@ -1796,10 +1720,47 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)
|
|
return false;
|
|
|
|
/* Compare with previous value */
|
|
- if (pin->freq_offset != ref->ffo) {
|
|
+ ffo = zl3073x_ref_ffo_get(ref);
|
|
+ if (pin->freq_offset != ffo) {
|
|
dev_dbg(zldev->dev, "%s freq offset changed: %lld -> %lld\n",
|
|
- pin->label, pin->freq_offset, ref->ffo);
|
|
- pin->freq_offset = ref->ffo;
|
|
+ pin->label, pin->freq_offset, ffo);
|
|
+ pin->freq_offset = ffo;
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_dpll_pin_measured_freq_check - check for pin measured frequency
|
|
+ * change
|
|
+ * @pin: pin to check
|
|
+ *
|
|
+ * Check for the given pin's measured frequency change.
|
|
+ *
|
|
+ * Return: true on measured frequency change, false otherwise
|
|
+ */
|
|
+static bool
|
|
+zl3073x_dpll_pin_measured_freq_check(struct zl3073x_dpll_pin *pin)
|
|
+{
|
|
+ struct zl3073x_dpll *zldpll = pin->dpll;
|
|
+ struct zl3073x_dev *zldev = zldpll->dev;
|
|
+ const struct zl3073x_ref *ref;
|
|
+ u8 ref_id;
|
|
+ u32 freq;
|
|
+
|
|
+ if (!zldpll->freq_monitor)
|
|
+ return false;
|
|
+
|
|
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
|
|
+ ref = zl3073x_ref_state_get(zldev, ref_id);
|
|
+
|
|
+ freq = zl3073x_ref_meas_freq_get(ref);
|
|
+ if (pin->measured_freq != freq) {
|
|
+ dev_dbg(zldev->dev, "%s measured freq changed: %u -> %u\n",
|
|
+ pin->label, pin->measured_freq, freq);
|
|
+ pin->measured_freq = freq;
|
|
|
|
return true;
|
|
}
|
|
@@ -1822,8 +1783,10 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
|
|
struct zl3073x_dev *zldev = zldpll->dev;
|
|
enum dpll_lock_status lock_status;
|
|
struct device *dev = zldev->dev;
|
|
+ const struct zl3073x_chan *chan;
|
|
struct zl3073x_dpll_pin *pin;
|
|
int rc;
|
|
+ u8 mode;
|
|
|
|
zldpll->check_count++;
|
|
|
|
@@ -1845,8 +1808,10 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
|
|
/* Input pin monitoring does make sense only in automatic
|
|
* or forced reference modes.
|
|
*/
|
|
- if (zldpll->refsel_mode != ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
|
|
- zldpll->refsel_mode != ZL_DPLL_MODE_REFSEL_MODE_REFLOCK)
|
|
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
|
+ mode = zl3073x_chan_mode_get(chan);
|
|
+ if (mode != ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
|
|
+ mode != ZL_DPLL_MODE_REFSEL_MODE_REFLOCK)
|
|
return;
|
|
|
|
/* Update phase offset latch registers for this DPLL if the phase
|
|
@@ -1887,13 +1852,18 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
|
|
pin_changed = true;
|
|
}
|
|
|
|
- /* Check for phase offset and ffo change once per second */
|
|
+ /* Check for phase offset, ffo, and measured freq change
|
|
+ * once per second.
|
|
+ */
|
|
if (zldpll->check_count % 2 == 0) {
|
|
if (zl3073x_dpll_pin_phase_offset_check(pin))
|
|
pin_changed = true;
|
|
|
|
if (zl3073x_dpll_pin_ffo_check(pin))
|
|
pin_changed = true;
|
|
+
|
|
+ if (zl3073x_dpll_pin_measured_freq_check(pin))
|
|
+ pin_changed = true;
|
|
}
|
|
|
|
if (pin_changed)
|
|
@@ -1973,6 +1943,88 @@ zl3073x_dpll_free(struct zl3073x_dpll *zldpll)
|
|
kfree(zldpll);
|
|
}
|
|
|
|
+/**
|
|
+ * zl3073x_dpll_ref_sync_pair_register - register ref_sync pairs for a pin
|
|
+ * @pin: pointer to zl3073x_dpll_pin structure
|
|
+ *
|
|
+ * Iterates 'ref-sync-sources' phandles in the pin's firmware node and
|
|
+ * registers each declared pairing.
|
|
+ *
|
|
+ * Return: 0 on success, <0 on error
|
|
+ */
|
|
+static int
|
|
+zl3073x_dpll_ref_sync_pair_register(struct zl3073x_dpll_pin *pin)
|
|
+{
|
|
+ struct zl3073x_dev *zldev = pin->dpll->dev;
|
|
+ struct fwnode_handle *fwnode;
|
|
+ struct dpll_pin *sync_pin;
|
|
+ dpll_tracker tracker;
|
|
+ int n, rc;
|
|
+
|
|
+ for (n = 0; ; n++) {
|
|
+ /* Get n'th ref-sync source */
|
|
+ fwnode = fwnode_find_reference(pin->fwnode, "ref-sync-sources",
|
|
+ n);
|
|
+ if (IS_ERR(fwnode)) {
|
|
+ rc = PTR_ERR(fwnode);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Find associated dpll pin */
|
|
+ sync_pin = fwnode_dpll_pin_find(fwnode, &tracker);
|
|
+ fwnode_handle_put(fwnode);
|
|
+ if (!sync_pin) {
|
|
+ dev_warn(zldev->dev, "%s: ref-sync source %d not found",
|
|
+ pin->label, n);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* Register new ref-sync pair */
|
|
+ rc = dpll_pin_ref_sync_pair_add(pin->dpll_pin, sync_pin);
|
|
+ dpll_pin_put(sync_pin, &tracker);
|
|
+
|
|
+ /* -EBUSY means pairing already exists from another DPLL's
|
|
+ * registration.
|
|
+ */
|
|
+ if (rc && rc != -EBUSY) {
|
|
+ dev_err(zldev->dev,
|
|
+ "%s: failed to add ref-sync source %d: %pe",
|
|
+ pin->label, n, ERR_PTR(rc));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return rc != -ENOENT ? rc : 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_dpll_ref_sync_pairs_register - register ref_sync pairs for a DPLL
|
|
+ * @zldpll: pointer to zl3073x_dpll structure
|
|
+ *
|
|
+ * Iterates all registered input pins of the given DPLL and establishes
|
|
+ * ref_sync pairings declared by 'ref-sync-sources' phandles in the
|
|
+ * device tree.
|
|
+ *
|
|
+ * Return: 0 on success, <0 on error
|
|
+ */
|
|
+static int
|
|
+zl3073x_dpll_ref_sync_pairs_register(struct zl3073x_dpll *zldpll)
|
|
+{
|
|
+ struct zl3073x_dpll_pin *pin;
|
|
+ int rc;
|
|
+
|
|
+ list_for_each_entry(pin, &zldpll->pins, list) {
|
|
+ if (!zl3073x_dpll_is_input_pin(pin) || !pin->fwnode)
|
|
+ continue;
|
|
+
|
|
+ rc = zl3073x_dpll_ref_sync_pair_register(pin);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/**
|
|
* zl3073x_dpll_register - register DPLL device and all its pins
|
|
* @zldpll: pointer to zl3073x_dpll structure
|
|
@@ -1996,6 +2048,13 @@ zl3073x_dpll_register(struct zl3073x_dpll *zldpll)
|
|
return rc;
|
|
}
|
|
|
|
+ rc = zl3073x_dpll_ref_sync_pairs_register(zldpll);
|
|
+ if (rc) {
|
|
+ zl3073x_dpll_pins_unregister(zldpll);
|
|
+ zl3073x_dpll_device_unregister(zldpll);
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h
|
|
index c65c798..434c32a 100644
|
|
--- a/drivers/dpll/zl3073x/dpll.h
|
|
+++ b/drivers/dpll/zl3073x/dpll.h
|
|
@@ -13,10 +13,10 @@
|
|
* @list: this DPLL list entry
|
|
* @dev: pointer to multi-function parent device
|
|
* @id: DPLL index
|
|
- * @refsel_mode: reference selection mode
|
|
- * @forced_ref: selected reference in forced reference lock mode
|
|
* @check_count: periodic check counter
|
|
* @phase_monitor: is phase offset monitor enabled
|
|
+ * @freq_monitor: is frequency monitor enabled
|
|
+ * @ops: DPLL device operations for this instance
|
|
* @dpll_dev: pointer to registered DPLL device
|
|
* @tracker: tracking object for the acquired reference
|
|
* @lock_status: last saved DPLL lock status
|
|
@@ -27,10 +27,10 @@ struct zl3073x_dpll {
|
|
struct list_head list;
|
|
struct zl3073x_dev *dev;
|
|
u8 id;
|
|
- u8 refsel_mode;
|
|
- u8 forced_ref;
|
|
u8 check_count;
|
|
bool phase_monitor;
|
|
+ bool freq_monitor;
|
|
+ struct dpll_device_ops ops;
|
|
struct dpll_device *dpll_dev;
|
|
dpll_tracker tracker;
|
|
enum dpll_lock_status lock_status;
|
|
diff --git a/drivers/dpll/zl3073x/flash.c b/drivers/dpll/zl3073x/flash.c
|
|
index e2771dd..fa7ee9e 100644
|
|
--- a/drivers/dpll/zl3073x/flash.c
|
|
+++ b/drivers/dpll/zl3073x/flash.c
|
|
@@ -194,8 +194,7 @@ zl3073x_flash_cmd_wait(struct zl3073x_dev *zldev, u32 operation,
|
|
if (rc)
|
|
return rc;
|
|
|
|
- value &= ~ZL_WRITE_FLASH_OP;
|
|
- value |= FIELD_PREP(ZL_WRITE_FLASH_OP, operation);
|
|
+ FIELD_MODIFY(ZL_WRITE_FLASH_OP, &value, operation);
|
|
|
|
rc = zl3073x_write_u8(zldev, ZL_REG_WRITE_FLASH, value);
|
|
if (rc)
|
|
diff --git a/drivers/dpll/zl3073x/i2c.c b/drivers/dpll/zl3073x/i2c.c
|
|
index a7ac128..2f36f2e 100644
|
|
--- a/drivers/dpll/zl3073x/i2c.c
|
|
+++ b/drivers/dpll/zl3073x/i2c.c
|
|
@@ -22,40 +22,25 @@ static int zl3073x_i2c_probe(struct i2c_client *client)
|
|
return dev_err_probe(dev, PTR_ERR(zldev->regmap),
|
|
"Failed to initialize regmap\n");
|
|
|
|
- return zl3073x_dev_probe(zldev, i2c_get_match_data(client));
|
|
+ return zl3073x_dev_probe(zldev);
|
|
}
|
|
|
|
static const struct i2c_device_id zl3073x_i2c_id[] = {
|
|
- {
|
|
- .name = "zl30731",
|
|
- .driver_data = (kernel_ulong_t)&zl30731_chip_info,
|
|
- },
|
|
- {
|
|
- .name = "zl30732",
|
|
- .driver_data = (kernel_ulong_t)&zl30732_chip_info,
|
|
- },
|
|
- {
|
|
- .name = "zl30733",
|
|
- .driver_data = (kernel_ulong_t)&zl30733_chip_info,
|
|
- },
|
|
- {
|
|
- .name = "zl30734",
|
|
- .driver_data = (kernel_ulong_t)&zl30734_chip_info,
|
|
- },
|
|
- {
|
|
- .name = "zl30735",
|
|
- .driver_data = (kernel_ulong_t)&zl30735_chip_info,
|
|
- },
|
|
+ { "zl30731" },
|
|
+ { "zl30732" },
|
|
+ { "zl30733" },
|
|
+ { "zl30734" },
|
|
+ { "zl30735" },
|
|
{ /* sentinel */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, zl3073x_i2c_id);
|
|
|
|
static const struct of_device_id zl3073x_i2c_of_match[] = {
|
|
- { .compatible = "microchip,zl30731", .data = &zl30731_chip_info },
|
|
- { .compatible = "microchip,zl30732", .data = &zl30732_chip_info },
|
|
- { .compatible = "microchip,zl30733", .data = &zl30733_chip_info },
|
|
- { .compatible = "microchip,zl30734", .data = &zl30734_chip_info },
|
|
- { .compatible = "microchip,zl30735", .data = &zl30735_chip_info },
|
|
+ { .compatible = "microchip,zl30731" },
|
|
+ { .compatible = "microchip,zl30732" },
|
|
+ { .compatible = "microchip,zl30733" },
|
|
+ { .compatible = "microchip,zl30734" },
|
|
+ { .compatible = "microchip,zl30735" },
|
|
{ /* sentinel */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, zl3073x_i2c_of_match);
|
|
diff --git a/drivers/dpll/zl3073x/out.c b/drivers/dpll/zl3073x/out.c
|
|
index 86829a0..eb5628a 100644
|
|
--- a/drivers/dpll/zl3073x/out.c
|
|
+++ b/drivers/dpll/zl3073x/out.c
|
|
@@ -106,12 +106,32 @@ const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
|
|
return &zldev->out[index];
|
|
}
|
|
|
|
+/**
|
|
+ * zl3073x_out_state_set - commit output state changes to hardware
|
|
+ * @zldev: pointer to zl3073x_dev structure
|
|
+ * @index: output index to set state for
|
|
+ * @out: desired output state
|
|
+ *
|
|
+ * Validates that invariant fields have not been modified, skips the HW
|
|
+ * write if the mutable configuration is unchanged, and otherwise writes
|
|
+ * only the changed cfg fields to hardware via the mailbox interface.
|
|
+ *
|
|
+ * Return: 0 on success, -EINVAL if invariants changed, <0 on HW error
|
|
+ */
|
|
int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
|
|
const struct zl3073x_out *out)
|
|
{
|
|
struct zl3073x_out *dout = &zldev->out[index];
|
|
int rc;
|
|
|
|
+ /* Reject attempts to change invariant fields (set at fetch only) */
|
|
+ if (WARN_ON(memcmp(&dout->inv, &out->inv, sizeof(out->inv))))
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Skip HW write if configuration hasn't changed */
|
|
+ if (!memcmp(&dout->cfg, &out->cfg, sizeof(out->cfg)))
|
|
+ return 0;
|
|
+
|
|
guard(mutex)(&zldev->multiop_lock);
|
|
|
|
/* Read output configuration into mailbox */
|
|
@@ -146,12 +166,7 @@ int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
|
|
return rc;
|
|
|
|
/* After successful commit store new state */
|
|
- dout->div = out->div;
|
|
- dout->width = out->width;
|
|
- dout->esync_n_period = out->esync_n_period;
|
|
- dout->esync_n_width = out->esync_n_width;
|
|
- dout->mode = out->mode;
|
|
- dout->phase_comp = out->phase_comp;
|
|
+ dout->cfg = out->cfg;
|
|
|
|
return 0;
|
|
}
|
|
diff --git a/drivers/dpll/zl3073x/out.h b/drivers/dpll/zl3073x/out.h
|
|
index 318f9bb..660889c 100644
|
|
--- a/drivers/dpll/zl3073x/out.h
|
|
+++ b/drivers/dpll/zl3073x/out.h
|
|
@@ -4,6 +4,7 @@
|
|
#define _ZL3073X_OUT_H
|
|
|
|
#include <linux/bitfield.h>
|
|
+#include <linux/stddef.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "regs.h"
|
|
@@ -17,17 +18,21 @@ struct zl3073x_dev;
|
|
* @esync_n_period: embedded sync or n-pin period (for n-div formats)
|
|
* @esync_n_width: embedded sync or n-pin pulse width
|
|
* @phase_comp: phase compensation
|
|
- * @ctrl: output control
|
|
* @mode: output mode
|
|
+ * @ctrl: output control
|
|
*/
|
|
struct zl3073x_out {
|
|
- u32 div;
|
|
- u32 width;
|
|
- u32 esync_n_period;
|
|
- u32 esync_n_width;
|
|
- s32 phase_comp;
|
|
- u8 ctrl;
|
|
- u8 mode;
|
|
+ struct_group(cfg, /* Config */
|
|
+ u32 div;
|
|
+ u32 width;
|
|
+ u32 esync_n_period;
|
|
+ u32 esync_n_width;
|
|
+ s32 phase_comp;
|
|
+ u8 mode;
|
|
+ );
|
|
+ struct_group(inv, /* Invariants */
|
|
+ u8 ctrl;
|
|
+ );
|
|
};
|
|
|
|
int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
|
@@ -37,6 +42,28 @@ const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
|
|
int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
|
|
const struct zl3073x_out *out);
|
|
|
|
+/**
|
|
+ * zl3073x_out_clock_type_get - get output clock type
|
|
+ * @out: pointer to out state
|
|
+ *
|
|
+ * Return: clock type of given output (ZL_OUTPUT_MODE_CLOCK_TYPE_*)
|
|
+ */
|
|
+static inline u8 zl3073x_out_clock_type_get(const struct zl3073x_out *out)
|
|
+{
|
|
+ return FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, out->mode);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_out_clock_type_set - set output clock type
|
|
+ * @out: pointer to out state
|
|
+ * @type: clock type (ZL_OUTPUT_MODE_CLOCK_TYPE_*)
|
|
+ */
|
|
+static inline void
|
|
+zl3073x_out_clock_type_set(struct zl3073x_out *out, u8 type)
|
|
+{
|
|
+ FIELD_MODIFY(ZL_OUTPUT_MODE_CLOCK_TYPE, &out->mode, type);
|
|
+}
|
|
+
|
|
/**
|
|
* zl3073x_out_signal_format_get - get output signal format
|
|
* @out: pointer to out state
|
|
diff --git a/drivers/dpll/zl3073x/ref.c b/drivers/dpll/zl3073x/ref.c
|
|
index 6b65e61..825ac30 100644
|
|
--- a/drivers/dpll/zl3073x/ref.c
|
|
+++ b/drivers/dpll/zl3073x/ref.c
|
|
@@ -51,6 +51,21 @@ zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult)
|
|
return -EINVAL;
|
|
}
|
|
|
|
+/**
|
|
+ * zl3073x_ref_state_update - update input reference status from HW
|
|
+ * @zldev: pointer to zl3073x_dev structure
|
|
+ * @index: input reference index
|
|
+ *
|
|
+ * Return: 0 on success, <0 on error
|
|
+ */
|
|
+int zl3073x_ref_state_update(struct zl3073x_dev *zldev, u8 index)
|
|
+{
|
|
+ struct zl3073x_ref *ref = &zldev->ref[index];
|
|
+
|
|
+ return zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(index),
|
|
+ &ref->mon_status);
|
|
+}
|
|
+
|
|
/**
|
|
* zl3073x_ref_state_fetch - fetch input reference state from hardware
|
|
* @zldev: pointer to zl3073x_dev structure
|
|
@@ -73,18 +88,17 @@ int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
|
struct zl3073x_ref *p_ref = ref - 1; /* P-pin counterpart*/
|
|
|
|
/* Copy the shared items from the P-pin */
|
|
- ref->config = p_ref->config;
|
|
- ref->esync_n_div = p_ref->esync_n_div;
|
|
- ref->freq_base = p_ref->freq_base;
|
|
- ref->freq_mult = p_ref->freq_mult;
|
|
- ref->freq_ratio_m = p_ref->freq_ratio_m;
|
|
- ref->freq_ratio_n = p_ref->freq_ratio_n;
|
|
- ref->phase_comp = p_ref->phase_comp;
|
|
- ref->sync_ctrl = p_ref->sync_ctrl;
|
|
+ ref->cfg = p_ref->cfg;
|
|
+ ref->inv = p_ref->inv;
|
|
|
|
return 0; /* Finish - no non-shared items for now */
|
|
}
|
|
|
|
+ /* Read reference status */
|
|
+ rc = zl3073x_ref_state_update(zldev, index);
|
|
+ if (rc)
|
|
+ return rc;
|
|
+
|
|
guard(mutex)(&zldev->multiop_lock);
|
|
|
|
/* Read reference configuration */
|
|
@@ -154,12 +168,32 @@ zl3073x_ref_state_get(struct zl3073x_dev *zldev, u8 index)
|
|
return &zldev->ref[index];
|
|
}
|
|
|
|
+/**
|
|
+ * zl3073x_ref_state_set - commit input reference state changes to hardware
|
|
+ * @zldev: pointer to zl3073x_dev structure
|
|
+ * @index: input reference index to set state for
|
|
+ * @ref: desired reference state
|
|
+ *
|
|
+ * Validates that invariant fields have not been modified, skips the HW
|
|
+ * write if the mutable configuration is unchanged, and otherwise writes
|
|
+ * only the changed cfg fields to hardware via the mailbox interface.
|
|
+ *
|
|
+ * Return: 0 on success, -EINVAL if invariants changed, <0 on HW error
|
|
+ */
|
|
int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
|
|
const struct zl3073x_ref *ref)
|
|
{
|
|
struct zl3073x_ref *dref = &zldev->ref[index];
|
|
int rc;
|
|
|
|
+ /* Reject attempts to change invariant fields (set at init only) */
|
|
+ if (WARN_ON(memcmp(&dref->inv, &ref->inv, sizeof(ref->inv))))
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Skip HW write if configuration hasn't changed */
|
|
+ if (!memcmp(&dref->cfg, &ref->cfg, sizeof(ref->cfg)))
|
|
+ return 0;
|
|
+
|
|
guard(mutex)(&zldev->multiop_lock);
|
|
|
|
/* Read reference configuration into mailbox */
|
|
@@ -207,13 +241,7 @@ int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
|
|
return rc;
|
|
|
|
/* After successful commit store new state */
|
|
- dref->freq_base = ref->freq_base;
|
|
- dref->freq_mult = ref->freq_mult;
|
|
- dref->freq_ratio_m = ref->freq_ratio_m;
|
|
- dref->freq_ratio_n = ref->freq_ratio_n;
|
|
- dref->esync_n_div = ref->esync_n_div;
|
|
- dref->sync_ctrl = ref->sync_ctrl;
|
|
- dref->phase_comp = ref->phase_comp;
|
|
+ dref->cfg = ref->cfg;
|
|
|
|
return 0;
|
|
}
|
|
diff --git a/drivers/dpll/zl3073x/ref.h b/drivers/dpll/zl3073x/ref.h
|
|
index 0d8618f..55e80e4 100644
|
|
--- a/drivers/dpll/zl3073x/ref.h
|
|
+++ b/drivers/dpll/zl3073x/ref.h
|
|
@@ -5,6 +5,7 @@
|
|
|
|
#include <linux/bitfield.h>
|
|
#include <linux/math64.h>
|
|
+#include <linux/stddef.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "regs.h"
|
|
@@ -13,28 +14,36 @@ struct zl3073x_dev;
|
|
|
|
/**
|
|
* struct zl3073x_ref - input reference state
|
|
- * @ffo: current fractional frequency offset
|
|
* @phase_comp: phase compensation
|
|
* @esync_n_div: divisor for embedded sync or n-divided signal formats
|
|
* @freq_base: frequency base
|
|
* @freq_mult: frequnecy multiplier
|
|
* @freq_ratio_m: FEC mode multiplier
|
|
* @freq_ratio_n: FEC mode divisor
|
|
- * @config: reference config
|
|
* @sync_ctrl: reference sync control
|
|
+ * @config: reference config
|
|
+ * @ffo: current fractional frequency offset
|
|
+ * @meas_freq: measured input frequency in Hz
|
|
* @mon_status: reference monitor status
|
|
*/
|
|
struct zl3073x_ref {
|
|
- s64 ffo;
|
|
- u64 phase_comp;
|
|
- u32 esync_n_div;
|
|
- u16 freq_base;
|
|
- u16 freq_mult;
|
|
- u16 freq_ratio_m;
|
|
- u16 freq_ratio_n;
|
|
- u8 config;
|
|
- u8 sync_ctrl;
|
|
- u8 mon_status;
|
|
+ struct_group(cfg, /* Configuration */
|
|
+ u64 phase_comp;
|
|
+ u32 esync_n_div;
|
|
+ u16 freq_base;
|
|
+ u16 freq_mult;
|
|
+ u16 freq_ratio_m;
|
|
+ u16 freq_ratio_n;
|
|
+ u8 sync_ctrl;
|
|
+ );
|
|
+ struct_group(inv, /* Invariants */
|
|
+ u8 config;
|
|
+ );
|
|
+ struct_group(stat, /* Status */
|
|
+ s64 ffo;
|
|
+ u32 meas_freq;
|
|
+ u8 mon_status;
|
|
+ );
|
|
};
|
|
|
|
int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
|
@@ -45,6 +54,8 @@ const struct zl3073x_ref *zl3073x_ref_state_get(struct zl3073x_dev *zldev,
|
|
int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
|
|
const struct zl3073x_ref *ref);
|
|
|
|
+int zl3073x_ref_state_update(struct zl3073x_dev *zldev, u8 index);
|
|
+
|
|
int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult);
|
|
|
|
/**
|
|
@@ -59,6 +70,18 @@ zl3073x_ref_ffo_get(const struct zl3073x_ref *ref)
|
|
return ref->ffo;
|
|
}
|
|
|
|
+/**
|
|
+ * zl3073x_ref_meas_freq_get - get measured input frequency
|
|
+ * @ref: pointer to ref state
|
|
+ *
|
|
+ * Return: measured input frequency in Hz
|
|
+ */
|
|
+static inline u32
|
|
+zl3073x_ref_meas_freq_get(const struct zl3073x_ref *ref)
|
|
+{
|
|
+ return ref->meas_freq;
|
|
+}
|
|
+
|
|
/**
|
|
* zl3073x_ref_freq_get - get given input reference frequency
|
|
* @ref: pointer to ref state
|
|
@@ -97,6 +120,52 @@ zl3073x_ref_freq_set(struct zl3073x_ref *ref, u32 freq)
|
|
return 0;
|
|
}
|
|
|
|
+/**
|
|
+ * zl3073x_ref_sync_mode_get - get sync control mode
|
|
+ * @ref: pointer to ref state
|
|
+ *
|
|
+ * Return: sync control mode (ZL_REF_SYNC_CTRL_MODE_*)
|
|
+ */
|
|
+static inline u8
|
|
+zl3073x_ref_sync_mode_get(const struct zl3073x_ref *ref)
|
|
+{
|
|
+ return FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref->sync_ctrl);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_ref_sync_mode_set - set sync control mode
|
|
+ * @ref: pointer to ref state
|
|
+ * @mode: sync control mode (ZL_REF_SYNC_CTRL_MODE_*)
|
|
+ */
|
|
+static inline void
|
|
+zl3073x_ref_sync_mode_set(struct zl3073x_ref *ref, u8 mode)
|
|
+{
|
|
+ FIELD_MODIFY(ZL_REF_SYNC_CTRL_MODE, &ref->sync_ctrl, mode);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_ref_sync_pair_get - get sync pair reference index
|
|
+ * @ref: pointer to ref state
|
|
+ *
|
|
+ * Return: paired reference index
|
|
+ */
|
|
+static inline u8
|
|
+zl3073x_ref_sync_pair_get(const struct zl3073x_ref *ref)
|
|
+{
|
|
+ return FIELD_GET(ZL_REF_SYNC_CTRL_PAIR, ref->sync_ctrl);
|
|
+}
|
|
+
|
|
+/**
|
|
+ * zl3073x_ref_sync_pair_set - set sync pair reference index
|
|
+ * @ref: pointer to ref state
|
|
+ * @pair: paired reference index
|
|
+ */
|
|
+static inline void
|
|
+zl3073x_ref_sync_pair_set(struct zl3073x_ref *ref, u8 pair)
|
|
+{
|
|
+ FIELD_MODIFY(ZL_REF_SYNC_CTRL_PAIR, &ref->sync_ctrl, pair);
|
|
+}
|
|
+
|
|
/**
|
|
* zl3073x_ref_is_diff - check if the given input reference is differential
|
|
* @ref: pointer to ref state
|
|
diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h
|
|
index 5573d71..d425dc6 100644
|
|
--- a/drivers/dpll/zl3073x/regs.h
|
|
+++ b/drivers/dpll/zl3073x/regs.h
|
|
@@ -6,6 +6,18 @@
|
|
#include <linux/bitfield.h>
|
|
#include <linux/bits.h>
|
|
|
|
+/*
|
|
+ * Hardware limits for ZL3073x chip family
|
|
+ */
|
|
+#define ZL3073X_MAX_CHANNELS 5
|
|
+#define ZL3073X_NUM_REFS 10
|
|
+#define ZL3073X_NUM_OUTS 10
|
|
+#define ZL3073X_NUM_SYNTHS 5
|
|
+#define ZL3073X_NUM_INPUT_PINS ZL3073X_NUM_REFS
|
|
+#define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2)
|
|
+#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \
|
|
+ ZL3073X_NUM_OUTPUT_PINS)
|
|
+
|
|
/*
|
|
* Register address structure:
|
|
* ===========================
|
|
@@ -78,6 +90,8 @@
|
|
#define ZL_REG_RESET_STATUS ZL_REG(0, 0x18, 1)
|
|
#define ZL_REG_RESET_STATUS_RESET BIT(0)
|
|
|
|
+#define ZL_REG_DIE_TEMP_STATUS ZL_REG(0, 0x44, 2)
|
|
+
|
|
/*************************
|
|
* Register Page 2, Status
|
|
*************************/
|
|
@@ -199,7 +213,9 @@
|
|
#define ZL_REG_REF_SYNC_CTRL ZL_REG(10, 0x2e, 1)
|
|
#define ZL_REF_SYNC_CTRL_MODE GENMASK(2, 0)
|
|
#define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF 0
|
|
+#define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR 1
|
|
#define ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75 2
|
|
+#define ZL_REF_SYNC_CTRL_PAIR GENMASK(7, 4)
|
|
|
|
#define ZL_REG_REF_ESYNC_DIV ZL_REG(10, 0x30, 4)
|
|
#define ZL_REF_ESYNC_DIV_1HZ 0
|
|
diff --git a/drivers/dpll/zl3073x/spi.c b/drivers/dpll/zl3073x/spi.c
|
|
index 00c5e38..ad43bf9 100644
|
|
--- a/drivers/dpll/zl3073x/spi.c
|
|
+++ b/drivers/dpll/zl3073x/spi.c
|
|
@@ -22,40 +22,25 @@ static int zl3073x_spi_probe(struct spi_device *spi)
|
|
return dev_err_probe(dev, PTR_ERR(zldev->regmap),
|
|
"Failed to initialize regmap\n");
|
|
|
|
- return zl3073x_dev_probe(zldev, spi_get_device_match_data(spi));
|
|
+ return zl3073x_dev_probe(zldev);
|
|
}
|
|
|
|
static const struct spi_device_id zl3073x_spi_id[] = {
|
|
- {
|
|
- .name = "zl30731",
|
|
- .driver_data = (kernel_ulong_t)&zl30731_chip_info
|
|
- },
|
|
- {
|
|
- .name = "zl30732",
|
|
- .driver_data = (kernel_ulong_t)&zl30732_chip_info,
|
|
- },
|
|
- {
|
|
- .name = "zl30733",
|
|
- .driver_data = (kernel_ulong_t)&zl30733_chip_info,
|
|
- },
|
|
- {
|
|
- .name = "zl30734",
|
|
- .driver_data = (kernel_ulong_t)&zl30734_chip_info,
|
|
- },
|
|
- {
|
|
- .name = "zl30735",
|
|
- .driver_data = (kernel_ulong_t)&zl30735_chip_info,
|
|
- },
|
|
+ { "zl30731" },
|
|
+ { "zl30732" },
|
|
+ { "zl30733" },
|
|
+ { "zl30734" },
|
|
+ { "zl30735" },
|
|
{ /* sentinel */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(spi, zl3073x_spi_id);
|
|
|
|
static const struct of_device_id zl3073x_spi_of_match[] = {
|
|
- { .compatible = "microchip,zl30731", .data = &zl30731_chip_info },
|
|
- { .compatible = "microchip,zl30732", .data = &zl30732_chip_info },
|
|
- { .compatible = "microchip,zl30733", .data = &zl30733_chip_info },
|
|
- { .compatible = "microchip,zl30734", .data = &zl30734_chip_info },
|
|
- { .compatible = "microchip,zl30735", .data = &zl30735_chip_info },
|
|
+ { .compatible = "microchip,zl30731" },
|
|
+ { .compatible = "microchip,zl30732" },
|
|
+ { .compatible = "microchip,zl30733" },
|
|
+ { .compatible = "microchip,zl30734" },
|
|
+ { .compatible = "microchip,zl30735" },
|
|
{ /* sentinel */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, zl3073x_spi_of_match);
|
|
diff --git a/drivers/dpll/zl3073x/synth.h b/drivers/dpll/zl3073x/synth.h
|
|
index 6c55eb8..89e13ea 100644
|
|
--- a/drivers/dpll/zl3073x/synth.h
|
|
+++ b/drivers/dpll/zl3073x/synth.h
|
|
@@ -5,6 +5,7 @@
|
|
|
|
#include <linux/bitfield.h>
|
|
#include <linux/math64.h>
|
|
+#include <linux/stddef.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "regs.h"
|
|
@@ -20,11 +21,13 @@ struct zl3073x_dev;
|
|
* @ctrl: synth control
|
|
*/
|
|
struct zl3073x_synth {
|
|
- u32 freq_mult;
|
|
- u16 freq_base;
|
|
- u16 freq_m;
|
|
- u16 freq_n;
|
|
- u8 ctrl;
|
|
+ struct_group(inv, /* Invariants */
|
|
+ u32 freq_mult;
|
|
+ u16 freq_base;
|
|
+ u16 freq_m;
|
|
+ u16 freq_n;
|
|
+ u8 ctrl;
|
|
+ );
|
|
};
|
|
|
|
int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 synth_id);
|
|
@@ -32,9 +35,6 @@ int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 synth_id);
|
|
const struct zl3073x_synth *zl3073x_synth_state_get(struct zl3073x_dev *zldev,
|
|
u8 synth_id);
|
|
|
|
-int zl3073x_synth_state_set(struct zl3073x_dev *zldev, u8 synth_id,
|
|
- const struct zl3073x_synth *synth);
|
|
-
|
|
/**
|
|
* zl3073x_synth_dpll_get - get DPLL ID the synth is driven by
|
|
* @synth: pointer to synth state
|
|
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
|
|
index 6961f6e..7f8fa58 100644
|
|
--- a/include/linux/dpll.h
|
|
+++ b/include/linux/dpll.h
|
|
@@ -55,8 +55,12 @@ struct dpll_device_ops {
|
|
void *dpll_priv, u32 *factor,
|
|
struct netlink_ext_ack *extack);
|
|
|
|
- RH_KABI_RESERVE(1)
|
|
- RH_KABI_RESERVE(2)
|
|
+ RH_KABI_USE(1, int (*freq_monitor_set)(const struct dpll_device *dpll, void *dpll_priv,
|
|
+ enum dpll_feature_state state,
|
|
+ struct netlink_ext_ack *extack))
|
|
+ RH_KABI_USE(2, int (*freq_monitor_get)(const struct dpll_device *dpll, void *dpll_priv,
|
|
+ enum dpll_feature_state *state,
|
|
+ struct netlink_ext_ack *extack))
|
|
RH_KABI_RESERVE(3)
|
|
RH_KABI_RESERVE(4)
|
|
RH_KABI_RESERVE(5)
|
|
@@ -141,7 +145,10 @@ struct dpll_pin_ops {
|
|
enum dpll_pin_state *state,
|
|
struct netlink_ext_ack *extack);
|
|
|
|
- RH_KABI_RESERVE(1)
|
|
+ RH_KABI_USE(1, int (*measured_freq_get)(const struct dpll_pin *pin, void *pin_priv,
|
|
+ const struct dpll_device *dpll,
|
|
+ void *dpll_priv, u64 *measured_freq,
|
|
+ struct netlink_ext_ack *extack))
|
|
RH_KABI_RESERVE(2)
|
|
RH_KABI_RESERVE(3)
|
|
RH_KABI_RESERVE(4)
|
|
diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
|
|
index 603a88c..93614cc 100644
|
|
--- a/include/uapi/linux/dpll.h
|
|
+++ b/include/uapi/linux/dpll.h
|
|
@@ -190,7 +190,8 @@ enum dpll_pin_capabilities {
|
|
DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE = 4,
|
|
};
|
|
|
|
-#define DPLL_PHASE_OFFSET_DIVIDER 1000
|
|
+#define DPLL_PHASE_OFFSET_DIVIDER 1000
|
|
+#define DPLL_PIN_MEASURED_FREQUENCY_DIVIDER 1000
|
|
|
|
/**
|
|
* enum dpll_feature_state - Allow control (enable/disable) and status checking
|
|
@@ -217,6 +218,7 @@ enum dpll_a {
|
|
DPLL_A_CLOCK_QUALITY_LEVEL,
|
|
DPLL_A_PHASE_OFFSET_MONITOR,
|
|
DPLL_A_PHASE_OFFSET_AVG_FACTOR,
|
|
+ DPLL_A_FREQUENCY_MONITOR,
|
|
|
|
__DPLL_A_MAX,
|
|
DPLL_A_MAX = (__DPLL_A_MAX - 1)
|
|
@@ -253,6 +255,7 @@ enum dpll_a_pin {
|
|
DPLL_A_PIN_REFERENCE_SYNC,
|
|
DPLL_A_PIN_PHASE_ADJUST_GRAN,
|
|
DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT,
|
|
+ DPLL_A_PIN_MEASURED_FREQUENCY,
|
|
|
|
__DPLL_A_PIN_MAX,
|
|
DPLL_A_PIN_MAX = (__DPLL_A_PIN_MAX - 1)
|