From nobody Mon Jun 8 07:22:52 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0F2F533AD9D for ; Sun, 31 May 2026 19:44:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780256684; cv=none; b=vB4ajR2CeBQ/3NnWWJp03kQkdb8fN9NWXVWWSEd2GMmlaoA6WVjWt6jsd+EweFXDHbViD/+cve5YCjtGaCG2XsTFWTDvdHVltSIbrmPZKQXqurHwkg/M0Utoq3ItVxcob0iTJ3wPw2zz68A/lHrQF1dHLXgiq0xppdT/owXvPZg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780256684; c=relaxed/simple; bh=j4Jjp6nEOjEmwAmdRBZ09+RgL1Y8Yi6keEPpAPUlVAI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=axHZlcE87041fR3LF4DWfYHYOkyNT9OZ3KJ0nQljc4oceQbtaZaICQ9Mhbof0MrtJ0oz94KShcoTdIisec3muiUM2o8lDQVamvmrX+3jZ71NS5wdb676BeS95d48jXy2GOw5f8ziYBH3IcYN54Jec6DwxHv5sVXtn0NjKV0+2TI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=izIIz66S; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="izIIz66S" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780256682; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ow8OirZdS6gb+ZP38FzHIeBgDOLzreU2b2dwfvbHcfw=; b=izIIz66SgOJcD4Djxm+lwYcrNBQhIgrFTOhTCLTW/sdj8bjwS4PqBVBeu80uRTxW5FOEwN +s8Qddz1zqnq0A/fPfT4FKvi/WVlce+VDYAKDEg7SiBX6r/9rRT0h3G2yIiFPzAj3FnlIF ujrlHJYsNJPBLMLj7swjHfzpZKRrZN8= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-635-Ij8oNTy9PAyTKMg1bk6ihA-1; Sun, 31 May 2026 15:44:36 -0400 X-MC-Unique: Ij8oNTy9PAyTKMg1bk6ihA-1 X-Mimecast-MFC-AGG-ID: Ij8oNTy9PAyTKMg1bk6ihA_1780256674 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 170021956088; Sun, 31 May 2026 19:44:34 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.44.32.62]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 71D861955F22; Sun, 31 May 2026 19:44:29 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Jiri Pirko , Arkadiusz Kubalewski , "David S. Miller" , Donald Hunter , Eric Dumazet , Jakub Kicinski , Jiri Pirko , Michal Schmidt , Paolo Abeni , Pasi Vaananen , Petr Oros , Prathosh Satish , Simon Horman , Vadim Fedorenko , linux-kernel@vger.kernel.org Subject: [PATCH net-next v5 1/4] dpll: add DPLL_PIN_TYPE_INT_NCO pin type Date: Sun, 31 May 2026 21:44:20 +0200 Message-ID: <20260531194423.383366-2-ivecera@redhat.com> In-Reply-To: <20260531194423.383366-1-ivecera@redhat.com> References: <20260531194423.383366-1-ivecera@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" Add DPLL_PIN_TYPE_INT_NCO pin type for virtual pins representing the NCO mode of a DPLL. When connected as a DPLL input, the DPLL enters NCO mode where the output frequency is adjusted by the host via the PTP clock interface. Update the fractional-frequency-offset and fractional-frequency- offset-ppt attribute documentation to note that for INT_NCO pins these attributes represent the DPLL's current output frequency offset from its nominal frequency. Reviewed-by: Jiri Pirko Signed-off-by: Ivan Vecera --- v2: - Clarify int-nco pin type documentation to describe frequency control via the PTP clock interface instead of generic "controlled by the host". - Tighten FFO attribute documentation for INT_NCO pins to describe the DPLL's output frequency offset from nominal frequency. - Mention both fractional-frequency-offset (PPM) and fractional-frequency-offset-ppt attributes in the commit message. --- Documentation/netlink/specs/dpll.yaml | 13 +++++++++++++ drivers/dpll/dpll_nl.c | 2 +- include/uapi/linux/dpll.h | 4 ++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/= specs/dpll.yaml index 91a172617b3a9..5cdb93e8649a0 100644 --- a/Documentation/netlink/specs/dpll.yaml +++ b/Documentation/netlink/specs/dpll.yaml @@ -162,6 +162,13 @@ definitions: - name: gnss doc: GNSS recovered clock + - + name: int-nco + doc: | + Device internal numerically controlled oscillator. + When connected as a DPLL input, the DPLL enters NCO mode + where the output frequency is adjusted by the host via + the PTP clock interface. render-max: true - type: enum @@ -453,6 +460,9 @@ attribute-sets: offset on the media associated with the pin. Inside the pin-parent-device nest it represents the frequency offset between the pin and its parent DPLL device. + For pins of type PIN_TYPE_INT_NCO this represents + the DPLL's current output frequency offset from its + nominal frequency. Value is in PPM (parts per million). This is a lower-precision version of fractional-frequency-offset-ppt. @@ -499,6 +509,9 @@ attribute-sets: offset on the media associated with the pin. Inside the pin-parent-device nest it represents the frequency offset between the pin and its parent DPLL device. + For pins of type PIN_TYPE_INT_NCO this represents + the DPLL's current output frequency offset from its + nominal frequency. Value is in PPT (parts per trillion, 10^-12). This is a higher-precision version of fractional-frequency-offset. diff --git a/drivers/dpll/dpll_nl.c b/drivers/dpll/dpll_nl.c index b1d9182c7802f..2dab99202764f 100644 --- a/drivers/dpll/dpll_nl.c +++ b/drivers/dpll/dpll_nl.c @@ -61,7 +61,7 @@ static const struct nla_policy dpll_pin_id_get_nl_policy[= DPLL_A_PIN_TYPE + 1] =3D [DPLL_A_PIN_BOARD_LABEL] =3D { .type =3D NLA_NUL_STRING, }, [DPLL_A_PIN_PANEL_LABEL] =3D { .type =3D NLA_NUL_STRING, }, [DPLL_A_PIN_PACKAGE_LABEL] =3D { .type =3D NLA_NUL_STRING, }, - [DPLL_A_PIN_TYPE] =3D NLA_POLICY_RANGE(NLA_U32, 1, 5), + [DPLL_A_PIN_TYPE] =3D NLA_POLICY_RANGE(NLA_U32, 1, 6), }; =20 /* DPLL_CMD_PIN_GET - do */ diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h index cb363cccf2e2a..9245827de3cfd 100644 --- a/include/uapi/linux/dpll.h +++ b/include/uapi/linux/dpll.h @@ -127,6 +127,9 @@ enum dpll_type { * @DPLL_PIN_TYPE_SYNCE_ETH_PORT: ethernet port PHY's recovered clock * @DPLL_PIN_TYPE_INT_OSCILLATOR: device internal oscillator * @DPLL_PIN_TYPE_GNSS: GNSS recovered clock + * @DPLL_PIN_TYPE_INT_NCO: Device internal numerically controlled oscillat= or. + * When connected as a DPLL input, the DPLL enters NCO mode where the ou= tput + * frequency is adjusted by the host via the PTP clock interface. */ enum dpll_pin_type { DPLL_PIN_TYPE_MUX =3D 1, @@ -134,6 +137,7 @@ enum dpll_pin_type { DPLL_PIN_TYPE_SYNCE_ETH_PORT, DPLL_PIN_TYPE_INT_OSCILLATOR, DPLL_PIN_TYPE_GNSS, + DPLL_PIN_TYPE_INT_NCO, =20 /* private: */ __DPLL_PIN_TYPE_MAX, --=20 2.53.0 From nobody Mon Jun 8 07:22:52 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C5DD8332601 for ; Sun, 31 May 2026 19:44:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780256688; cv=none; b=NtZpfhaMZ3TYJSM9LbgWHVmZVOSde2u3rfPjfE49+iuxnwLBRE3wgRamD/gJ+M1ZBk/CaF1viQBWGIFNjLt/Tk9e6USTawECluGP3ii6RxTZFXCexUwaxECD61yivCE39kbs782POL9lA35SUwwOecYotxTb+7Uuqk796YIPNCg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780256688; c=relaxed/simple; bh=59KSa1Og8pFBLuw2SFpkoZ8zQExO8+M5li+owmnWJ2Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=p6vmUNTc3CDtXAhzEiGtX2ySbuE2l4ah67QMeLQSscxud7vjyCa2urhSXdZzZcuMUdspvWQDBhZ+gaUT8p+4vljsGHw9jpTIoNF4RB3bp+zwHnP5kyEP5tAk3wyOrRsontj5WXNcDbyWWdMejZyrCqOEEpGRJfLFfLDpYQpXBIs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=AX6lrn5J; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="AX6lrn5J" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780256685; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=kfIpV7qt/1OjfnIZ3tjJZYz95w78ikfiJgqE9dVyiaE=; b=AX6lrn5JyQSV42k209H0zBpJKuOOVOpm4J7h6p1HCyHFyVyNHXv+p0oWPUXyutiSrJiFHX Vgra0LZ5OliH6HZGoUUcsTzPMZMteZ7sqWAhDvsLp8T2/IeVHSdQjkUC7iEwfdnSrzLyyZ 2p6YURCjHmu3u+aIUawbRlJZxQ0SPP8= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-255-ksONC98dNp-ce3G8wdbwHA-1; Sun, 31 May 2026 15:44:41 -0400 X-MC-Unique: ksONC98dNp-ce3G8wdbwHA-1 X-Mimecast-MFC-AGG-ID: ksONC98dNp-ce3G8wdbwHA_1780256679 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E5834195608B; Sun, 31 May 2026 19:44:38 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.44.32.62]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 7CBAF1956053; Sun, 31 May 2026 19:44:34 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Arkadiusz Kubalewski , "David S. Miller" , Donald Hunter , Eric Dumazet , Jakub Kicinski , Jiri Pirko , Michal Schmidt , Paolo Abeni , Pasi Vaananen , Petr Oros , Prathosh Satish , Simon Horman , Vadim Fedorenko , linux-kernel@vger.kernel.org Subject: [PATCH net-next v5 2/4] dpll: zl3073x: use per-operation poll timeouts Date: Sun, 31 May 2026 21:44:21 +0200 Message-ID: <20260531194423.383366-3-ivecera@redhat.com> In-Reply-To: <20260531194423.383366-1-ivecera@redhat.com> References: <20260531194423.383366-1-ivecera@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" Replace the single 2s timeout in zl3073x_poll_zero_u8() with a per-caller timeout parameter. Different HW operations have different expected completion times so using per-operation timeouts improves error detection. The timeout values are based on proprietary source code provided by Microchip and own measurement. Signed-off-by: Ivan Vecera --- drivers/dpll/zl3073x/chan.c | 6 ++++-- drivers/dpll/zl3073x/core.c | 29 +++++++++++++++++------------ drivers/dpll/zl3073x/core.h | 10 +++++++++- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/drivers/dpll/zl3073x/chan.c b/drivers/dpll/zl3073x/chan.c index 2fe3c3da84bb5..677a920c16254 100644 --- a/drivers/dpll/zl3073x/chan.c +++ b/drivers/dpll/zl3073x/chan.c @@ -33,7 +33,8 @@ int zl3073x_chan_state_update(struct zl3073x_dev *zldev, = u8 index) =20 /* Read df_offset vs tracked reference */ rc =3D zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index), - ZL_DPLL_DF_READ_SEM); + ZL_DPLL_DF_READ_SEM, + ZL_POLL_DF_READ_TIMEOUT_US); if (rc) return rc; =20 @@ -43,7 +44,8 @@ int zl3073x_chan_state_update(struct zl3073x_dev *zldev, = u8 index) return rc; =20 rc =3D zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index), - ZL_DPLL_DF_READ_SEM); + ZL_DPLL_DF_READ_SEM, + ZL_POLL_DF_READ_TIMEOUT_US); if (rc) return rc; =20 diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index 8e6416a4741de..0b2050aa2ed92 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -311,17 +311,17 @@ int zl3073x_write_u48(struct zl3073x_dev *zldev, unsi= gned int reg, u64 val) * @zldev: zl3073x device pointer * @reg: register to poll (has to be 8bit register) * @mask: bit mask for polling + * @timeout_us: timeout in microseconds * * Waits for bits specified by @mask in register @reg value to be cleared * by the device. * * Returns: 0 on success, <0 on error */ -int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 m= ask) +int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, + u8 mask, unsigned int timeout_us) { - /* Register polling sleep & timeout */ -#define ZL_POLL_SLEEP_US 10 -#define ZL_POLL_TIMEOUT_US 2000000 +#define ZL_POLL_SLEEP_US 10 unsigned int val; =20 /* Check the register is 8bit */ @@ -335,7 +335,7 @@ int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, uns= igned int reg, u8 mask) reg =3D ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET; =20 return regmap_read_poll_timeout(zldev->regmap, reg, val, !(val & mask), - ZL_POLL_SLEEP_US, ZL_POLL_TIMEOUT_US); + ZL_POLL_SLEEP_US, timeout_us); } =20 int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_va= l, @@ -354,7 +354,8 @@ int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned i= nt op_reg, u8 op_val, return rc; =20 /* Wait for the operation to actually finish */ - return zl3073x_poll_zero_u8(zldev, op_reg, op_val); + return zl3073x_poll_zero_u8(zldev, op_reg, op_val, + ZL_POLL_MB_TIMEOUT_US); } =20 /** @@ -377,8 +378,8 @@ zl3073x_do_hwreg_op(struct zl3073x_dev *zldev, u8 op) return rc; =20 /* Poll for completion - pending bit cleared */ - return zl3073x_poll_zero_u8(zldev, ZL_REG_HWREG_OP, - ZL_HWREG_OP_PENDING); + return zl3073x_poll_zero_u8(zldev, ZL_REG_HWREG_OP, ZL_HWREG_OP_PENDING, + ZL_POLL_HWREG_TIMEOUT_US); } =20 /** @@ -609,7 +610,8 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev= *zldev, int channel) * to be zero to ensure that the measured data are coherent. */ rc =3D zl3073x_poll_zero_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST, - ZL_REF_PHASE_ERR_READ_RQST_RD); + ZL_REF_PHASE_ERR_READ_RQST_RD, + ZL_POLL_PHASE_ERR_TIMEOUT_US); if (rc) return rc; =20 @@ -628,7 +630,8 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev= *zldev, int channel) =20 /* Wait for finish */ return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_PHASE_ERR_READ_RQST, - ZL_REF_PHASE_ERR_READ_RQST_RD); + ZL_REF_PHASE_ERR_READ_RQST_RD, + ZL_POLL_PHASE_ERR_TIMEOUT_US); } =20 /** @@ -648,7 +651,8 @@ zl3073x_ref_freq_meas_latch(struct zl3073x_dev *zldev, = u8 type) =20 /* Wait for previous measurement to finish */ rc =3D zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, - ZL_REF_FREQ_MEAS_CTRL); + ZL_REF_FREQ_MEAS_CTRL, + ZL_POLL_FREQ_MEAS_TIMEOUT_US); if (rc) return rc; =20 @@ -669,7 +673,8 @@ zl3073x_ref_freq_meas_latch(struct zl3073x_dev *zldev, = u8 type) =20 /* Wait for finish */ return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, - ZL_REF_FREQ_MEAS_CTRL); + ZL_REF_FREQ_MEAS_CTRL, + ZL_POLL_FREQ_MEAS_TIMEOUT_US); } =20 /** diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h index addba378b0df4..6b55a05a222ed 100644 --- a/drivers/dpll/zl3073x/core.h +++ b/drivers/dpll/zl3073x/core.h @@ -7,6 +7,7 @@ #include #include #include +#include #include =20 #include "chan.h" @@ -19,6 +20,12 @@ struct device; struct regmap; struct zl3073x_dpll; =20 +/* Per-operation poll timeouts */ +#define ZL_POLL_DF_READ_TIMEOUT_US (25 * USEC_PER_MSEC) +#define ZL_POLL_FREQ_MEAS_TIMEOUT_US (50 * USEC_PER_MSEC) +#define ZL_POLL_HWREG_TIMEOUT_US (50 * USEC_PER_MSEC) +#define ZL_POLL_MB_TIMEOUT_US (30 * USEC_PER_MSEC) +#define ZL_POLL_PHASE_ERR_TIMEOUT_US (50 * USEC_PER_MSEC) =20 enum zl3073x_flags { ZL3073X_FLAG_REF_PHASE_COMP_32_BIT, @@ -127,7 +134,8 @@ struct zl3073x_hwreg_seq_item { =20 int zl3073x_mb_op(struct zl3073x_dev *zldev, unsigned int op_reg, u8 op_va= l, unsigned int mask_reg, u16 mask_val); -int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 m= ask); +int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, + u8 mask, unsigned int timeout_us); int zl3073x_read_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 *val); int zl3073x_read_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 *val= ); int zl3073x_read_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 *val= ); --=20 2.53.0 From nobody Mon Jun 8 07:22:52 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2F8FE33263E for ; Sun, 31 May 2026 19:44:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780256693; cv=none; b=XitZo4d51FyhzGIbSC9sWh11Ag6VmnS+7tNVK3fDHeJuC4WLyD40hwI/dMYhc/NibklWBmP/GgdCvA4mxCEb5yR++0+5tiGvtLi+c9cBVfpIm62fMAa7oSRkFs8AEo5ZuVv/wC1zgPAi8H/K/0KI4GVk5Zswlfj++ZcpSjm4d6w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780256693; c=relaxed/simple; bh=4yLlvYfaFwDr/9y90oSJp9mkWD4wvjhdzc+F1I1kN38=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fSoPaP9tnn/G5R/4actAC65DDbj559Kzc5oZyZJnC/o2m+im/HPxI2AFQ3w5yz9t181tAi7KLG000QuUri6YExgPxBJ7IKoX8tJRiIBnKOZen5rh/rmsdrTefzcKYsOAau4cGdEEEKUUmUhIiJNteG5B22S8u3KmkPDkU+3yT9U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Ic7xyQcK; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Ic7xyQcK" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780256690; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=UDen/ZEuNGMJ7ROimHwADk63BYCJZNPvbVaokLBrNoI=; b=Ic7xyQcKTEAHB+j3D5sa1T8Rjw6H2Y+S8LkGMhcuVplZJQrQYT/vUigwDxLTj4d35rR1c3 AJM+rZaTIHqa6BvWNHyRG67B5Op42gZTPfayxDQClzEF5FNRrbrtcDPEJ2LxnTDJZF+G6J tUN3iy4Xgp7IGQ0wVFDGKsBVxdKRkEc= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-287-Q9bIJQHqNDipag0OzI6TPg-1; Sun, 31 May 2026 15:44:46 -0400 X-MC-Unique: Q9bIJQHqNDipag0OzI6TPg-1 X-Mimecast-MFC-AGG-ID: Q9bIJQHqNDipag0OzI6TPg_1780256684 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5A77C19560B4; Sun, 31 May 2026 19:44:44 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.44.32.62]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 580FD1956053; Sun, 31 May 2026 19:44:39 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Arkadiusz Kubalewski , "David S. Miller" , Donald Hunter , Eric Dumazet , Jakub Kicinski , Jiri Pirko , Michal Schmidt , Paolo Abeni , Pasi Vaananen , Petr Oros , Prathosh Satish , Simon Horman , Vadim Fedorenko , linux-kernel@vger.kernel.org Subject: [PATCH net-next v5 3/4] dpll: zl3073x: add per-DPLL serialization lock Date: Sun, 31 May 2026 21:44:22 +0200 Message-ID: <20260531194423.383366-4-ivecera@redhat.com> In-Reply-To: <20260531194423.383366-1-ivecera@redhat.com> References: <20260531194423.383366-1-ivecera@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" Add a per-DPLL mutex that serializes all operations on a given DPLL channel across DPLL netlink callbacks, the periodic kthread worker, and (in subsequent patches) PTP clock callbacks. All DPLL pin and device callbacks that access mutable state take the lock as the first operation. The periodic worker holds it for the entire check cycle of each channel, deferring change notifications until after the lock is released to avoid ABBA deadlock with dpll_lock. This establishes the lock ordering: dpll_lock (subsystem, outer) -> zldpll->lock (driver, inner). Move zl3073x_chan_state_update() from the per-device zl3073x_dev_chan_states_update() loop into the per-DPLL zl3073x_dpll_changes_check() so it runs under zldpll->lock. This serializes df_offset writes with all readers and eliminates the need for separate df_offset synchronization. Change pin->freq_offset from atomic64_t to plain s64 since all readers and writers are now serialized by zldpll->lock, making atomic access unnecessary. Signed-off-by: Ivan Vecera --- drivers/dpll/zl3073x/core.c | 15 --- drivers/dpll/zl3073x/dpll.c | 188 ++++++++++++++++++++++++++++-------- drivers/dpll/zl3073x/dpll.h | 2 + 3 files changed, 149 insertions(+), 56 deletions(-) diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index 0b2050aa2ed92..27c71807e4eff 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -567,19 +567,7 @@ zl3073x_dev_ref_states_update(struct zl3073x_dev *zlde= v) } } =20 -static void -zl3073x_dev_chan_states_update(struct zl3073x_dev *zldev) -{ - int i, rc; =20 - for (i =3D 0; i < zldev->info->num_channels; i++) { - rc =3D zl3073x_chan_state_update(zldev, i); - if (rc) - dev_warn(zldev->dev, - "Failed to get DPLL%u state: %pe\n", i, - ERR_PTR(rc)); - } -} =20 /** * zl3073x_ref_phase_offsets_update - update reference phase offsets @@ -720,9 +708,6 @@ zl3073x_dev_periodic_work(struct kthread_work *work) /* Update input references' states */ zl3073x_dev_ref_states_update(zldev); =20 - /* Update DPLL channels' states */ - zl3073x_dev_chan_states_update(zldev); - /* Update DPLL-to-connected-ref phase offsets registers */ rc =3D zl3073x_ref_phase_offsets_update(zldev, -1); if (rc) diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 5e58ded5734d7..4bee3d0c46593 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only =20 -#include #include #include #include @@ -58,7 +57,7 @@ struct zl3073x_dpll_pin { s32 phase_gran; enum dpll_pin_operstate operstate; s64 phase_offset; - atomic64_t freq_offset; + s64 freq_offset; u32 measured_freq; }; =20 @@ -134,6 +133,8 @@ zl3073x_dpll_input_pin_esync_get(const struct dpll_pin = *dpll_pin, const struct zl3073x_ref *ref; u8 ref_id; =20 + guard(mutex)(&zldpll->lock); + ref_id =3D zl3073x_input_pin_ref_get(pin->id); ref =3D zl3073x_ref_state_get(zldev, ref_id); =20 @@ -170,6 +171,8 @@ zl3073x_dpll_input_pin_esync_set(const struct dpll_pin = *dpll_pin, struct zl3073x_ref ref; u8 ref_id, sync_mode; =20 + guard(mutex)(&zldpll->lock); + ref_id =3D zl3073x_input_pin_ref_get(pin->id); ref =3D *zl3073x_ref_state_get(zldev, ref_id); =20 @@ -205,6 +208,8 @@ zl3073x_dpll_input_pin_ref_sync_get(const struct dpll_p= in *dpll_pin, const struct zl3073x_ref *ref; u8 ref_id, mode, pair; =20 + guard(mutex)(&zldpll->lock); + ref_id =3D zl3073x_input_pin_ref_get(pin->id); ref =3D zl3073x_ref_state_get(zldev, ref_id); mode =3D zl3073x_ref_sync_mode_get(ref); @@ -236,6 +241,8 @@ zl3073x_dpll_input_pin_ref_sync_set(const struct dpll_p= in *dpll_pin, struct zl3073x_ref ref; int rc; =20 + guard(mutex)(&zldpll->lock); + ref_id =3D zl3073x_input_pin_ref_get(pin->id); sync_ref_id =3D zl3073x_input_pin_ref_get(sync_pin->id); ref =3D *zl3073x_ref_state_get(zldev, ref_id); @@ -299,12 +306,15 @@ zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin = *dpll_pin, void *pin_priv, struct dpll_ffo_param *ffo, struct netlink_ext_ack *extack) { + struct zl3073x_dpll *zldpll =3D dpll_priv; struct zl3073x_dpll_pin *pin =3D pin_priv; =20 + guard(mutex)(&zldpll->lock); + if (pin->operstate !=3D DPLL_PIN_OPERSTATE_ACTIVE) return -ENODATA; =20 - ffo->ffo =3D atomic64_read(&pin->freq_offset); + ffo->ffo =3D pin->freq_offset; =20 return 0; } @@ -316,8 +326,11 @@ zl3073x_dpll_input_pin_measured_freq_get(const struct = dpll_pin *dpll_pin, void *dpll_priv, u64 *measured_freq, struct netlink_ext_ack *extack) { + struct zl3073x_dpll *zldpll =3D dpll_priv; struct zl3073x_dpll_pin *pin =3D pin_priv; =20 + guard(mutex)(&zldpll->lock); + *measured_freq =3D pin->measured_freq; *measured_freq *=3D DPLL_PIN_MEASURED_FREQUENCY_DIVIDER; =20 @@ -335,6 +348,8 @@ zl3073x_dpll_input_pin_frequency_get(const struct dpll_= pin *dpll_pin, struct zl3073x_dpll_pin *pin =3D pin_priv; u8 ref_id; =20 + guard(mutex)(&zldpll->lock); + ref_id =3D zl3073x_input_pin_ref_get(pin->id); *frequency =3D zl3073x_dev_ref_freq_get(zldpll->dev, ref_id); =20 @@ -354,6 +369,8 @@ zl3073x_dpll_input_pin_frequency_set(const struct dpll_= pin *dpll_pin, struct zl3073x_ref ref; u8 ref_id; =20 + guard(mutex)(&zldpll->lock); + /* Get reference state */ ref_id =3D zl3073x_input_pin_ref_get(pin->id); ref =3D *zl3073x_ref_state_get(zldev, ref_id); @@ -402,6 +419,8 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dp= ll_pin *dpll_pin, u8 conn_id, ref_id; s64 ref_phase; =20 + guard(mutex)(&zldpll->lock); + /* Get currently connected reference */ conn_id =3D zl3073x_dpll_connected_ref_get(zldpll); =20 @@ -459,6 +478,8 @@ zl3073x_dpll_input_pin_phase_adjust_get(const struct dp= ll_pin *dpll_pin, s64 phase_comp; u8 ref_id; =20 + guard(mutex)(&zldpll->lock); + /* Read reference configuration */ ref_id =3D zl3073x_input_pin_ref_get(pin->id); ref =3D zl3073x_ref_state_get(zldev, ref_id); @@ -491,6 +512,8 @@ zl3073x_dpll_input_pin_phase_adjust_set(const struct dp= ll_pin *dpll_pin, struct zl3073x_ref ref; u8 ref_id; =20 + guard(mutex)(&zldpll->lock); + /* Read reference configuration */ ref_id =3D zl3073x_input_pin_ref_get(pin->id); ref =3D *zl3073x_ref_state_get(zldev, ref_id); @@ -524,6 +547,8 @@ zl3073x_dpll_ref_operstate_get(struct zl3073x_dpll_pin = *pin, const struct zl3073x_ref *ref; u8 ref_id; =20 + lockdep_assert_held(&zldpll->lock); + ref_id =3D zl3073x_input_pin_ref_get(pin->id); =20 /* Check if this pin is the currently locked reference */ @@ -557,6 +582,8 @@ zl3073x_dpll_input_pin_state_on_dpll_get(const struct d= pll_pin *dpll_pin, const struct zl3073x_chan *chan; u8 mode, ref; =20 + guard(mutex)(&zldpll->lock); + chan =3D zl3073x_chan_state_get(zldpll->dev, zldpll->id); ref =3D zl3073x_input_pin_ref_get(pin->id); mode =3D zl3073x_chan_mode_get(chan); @@ -590,8 +617,11 @@ zl3073x_dpll_input_pin_operstate_on_dpll_get(const str= uct dpll_pin *dpll_pin, enum dpll_pin_operstate *operstate, struct netlink_ext_ack *extack) { + struct zl3073x_dpll *zldpll =3D dpll_priv; struct zl3073x_dpll_pin *pin =3D pin_priv; =20 + guard(mutex)(&zldpll->lock); + return zl3073x_dpll_ref_operstate_get(pin, operstate); } =20 @@ -607,7 +637,9 @@ zl3073x_dpll_input_pin_state_on_dpll_set(const struct d= pll_pin *dpll_pin, struct zl3073x_dpll_pin *pin =3D pin_priv; struct zl3073x_chan chan; u8 mode, ref; - int rc; + int rc =3D 0; + + mutex_lock(&zldpll->lock); =20 chan =3D *zl3073x_chan_state_get(zldpll->dev, zldpll->id); ref =3D zl3073x_input_pin_ref_get(pin->id); @@ -649,13 +681,13 @@ zl3073x_dpll_input_pin_state_on_dpll_set(const struct= dpll_pin *dpll_pin, case ZL_DPLL_MODE_REFSEL_MODE_AUTO: if (state =3D=3D DPLL_PIN_STATE_SELECTABLE) { if (zl3073x_chan_ref_is_selectable(&chan, ref)) - return 0; /* Pin is already selectable */ + goto unlock; /* Pin is already selectable */ =20 /* Restore pin priority in HW */ zl3073x_chan_ref_prio_set(&chan, ref, pin->prio); } else if (state =3D=3D DPLL_PIN_STATE_DISCONNECTED) { if (!zl3073x_chan_ref_is_selectable(&chan, ref)) - return 0; /* Pin is already disconnected */ + goto unlock; /* Pin is already disconnected */ =20 /* Set pin priority to none in HW */ zl3073x_chan_ref_prio_set(&chan, ref, @@ -668,18 +700,20 @@ zl3073x_dpll_input_pin_state_on_dpll_set(const struct= dpll_pin *dpll_pin, /* In other modes we cannot change input reference */ NL_SET_ERR_MSG(extack, "Pin state cannot be changed in current mode"); - return -EOPNOTSUPP; + rc =3D -EOPNOTSUPP; + goto unlock; } =20 /* Commit DPLL channel changes */ rc =3D zl3073x_chan_state_set(zldpll->dev, zldpll->id, &chan); - if (rc) - return rc; + goto unlock; =20 - return 0; invalid_state: NL_SET_ERR_MSG_MOD(extack, "Invalid pin state for this device mode"); - return -EINVAL; + rc =3D -EINVAL; +unlock: + mutex_unlock(&zldpll->lock); + return rc; } =20 static int @@ -687,8 +721,11 @@ zl3073x_dpll_input_pin_prio_get(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 =3D dpll_priv; struct zl3073x_dpll_pin *pin =3D pin_priv; =20 + guard(mutex)(&zldpll->lock); + *prio =3D pin->prio; =20 return 0; @@ -705,6 +742,8 @@ zl3073x_dpll_input_pin_prio_set(const struct dpll_pin *= dpll_pin, void *pin_priv, u8 ref; int rc; =20 + guard(mutex)(&zldpll->lock); + if (prio > ZL_DPLL_REF_PRIO_MAX) return -EINVAL; =20 @@ -740,6 +779,8 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin= *dpll_pin, u32 synth_freq, out_freq; u8 out_id; =20 + guard(mutex)(&zldpll->lock); + out_id =3D zl3073x_output_pin_out_get(pin->id); out =3D zl3073x_out_state_get(zldev, out_id); =20 @@ -797,6 +838,8 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin= *dpll_pin, u32 synth_freq; u8 out_id; =20 + guard(mutex)(&zldpll->lock); + out_id =3D zl3073x_output_pin_out_get(pin->id); out =3D *zl3073x_out_state_get(zldev, out_id); =20 @@ -817,7 +860,7 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin= *dpll_pin, =20 /* If esync is being disabled just write mailbox and finish */ if (!freq) - goto write_mailbox; + return zl3073x_out_state_set(zldev, out_id, &out); =20 /* Get attached synth frequency */ synth =3D zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(&out)); @@ -834,7 +877,6 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin= *dpll_pin, */ out.esync_n_width =3D out.div / 2; =20 -write_mailbox: /* Commit output configuration */ return zl3073x_out_state_set(zldev, out_id, &out); } @@ -849,6 +891,8 @@ zl3073x_dpll_output_pin_frequency_get(const struct dpll= _pin *dpll_pin, struct zl3073x_dpll *zldpll =3D dpll_priv; struct zl3073x_dpll_pin *pin =3D pin_priv; =20 + guard(mutex)(&zldpll->lock); + *frequency =3D zl3073x_dev_output_pin_freq_get(zldpll->dev, pin->id); =20 return 0; @@ -869,6 +913,8 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll= _pin *dpll_pin, struct zl3073x_out out; u8 out_id; =20 + guard(mutex)(&zldpll->lock); + out_id =3D zl3073x_output_pin_out_get(pin->id); out =3D *zl3073x_out_state_get(zldev, out_id); =20 @@ -942,6 +988,8 @@ zl3073x_dpll_output_pin_phase_adjust_get(const struct d= pll_pin *dpll_pin, const struct zl3073x_out *out; u8 out_id; =20 + guard(mutex)(&zldpll->lock); + out_id =3D zl3073x_output_pin_out_get(pin->id); out =3D zl3073x_out_state_get(zldev, out_id); =20 @@ -965,6 +1013,8 @@ zl3073x_dpll_output_pin_phase_adjust_set(const struct = dpll_pin *dpll_pin, struct zl3073x_out out; u8 out_id; =20 + guard(mutex)(&zldpll->lock); + out_id =3D zl3073x_output_pin_out_get(pin->id); out =3D *zl3073x_out_state_get(zldev, out_id); =20 @@ -998,6 +1048,8 @@ zl3073x_dpll_temp_get(const struct dpll_device *dpll, = void *dpll_priv, u16 val; int rc; =20 + guard(mutex)(&zldpll->lock); + rc =3D zl3073x_read_u16(zldev, ZL_REG_DIE_TEMP_STATUS, &val); if (rc) return rc; @@ -1009,14 +1061,13 @@ zl3073x_dpll_temp_get(const struct dpll_device *dpl= l, void *dpll_priv, } =20 static int -zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_pr= iv, - enum dpll_lock_status *status, - enum dpll_lock_status_error *status_error, - struct netlink_ext_ack *extack) +__zl3073x_dpll_lock_status_get(struct zl3073x_dpll *zldpll, + enum dpll_lock_status *status) { - struct zl3073x_dpll *zldpll =3D dpll_priv; const struct zl3073x_chan *chan; =20 + lockdep_assert_held(&zldpll->lock); + chan =3D zl3073x_chan_state_get(zldpll->dev, zldpll->id); =20 switch (zl3073x_chan_mode_get(chan)) { @@ -1052,6 +1103,19 @@ zl3073x_dpll_lock_status_get(const struct dpll_devic= e *dpll, void *dpll_priv, return 0; } =20 +static int +zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_pr= iv, + enum dpll_lock_status *status, + enum dpll_lock_status_error *status_error, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + + guard(mutex)(&zldpll->lock); + + return __zl3073x_dpll_lock_status_get(zldpll, status); +} + static int zl3073x_dpll_supported_modes_get(const struct dpll_device *dpll, void *dpll_priv, unsigned long *modes, @@ -1060,6 +1124,8 @@ zl3073x_dpll_supported_modes_get(const struct dpll_de= vice *dpll, struct zl3073x_dpll *zldpll =3D dpll_priv; const struct zl3073x_chan *chan; =20 + guard(mutex)(&zldpll->lock); + chan =3D zl3073x_chan_state_get(zldpll->dev, zldpll->id); =20 /* We support switching between automatic and manual mode, except in @@ -1082,6 +1148,8 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll,= void *dpll_priv, struct zl3073x_dpll *zldpll =3D dpll_priv; const struct zl3073x_chan *chan; =20 + guard(mutex)(&zldpll->lock); + chan =3D zl3073x_chan_state_get(zldpll->dev, zldpll->id); =20 switch (zl3073x_chan_mode_get(chan)) { @@ -1112,6 +1180,8 @@ zl3073x_dpll_phase_offset_avg_factor_get(const struct= dpll_device *dpll, { struct zl3073x_dpll *zldpll =3D dpll_priv; =20 + guard(mutex)(&zldpll->lock); + *factor =3D zl3073x_dev_phase_avg_factor_get(zldpll->dev); =20 return 0; @@ -1125,9 +1195,12 @@ zl3073x_dpll_phase_offset_avg_factor_set(const struc= t dpll_device *dpll, struct zl3073x_dpll *item, *zldpll =3D dpll_priv; int rc; =20 + mutex_lock(&zldpll->lock); + if (factor > 15) { NL_SET_ERR_MSG_FMT(extack, "Phase offset average factor has to be from range <0,15>"); + mutex_unlock(&zldpll->lock); return -EINVAL; } =20 @@ -1135,11 +1208,14 @@ zl3073x_dpll_phase_offset_avg_factor_set(const stru= ct dpll_device *dpll, if (rc) { NL_SET_ERR_MSG_FMT(extack, "Failed to set phase offset averaging factor"); + mutex_unlock(&zldpll->lock); return rc; } =20 - /* The averaging factor is common for all DPLL channels so after change - * we have to send a notification for other DPLL devices. + mutex_unlock(&zldpll->lock); + + /* The averaging factor is common for all DPLL channels so after + * change we have to send a notification for other DPLL devices. */ list_for_each_entry(item, &zldpll->dev->dplls, list) { struct dpll_device *dpll_dev =3D READ_ONCE(item->dpll_dev); @@ -1160,6 +1236,8 @@ zl3073x_dpll_mode_set(const struct dpll_device *dpll,= void *dpll_priv, u8 hw_mode, ref; int rc; =20 + guard(mutex)(&zldpll->lock); + chan =3D *zl3073x_chan_state_get(zldpll->dev, zldpll->id); ref =3D zl3073x_chan_refsel_ref_get(&chan); =20 @@ -1221,6 +1299,8 @@ zl3073x_dpll_phase_offset_monitor_get(const struct dp= ll_device *dpll, { struct zl3073x_dpll *zldpll =3D dpll_priv; =20 + guard(mutex)(&zldpll->lock); + if (zldpll->phase_monitor) *state =3D DPLL_FEATURE_STATE_ENABLE; else @@ -1237,6 +1317,8 @@ zl3073x_dpll_phase_offset_monitor_set(const struct dp= ll_device *dpll, { struct zl3073x_dpll *zldpll =3D dpll_priv; =20 + guard(mutex)(&zldpll->lock); + zldpll->phase_monitor =3D (state =3D=3D DPLL_FEATURE_STATE_ENABLE); =20 return 0; @@ -1697,6 +1779,8 @@ zl3073x_dpll_pin_phase_offset_check(struct zl3073x_dp= ll_pin *pin) u8 ref_id; int rc; =20 + lockdep_assert_held(&zldpll->lock); + /* No phase offset if the ref monitor reports signal errors */ ref_id =3D zl3073x_input_pin_ref_get(pin->id); if (!zl3073x_dev_ref_is_status_ok(zldev, ref_id)) @@ -1753,6 +1837,8 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *p= in) const struct zl3073x_chan *chan; s64 ffo; =20 + lockdep_assert_held(&zldpll->lock); + if (pin->operstate !=3D DPLL_PIN_OPERSTATE_ACTIVE) return false; =20 @@ -1760,9 +1846,10 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *= pin) ffo =3D mul_s64_u64_shr(zl3073x_chan_df_offset_get(chan), 244140625, 36); =20 - if (atomic64_xchg(&pin->freq_offset, ffo) !=3D ffo) { + if (pin->freq_offset !=3D ffo) { dev_dbg(zldev->dev, "%s freq offset changed to: %lld\n", pin->label, ffo); + pin->freq_offset =3D ffo; return true; } =20 @@ -1787,6 +1874,8 @@ zl3073x_dpll_pin_measured_freq_check(struct zl3073x_d= pll_pin *pin) u8 ref_id; u32 freq; =20 + lockdep_assert_held(&zldpll->lock); + if (!zldpll->dev->freq_monitor) return false; =20 @@ -1817,27 +1906,37 @@ zl3073x_dpll_pin_measured_freq_check(struct zl3073x= _dpll_pin *pin) void zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll) { + DECLARE_BITMAP(changed_pins, ZL3073X_NUM_INPUT_PINS); struct zl3073x_dev *zldev =3D zldpll->dev; enum dpll_lock_status lock_status; struct device *dev =3D zldev->dev; struct zl3073x_dpll_pin *pin; + bool dev_changed =3D false; int rc; =20 + bitmap_zero(changed_pins, ZL3073X_NUM_INPUT_PINS); + + mutex_lock(&zldpll->lock); + zldpll->check_count++; =20 - /* Get current lock status for the DPLL */ - rc =3D zl3073x_dpll_lock_status_get(zldpll->dpll_dev, zldpll, - &lock_status, NULL, NULL); + rc =3D zl3073x_chan_state_update(zldev, zldpll->id); + if (rc) { + dev_err(dev, "Failed to get DPLL%u state: %pe\n", + zldpll->id, ERR_PTR(rc)); + goto unlock; + } + + rc =3D __zl3073x_dpll_lock_status_get(zldpll, &lock_status); if (rc) { dev_err(dev, "Failed to get DPLL%u lock status: %pe\n", zldpll->id, ERR_PTR(rc)); - return; + goto unlock; } =20 - /* If lock status was changed then notify DPLL core */ if (zldpll->lock_status !=3D lock_status) { zldpll->lock_status =3D lock_status; - dpll_device_change_ntf(zldpll->dpll_dev); + dev_changed =3D true; } =20 /* Update phase offset latch registers for this DPLL if the phase @@ -1849,17 +1948,13 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zld= pll) dev_err(zldev->dev, "Failed to update phase offsets: %pe\n", ERR_PTR(rc)); - return; + goto unlock; } } =20 list_for_each_entry(pin, &zldpll->pins, list) { enum dpll_pin_operstate operstate; - bool pin_changed =3D false; =20 - /* Output pins change checks are not necessary because output - * states are constant. - */ if (!zl3073x_dpll_is_input_pin(pin)) continue; =20 @@ -1868,31 +1963,40 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zld= pll) dev_err(dev, "Failed to get %s on DPLL%u oper state: %pe\n", pin->label, zldpll->id, ERR_PTR(rc)); - return; + goto unlock; } =20 if (operstate !=3D pin->operstate) { dev_dbg(dev, "%s oper state changed: %u->%u\n", pin->label, pin->operstate, operstate); pin->operstate =3D operstate; - pin_changed =3D true; + set_bit(pin->id, changed_pins); } =20 - /* Check for phase offset, ffo, and measured freq change - * once per second. - */ if (zldpll->check_count % 2 =3D=3D 0) { if (zl3073x_dpll_pin_phase_offset_check(pin)) - pin_changed =3D true; + set_bit(pin->id, changed_pins); =20 if (zl3073x_dpll_pin_ffo_check(pin)) - pin_changed =3D true; + set_bit(pin->id, changed_pins); =20 if (zl3073x_dpll_pin_measured_freq_check(pin)) - pin_changed =3D true; + set_bit(pin->id, changed_pins); } + } + +unlock: + mutex_unlock(&zldpll->lock); =20 - if (pin_changed) + /* Send notifications outside the lock to avoid ABBA deadlock + * with dpll_lock taken by notification functions. + */ + if (dev_changed) + dpll_device_change_ntf(zldpll->dpll_dev); + + list_for_each_entry(pin, &zldpll->pins, list) { + if (zl3073x_dpll_is_input_pin(pin) && + test_bit(pin->id, changed_pins)) dpll_pin_change_ntf(pin->dpll_pin); } } @@ -1949,6 +2053,7 @@ zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch) =20 zldpll->dev =3D zldev; zldpll->id =3D ch; + mutex_init(&zldpll->lock); INIT_LIST_HEAD(&zldpll->pins); =20 return zldpll; @@ -1965,6 +2070,7 @@ zl3073x_dpll_free(struct zl3073x_dpll *zldpll) { WARN(zldpll->dpll_dev, "DPLL device is still registered\n"); =20 + mutex_destroy(&zldpll->lock); kfree(zldpll); } =20 diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h index 21adcc18e45e1..9f57c944a0077 100644 --- a/drivers/dpll/zl3073x/dpll.h +++ b/drivers/dpll/zl3073x/dpll.h @@ -18,6 +18,7 @@ * @ops: DPLL device operations for this instance * @dpll_dev: pointer to registered DPLL device * @tracker: tracking object for the acquired reference + * @lock: per-DPLL mutex serializing all operations * @lock_status: last saved DPLL lock status * @pins: list of pins */ @@ -30,6 +31,7 @@ struct zl3073x_dpll { struct dpll_device_ops ops; struct dpll_device *dpll_dev; dpll_tracker tracker; + struct mutex lock; enum dpll_lock_status lock_status; struct list_head pins; }; --=20 2.53.0 From nobody Mon Jun 8 07:22:52 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E7D5732F749 for ; Sun, 31 May 2026 19:44:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780256698; cv=none; b=j6KR/rmHlavw4O8k/gcIE6SyxtZQDA1Kj1/83/OVNuT9Y9sWrmRFjaZpI0r0nfb58l2S+zmF5BBW4gBX52vYklSk3z0mRPpNHDFwgivv0uDzGoH2WaKCsuZrwh6agWUtLHH/qBluIyhVLfgdHXaZgj0kikPq9MZTqOh4P0Nuy7k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780256698; c=relaxed/simple; bh=cbqdCJINWasHmvEblnPW2+iMeO/QvNfLgph1ADfPM6w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Ry6G1g0cmlfMJlBceAhAsUwyr0/GSx7dawtnmWDI8xKKefAYE7SprY7w2bsON+N2FsLR7YUd1IKRunsOfrxajuOqxJ/se/80WtmCvpum0A8SvQichxSvaHZP8XK98S7dDXwIK1UNrH9OBOtZLrFbbV2ZLAqNx2mR/o5D24NAE/E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Cdjgl1DI; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Cdjgl1DI" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1780256695; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=eok+1K1gMoST0AIxZLwc8HYExBJnhkZBVyt6MWL/68I=; b=Cdjgl1DI+PlmuZCr+/zEQIT4lyDdExq5klAbrxXuMz4OVVlVnYoUYHbYjYHo3MhqPC5Bs+ PgXy6CqA+YAXcgBRN9qM5YUJMr3Aeyd9mXaMWWau7S0HB9o65ak0aQas9K+Xw6J+alIH47 jK71Ryf39ErBGUjDePWWLSqreQ1aiVY= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-383-KFAXGFXIMO2syG232z0Odw-1; Sun, 31 May 2026 15:44:50 -0400 X-MC-Unique: KFAXGFXIMO2syG232z0Odw-1 X-Mimecast-MFC-AGG-ID: KFAXGFXIMO2syG232z0Odw_1780256688 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id CE43819560B7; Sun, 31 May 2026 19:44:48 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.44.32.62]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 952CF1955F22; Sun, 31 May 2026 19:44:44 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Petr Oros , Arkadiusz Kubalewski , "David S. Miller" , Donald Hunter , Eric Dumazet , Jakub Kicinski , Jiri Pirko , Michal Schmidt , Paolo Abeni , Pasi Vaananen , Prathosh Satish , Simon Horman , Vadim Fedorenko , linux-kernel@vger.kernel.org Subject: [PATCH net-next v5 4/4] dpll: zl3073x: add NCO virtual input pin Date: Sun, 31 May 2026 21:44:23 +0200 Message-ID: <20260531194423.383366-5-ivecera@redhat.com> In-Reply-To: <20260531194423.383366-1-ivecera@redhat.com> References: <20260531194423.383366-1-ivecera@redhat.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Content-Type: text/plain; charset="utf-8" Add a virtual NCO (Numerically Controlled Oscillator) input pin that lets userspace switch a DPLL channel into NCO mode. A single NCO pin is shared across all DPLL channels - each channel has its own independent connection state. The NCO pin is registered with the new DPLL_PIN_TYPE_INT_NCO type and reports DPLL_PIN_STATE_CONNECTED / DPLL_PIN_OPERSTATE_ACTIVE when the channel is in NCO mode. At NCO pin registration the following bits are configured in dpll_ctrl_x: - nco_auto_read: auto-capture tracking offset on NCO entry - tod_step_reset: apply negated ToD step accumulator on NCO exit - tie_clear: PPS DPLLs set 1 to re-align outputs on NCO exit, EEC DPLLs keep 0 to prevent an unwanted TIE write Before switching to NCO mode, dpll_df_read_x is configured with ref_ofst=3D0 and cmd=3DACC_I so that nco_auto_read captures the accumulated I-part offset relative to the master clock. Without this, the captured df_offset would be near zero (offset relative to the input reference after lock). On NCO entry the df_offset captured by nco_auto_read is read from the register. Per the datasheet, nco_auto_read only captures a valid offset when entering NCO from reflock, auto or holdover mode; from freerun the captured value is not meaningful and df_offset is marked as ZL_DPLL_DF_OFFSET_UNKNOWN. The same sentinel is set in chan_state_update() when the channel is not locked, and both FFO consumers (NCO pin and input pin) guard against it. Disconnecting the NCO pin switches to freerun rather than holdover because holdover averaging is not updated during NCO mode. When connecting the NCO pin displaces a previously connected input pin (reflock mode), a change notification is sent for that input pin. Input reference pins are now always registered regardless of the initial DPLL mode. Previously they were skipped when the DPLL was in NCO mode, but the NCO pin provides the proper mechanism for mode transitions. Reviewed-by: Petr Oros Signed-off-by: Ivan Vecera --- Changes: v5: - Configure dpll_df_read register before NCO mode switch: ref_ofst=3D0 to read offset relative to master clock, cmd=3DACC_I for accumulated I-part. Without this, nco_auto_read captures incorrect df_offset (reported by Chris du Quesnay). v4: - Drop multiop_lock from chan_state_update() and nco_mode_set(), df_offset access is now serialized by the per-DPLL zldpll->lock introduced in the new lock patch. - Add zldpll->lock guard to all NCO pin callbacks for consistency with the lock patch. - Use mutex_lock/unlock in nco_pin_register, nco_pin_state_on_dpll_set, and input_pin_state_on_dpll_set instead of guard()/scoped_guard() to avoid mixing cleanup helpers with goto-based error handling. - Filter NCO pin in the deferred notification loop to match the monitoring loop filter. - Introduce ZL_DPLL_DF_OFFSET_UNKNOWN (S64_MIN) sentinel for df_offset: set on read failure, when entering NCO from a freerun mode, and when chan_state_update() finds the channel not locked. Guard both NCO pin and input pin FFO consumers against the sentinel. - Send __dpll_pin_change_ntf() for the displaced input pin when connecting the NCO pin from reflock mode. - Read df_offset from register at probe when firmware left the channel in NCO mode. - Add comment clarifying that nco_auto_read completes before the mode switch (specified by the datasheet and verified by HW testing). - Unify df_offset sign convention comments with datasheet reference (f_offset =3D f_nom * (-df_offset) / 2^48). v3: - Fixed Signed-off-by position v2: - Configure nco_auto_read, tod_step_reset and tie_clear once at NCO pin registration since these are persistent R/W bits. In v1 nco_auto_read was set at registration, while tod_step_reset and tie_clear were set on each NCO exit path. - Add zl3073x_chan_nco_mode_set() helper that writes mode_refsel directly and reads df_offset from the register without the DF_READ semaphore protocol. A short delay (~5 ms) is needed before nco_auto_read populates the df_offset register (determined by HW testing). In v1 the full DF_READ semaphore protocol with zl3073x_chan_state_update() was used. - Zero df_offset on read failure instead of keeping stale value. - Serialize zl3073x_chan_state_update() and zl3073x_chan_nco_mode_set() with multiop_lock to prevent concurrent df_offset access from the periodic worker. - Gate df_offset read in zl3073x_chan_state_update() on LOCK state instead of skipping NCO channels in chan_states_update(). This keeps mon_status and refsel_status fresh in all modes. - Send __dpll_pin_change_ntf() for the NCO pin when leaving NCO mode via mode_set() or input pin connect, since the periodic worker skips the NCO pin. - Add comments explaining the inverted sign convention of the dpll_df_offset register. - Document why NCO disconnect selects freerun over holdover, the shared NCO pin design, and the input pin registration change. --- drivers/dpll/zl3073x/chan.c | 118 +++++++++++++- drivers/dpll/zl3073x/chan.h | 48 ++++++ drivers/dpll/zl3073x/dpll.c | 308 ++++++++++++++++++++++++++++++++---- drivers/dpll/zl3073x/dpll.h | 2 + drivers/dpll/zl3073x/regs.h | 11 ++ 5 files changed, 458 insertions(+), 29 deletions(-) diff --git a/drivers/dpll/zl3073x/chan.c b/drivers/dpll/zl3073x/chan.c index 677a920c16254..1f0f904b57701 100644 --- a/drivers/dpll/zl3073x/chan.c +++ b/drivers/dpll/zl3073x/chan.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only =20 #include +#include #include #include #include @@ -31,7 +32,15 @@ int zl3073x_chan_state_update(struct zl3073x_dev *zldev,= u8 index) if (rc) return rc; =20 - /* Read df_offset vs tracked reference */ + /* Read df_offset only when locked to a reference. In NCO mode + * df_offset was captured at entry by nco_mode_set() - preserve it. + */ + if (!zl3073x_chan_is_locked(chan)) { + if (!zl3073x_chan_mode_is_nco(chan)) + chan->df_offset =3D ZL_DPLL_DF_OFFSET_UNKNOWN; + return 0; + } + rc =3D zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index), ZL_DPLL_DF_READ_SEM, ZL_POLL_DF_READ_TIMEOUT_US); @@ -58,6 +67,92 @@ int zl3073x_chan_state_update(struct zl3073x_dev *zldev,= u8 index) return 0; } =20 +/** + * zl3073x_chan_nco_mode_set - switch DPLL channel to NCO mode + * @zldev: pointer to zl3073x_dev structure + * @index: DPLL channel index + * + * Switches the channel to NCO mode and reads the df_offset + * auto-captured by nco_auto_read directly from the register. + * No DF_READ handshake is needed as nco_auto_read populates + * the register before the mode switch completes. + * + * Return: 0 on success, <0 on error + */ +int zl3073x_chan_nco_mode_set(struct zl3073x_dev *zldev, u8 index) +{ + struct zl3073x_chan *chan =3D &zldev->chan[index]; + u8 prev_mode, df_read; + u64 val; + int rc; + + prev_mode =3D zl3073x_chan_mode_get(chan); + + /* nco_auto_read captures the tracking offset at NCO entry only + * from reflock, auto or holdover mode. From freerun the captured + * value is not meaningful. + */ + if (prev_mode =3D=3D ZL_DPLL_MODE_REFSEL_MODE_FREERUN) { + zl3073x_chan_mode_set(chan, ZL_DPLL_MODE_REFSEL_MODE_NCO); + + rc =3D zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index), + chan->mode_refsel); + if (rc) { + zl3073x_chan_mode_set(chan, prev_mode); + return rc; + } + + chan->df_offset =3D ZL_DPLL_DF_OFFSET_UNKNOWN; + return 0; + } + + /* Configure df_read for nco_auto_read: + * ref_ofst=3D0 - reads offset relative to master clock (not input ref) + * cmd=3DCMD_ACC_I - accumulated I-part covering both locked and + * holdover entry. + * + * No semaphore is set - this only configures what the df_offset + * value represents after the mode switch; nco_auto_read performs + * the actual read automatically. + */ + df_read =3D FIELD_PREP(ZL_DPLL_DF_READ_REF_OFST, 0) | + FIELD_PREP(ZL_DPLL_DF_READ_CMD, ZL_DPLL_DF_READ_CMD_ACC_I); + rc =3D zl3073x_write_u8(zldev, ZL_REG_DPLL_DF_READ(index), df_read); + if (rc) + return rc; + + zl3073x_chan_mode_set(chan, ZL_DPLL_MODE_REFSEL_MODE_NCO); + rc =3D zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index), + chan->mode_refsel); + if (rc) { + zl3073x_chan_mode_set(chan, prev_mode); + return rc; + } + + /* Wait for nco_auto_read to populate df_offset. The datasheet + * does not specify a delay but HW testing shows ~3 ms is needed + * before the register contains the captured value. Use 5 ms to + * provide margin. + */ + fsleep(5000); + + /* Read df_offset captured by nco_auto_read during mode switch. + * No DF_READ semaphore handshake needed. Mode switch already + * succeeded, so don't propagate a read failure back to userspace. + */ + rc =3D zl3073x_read_u48(zldev, ZL_REG_DPLL_DF_OFFSET(index), &val); + if (rc) { + dev_warn(zldev->dev, + "Failed to read DPLL%u df_offset: %pe\n", + index, ERR_PTR(rc)); + chan->df_offset =3D ZL_DPLL_DF_OFFSET_UNKNOWN; + } else { + chan->df_offset =3D sign_extend64(val, 47); + } + + return 0; +} + /** * zl3073x_chan_state_fetch - fetch DPLL channel state from hardware * @zldev: pointer to zl3073x_dev structure @@ -73,6 +168,10 @@ int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev,= u8 index) struct zl3073x_chan *chan =3D &zldev->chan[index]; int rc, i; =20 + rc =3D zl3073x_read_u8(zldev, ZL_REG_DPLL_CTRL(index), &chan->ctrl); + if (rc) + return rc; + rc =3D zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index), &chan->mode_refsel); if (rc) @@ -85,6 +184,13 @@ int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev,= u8 index) if (rc) return rc; =20 + /* If firmware left the channel in NCO mode, mark df_offset as + * unknown - we cannot know whether the preconditions for a valid + * nco_auto_read capture were met. + */ + if (zl3073x_chan_mode_is_nco(chan)) + chan->df_offset =3D ZL_DPLL_DF_OFFSET_UNKNOWN; + 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), @@ -147,7 +253,15 @@ int zl3073x_chan_state_set(struct zl3073x_dev *zldev, = u8 index, if (!memcmp(&dchan->cfg, &chan->cfg, sizeof(chan->cfg))) return 0; =20 - /* Direct register write for mode_refsel */ + /* Direct register writes for ctrl and mode_refsel */ + if (dchan->ctrl !=3D chan->ctrl) { + rc =3D zl3073x_write_u8(zldev, ZL_REG_DPLL_CTRL(index), + chan->ctrl); + if (rc) + return rc; + dchan->ctrl =3D chan->ctrl; + } + if (dchan->mode_refsel !=3D chan->mode_refsel) { rc =3D zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index), chan->mode_refsel); diff --git a/drivers/dpll/zl3073x/chan.h b/drivers/dpll/zl3073x/chan.h index 4353809c69122..dc9c6d95bdee7 100644 --- a/drivers/dpll/zl3073x/chan.h +++ b/drivers/dpll/zl3073x/chan.h @@ -13,6 +13,7 @@ struct zl3073x_dev; =20 /** * struct zl3073x_chan - DPLL channel state + * @ctrl: DPLL control register value * @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 @@ -21,6 +22,7 @@ struct zl3073x_dev; */ struct zl3073x_chan { struct_group(cfg, + u8 ctrl; u8 mode_refsel; u8 ref_prio[ZL3073X_NUM_REFS / 2]; ); @@ -38,6 +40,7 @@ int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 = index, const struct zl3073x_chan *chan); =20 int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index); +int zl3073x_chan_nco_mode_set(struct zl3073x_dev *zldev, u8 index); =20 /** * zl3073x_chan_df_offset_get - get cached df_offset vs tracked reference @@ -152,6 +155,51 @@ static inline u8 zl3073x_chan_lock_state_get(const str= uct zl3073x_chan *chan) return FIELD_GET(ZL_DPLL_MON_STATUS_STATE, chan->mon_status); } =20 +/** + * zl3073x_chan_is_locked - check if channel is locked to a reference + * @chan: pointer to channel state + * + * Return: true if channel is locked, false otherwise + */ +static inline bool zl3073x_chan_is_locked(const struct zl3073x_chan *chan) +{ + u8 lock_state =3D zl3073x_chan_lock_state_get(chan); + return lock_state =3D=3D ZL_DPLL_MON_STATUS_STATE_LOCK; +} + +/** + * zl3073x_chan_mode_is_auto - check if channel is in automatic mode + * @chan: pointer to channel state + * + * Return: true if channel is in automatic mode, false otherwise + */ +static inline bool zl3073x_chan_mode_is_auto(const struct zl3073x_chan *ch= an) +{ + return zl3073x_chan_mode_get(chan) =3D=3D ZL_DPLL_MODE_REFSEL_MODE_AUTO; +} + +/** + * zl3073x_chan_mode_is_nco - check if channel is in NCO mode + * @chan: pointer to channel state + * + * Return: true if channel is in NCO mode, false otherwise + */ +static inline bool zl3073x_chan_mode_is_nco(const struct zl3073x_chan *cha= n) +{ + return zl3073x_chan_mode_get(chan) =3D=3D ZL_DPLL_MODE_REFSEL_MODE_NCO; +} + +/** + * zl3073x_chan_mode_is_reflock - check if channel is in reflock mode + * @chan: pointer to channel state + * + * Return: true if channel is in reflock mode, false otherwise + */ +static inline bool zl3073x_chan_mode_is_reflock(const struct zl3073x_chan = *chan) +{ + return zl3073x_chan_mode_get(chan) =3D=3D ZL_DPLL_MODE_REFSEL_MODE_REFLOC= K; +} + /** * zl3073x_chan_is_ho_ready - check if holdover is ready * @chan: pointer to channel state diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 4bee3d0c46593..8c39cdb8f5723 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -80,6 +80,18 @@ zl3073x_dpll_is_input_pin(struct zl3073x_dpll_pin *pin) return pin->dir =3D=3D DPLL_PIN_DIRECTION_INPUT; } =20 +/** + * zl3073x_dpll_is_nco_pin - check if the pin is a virtual NCO pin + * @pin: pin to check + * + * Return: true if pin is a virtual NCO pin, false otherwise. + */ +static bool +zl3073x_dpll_is_nco_pin(struct zl3073x_dpll_pin *pin) +{ + return pin->id =3D=3D ZL3073X_NCO_PIN_ID; +} + /** * zl3073x_dpll_is_p_pin - check if the pin is P-pin * @pin: pin to check @@ -119,6 +131,19 @@ zl3073x_dpll_pin_get_by_ref(struct zl3073x_dpll *zldpl= l, u8 ref_id) return NULL; } =20 +static struct zl3073x_dpll_pin * +zl3073x_dpll_nco_pin_get(struct zl3073x_dpll *zldpll) +{ + struct zl3073x_dpll_pin *pin; + + list_for_each_entry(pin, &zldpll->pins, list) { + if (zl3073x_dpll_is_nco_pin(pin)) + return pin; + } + + return NULL; +} + static int zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin, void *pin_priv, @@ -635,6 +660,7 @@ zl3073x_dpll_input_pin_state_on_dpll_set(const struct d= pll_pin *dpll_pin, { struct zl3073x_dpll *zldpll =3D dpll_priv; struct zl3073x_dpll_pin *pin =3D pin_priv; + struct zl3073x_dpll_pin *nco_pin =3D NULL; struct zl3073x_chan chan; u8 mode, ref; int rc =3D 0; @@ -666,6 +692,10 @@ zl3073x_dpll_input_pin_state_on_dpll_set(const struct = dpll_pin *dpll_pin, goto invalid_state; } break; + case ZL_DPLL_MODE_REFSEL_MODE_NCO: + if (state =3D=3D DPLL_PIN_STATE_CONNECTED) + nco_pin =3D zl3073x_dpll_nco_pin_get(zldpll); + fallthrough; case ZL_DPLL_MODE_REFSEL_MODE_FREERUN: case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER: if (state =3D=3D DPLL_PIN_STATE_CONNECTED) { @@ -713,6 +743,13 @@ zl3073x_dpll_input_pin_state_on_dpll_set(const struct = dpll_pin *dpll_pin, rc =3D -EINVAL; unlock: mutex_unlock(&zldpll->lock); + + /* If leaving NCO mode, notify userspace about the NCO pin + * state change - the periodic worker skips the NCO pin. + */ + if (!rc && nco_pin) + __dpll_pin_change_ntf(nco_pin->dpll_pin); + return rc; } =20 @@ -1039,6 +1076,144 @@ zl3073x_dpll_output_pin_state_on_dpll_get(const str= uct dpll_pin *dpll_pin, return 0; } =20 +static int +zl3073x_dpll_nco_pin_operstate_on_dpll_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_pin_operstate *operstate, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + const struct zl3073x_chan *chan; + + guard(mutex)(&zldpll->lock); + + chan =3D zl3073x_chan_state_get(zldpll->dev, zldpll->id); + if (zl3073x_chan_mode_is_nco(chan)) + *operstate =3D DPLL_PIN_OPERSTATE_ACTIVE; + else + *operstate =3D DPLL_PIN_OPERSTATE_STANDBY; + + return 0; +} + +static int +zl3073x_dpll_nco_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_pin_state *state, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + const struct zl3073x_chan *chan; + + guard(mutex)(&zldpll->lock); + + chan =3D zl3073x_chan_state_get(zldpll->dev, zldpll->id); + if (zl3073x_chan_mode_is_nco(chan)) + *state =3D DPLL_PIN_STATE_CONNECTED; + else + *state =3D DPLL_PIN_STATE_DISCONNECTED; + + return 0; +} + +static int +zl3073x_dpll_nco_pin_state_on_dpll_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, + enum dpll_pin_state state, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll_pin *ref_pin =3D NULL; + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_chan chan; + u8 ref; + int rc; + + mutex_lock(&zldpll->lock); + + chan =3D *zl3073x_chan_state_get(zldpll->dev, zldpll->id); + + switch (state) { + case DPLL_PIN_STATE_CONNECTED: + if (zl3073x_chan_mode_is_nco(&chan)) { + mutex_unlock(&zldpll->lock); + return 0; + } + if (zl3073x_chan_mode_is_auto(&chan)) { + NL_SET_ERR_MSG(extack, + "NCO pin cannot be connected in automatic mode"); + mutex_unlock(&zldpll->lock); + return -EINVAL; + } + if (zl3073x_chan_mode_is_reflock(&chan)) { + /* Get currently connected pin */ + ref =3D zl3073x_chan_ref_get(&chan); + ref_pin =3D zl3073x_dpll_pin_get_by_ref(zldpll, ref); + } + rc =3D zl3073x_chan_nco_mode_set(zldpll->dev, zldpll->id); + break; + case DPLL_PIN_STATE_DISCONNECTED: + if (!zl3073x_chan_mode_is_nco(&chan)) { + mutex_unlock(&zldpll->lock); + return 0; + } + /* Switch to freerun - holdover averaging was not + * updated during NCO mode. + */ + zl3073x_chan_mode_set(&chan, + ZL_DPLL_MODE_REFSEL_MODE_FREERUN); + rc =3D zl3073x_chan_state_set(zldpll->dev, zldpll->id, &chan); + break; + default: + NL_SET_ERR_MSG(extack, "invalid pin state for NCO pin"); + mutex_unlock(&zldpll->lock); + return -EINVAL; + } + + mutex_unlock(&zldpll->lock); + + if (!rc && ref_pin) + __dpll_pin_change_ntf(ref_pin->dpll_pin); + + return rc; +} + +static int +zl3073x_dpll_nco_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_pr= iv, + const struct dpll_device *dpll, void *dpll_priv, + struct dpll_ffo_param *ffo, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + const struct zl3073x_chan *chan; + s64 df_offset; + + guard(mutex)(&zldpll->lock); + + chan =3D zl3073x_chan_state_get(zldpll->dev, zldpll->id); + if (!zl3073x_chan_mode_is_nco(chan)) + return -ENODATA; + + /* Do not report FFO if a failure occurred during switching to NCO. */ + df_offset =3D zl3073x_chan_df_offset_get(chan); + if (df_offset =3D=3D ZL_DPLL_DF_OFFSET_UNKNOWN) + return -ENODATA; + + /* dpll_df_offset register has inverted sign per datasheet: + * f_offset =3D f_nom * (-df_offset) / 2^48 + * NCO pin reports DPLL output offset from nominal, so negate. + * Convert to PPT: ppt =3D -df * 5^12 / 2^36 + */ + ffo->ffo =3D -mul_s64_u64_shr(df_offset, 244140625, 36); + + return 0; +} + static int zl3073x_dpll_temp_get(const struct dpll_device *dpll, void *dpll_priv, s32 *temp, struct netlink_ext_ack *extack) @@ -1121,21 +1296,7 @@ zl3073x_dpll_supported_modes_get(const struct dpll_d= evice *dpll, void *dpll_priv, unsigned long *modes, struct netlink_ext_ack *extack) { - struct zl3073x_dpll *zldpll =3D dpll_priv; - const struct zl3073x_chan *chan; - - guard(mutex)(&zldpll->lock); - - chan =3D 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 (zl3073x_chan_mode_get(chan) !=3D ZL_DPLL_MODE_REFSEL_MODE_NCO) - __set_bit(DPLL_MODE_AUTOMATIC, modes); - + __set_bit(DPLL_MODE_AUTOMATIC, modes); __set_bit(DPLL_MODE_MANUAL, modes); =20 return 0; @@ -1232,11 +1393,12 @@ zl3073x_dpll_mode_set(const struct dpll_device *dpl= l, void *dpll_priv, enum dpll_mode mode, struct netlink_ext_ack *extack) { struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dpll_pin *nco_pin =3D NULL; struct zl3073x_chan chan; u8 hw_mode, ref; int rc; =20 - guard(mutex)(&zldpll->lock); + mutex_lock(&zldpll->lock); =20 chan =3D *zl3073x_chan_state_get(zldpll->dev, zldpll->id); ref =3D zl3073x_chan_refsel_ref_get(&chan); @@ -1257,6 +1419,9 @@ zl3073x_dpll_mode_set(const struct dpll_device *dpll,= void *dpll_priv, else hw_mode =3D ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER; } else { + if (zl3073x_chan_mode_is_nco(&chan)) + nco_pin =3D zl3073x_dpll_nco_pin_get(zldpll); + /* We are switching from manual to automatic mode: * - if there is a valid reference selected then ensure that * it is selectable after switch to automatic mode @@ -1285,9 +1450,18 @@ zl3073x_dpll_mode_set(const struct dpll_device *dpll= , void *dpll_priv, if (rc) { NL_SET_ERR_MSG_MOD(extack, "failed to set reference selection mode"); + mutex_unlock(&zldpll->lock); return rc; } =20 + mutex_unlock(&zldpll->lock); + + /* If leaving NCO mode, notify userspace about the NCO pin + * state change - the periodic worker skips the NCO pin. + */ + if (nco_pin) + __dpll_pin_change_ntf(nco_pin->dpll_pin); + return 0; } =20 @@ -1395,6 +1569,15 @@ static const struct dpll_pin_ops zl3073x_dpll_output= _pin_ops =3D { .state_on_dpll_get =3D zl3073x_dpll_output_pin_state_on_dpll_get, }; =20 +static const struct dpll_pin_ops zl3073x_dpll_nco_pin_ops =3D { + .supported_ffo =3D BIT(DPLL_FFO_PIN_DEVICE), + .direction_get =3D zl3073x_dpll_pin_direction_get, + .ffo_get =3D zl3073x_dpll_nco_pin_ffo_get, + .operstate_on_dpll_get =3D zl3073x_dpll_nco_pin_operstate_on_dpll_get, + .state_on_dpll_get =3D zl3073x_dpll_nco_pin_state_on_dpll_get, + .state_on_dpll_set =3D zl3073x_dpll_nco_pin_state_on_dpll_set, +}; + static const struct dpll_device_ops zl3073x_dpll_device_ops =3D { .lock_status_get =3D zl3073x_dpll_lock_status_get, .mode_get =3D zl3073x_dpll_mode_get, @@ -1542,7 +1725,9 @@ zl3073x_dpll_pin_unregister(struct zl3073x_dpll_pin *= pin) =20 WARN(!pin->dpll_pin, "DPLL pin is not registered\n"); =20 - if (zl3073x_dpll_is_input_pin(pin)) + if (zl3073x_dpll_is_nco_pin(pin)) + ops =3D &zl3073x_dpll_nco_pin_ops; + else if (zl3073x_dpll_is_input_pin(pin)) ops =3D &zl3073x_dpll_input_pin_ops; else ops =3D &zl3073x_dpll_output_pin_ops; @@ -1595,20 +1780,13 @@ zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll= *zldpll, enum dpll_pin_direction dir, u8 index) { struct zl3073x_dev *zldev =3D zldpll->dev; - const struct zl3073x_chan *chan; bool is_diff, is_enabled; const char *name; =20 - chan =3D zl3073x_chan_state_get(zldev, zldpll->id); - if (dir =3D=3D DPLL_PIN_DIRECTION_INPUT) { u8 ref_id =3D zl3073x_input_pin_ref_get(index); const struct zl3073x_ref *ref; =20 - /* Skip the pin if the DPLL is running in NCO mode */ - if (zl3073x_chan_mode_get(chan) =3D=3D ZL_DPLL_MODE_REFSEL_MODE_NCO) - return false; - name =3D "REF"; ref =3D zl3073x_ref_state_get(zldev, ref_id); is_diff =3D zl3073x_ref_is_diff(ref); @@ -1649,6 +1827,66 @@ zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll = *zldpll, return true; } =20 +static const struct dpll_pin_properties zl3073x_dpll_nco_pin_props =3D { + .type =3D DPLL_PIN_TYPE_INT_NCO, + .package_label =3D "NCO", + .capabilities =3D DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE, +}; + +static int +zl3073x_dpll_nco_pin_register(struct zl3073x_dpll *zldpll) +{ + struct zl3073x_dpll_pin *pin; + struct zl3073x_chan chan; + int rc; + + /* Ensure that ctrl bits are configured for NCO operation: + * - nco_auto_read: auto-capture tracking offset on NCO entry + * - tod_step_reset: apply negated ToD step on NCO exit + * - tie_clear: PPS DPLLs re-align outputs on NCO exit + */ + mutex_lock(&zldpll->lock); + chan =3D *zl3073x_chan_state_get(zldpll->dev, zldpll->id); + FIELD_MODIFY(ZL_DPLL_CTRL_NCO_AUTO_READ, &chan.ctrl, 1); + FIELD_MODIFY(ZL_DPLL_CTRL_TOD_STEP_RST, &chan.ctrl, 1); + FIELD_MODIFY(ZL_DPLL_CTRL_TIE_CLEAR, &chan.ctrl, + zldpll->type =3D=3D DPLL_TYPE_PPS ? 1 : 0); + rc =3D zl3073x_chan_state_set(zldpll->dev, zldpll->id, &chan); + mutex_unlock(&zldpll->lock); + if (rc) + return rc; + + pin =3D zl3073x_dpll_pin_alloc(zldpll, DPLL_PIN_DIRECTION_INPUT, + ZL3073X_NCO_PIN_ID); + if (IS_ERR(pin)) + return PTR_ERR(pin); + + pin->dpll_pin =3D dpll_pin_get(zldpll->dev->clock_id, ZL3073X_NCO_PIN_ID, + THIS_MODULE, &zl3073x_dpll_nco_pin_props, + &pin->tracker); + if (IS_ERR(pin->dpll_pin)) { + rc =3D PTR_ERR(pin->dpll_pin); + goto err_pin_get; + } + + rc =3D dpll_pin_register(zldpll->dpll_dev, pin->dpll_pin, + &zl3073x_dpll_nco_pin_ops, pin); + if (rc) + goto err_register; + + list_add(&pin->list, &zldpll->pins); + + return 0; + +err_register: + dpll_pin_put(pin->dpll_pin, &pin->tracker); +err_pin_get: + pin->dpll_pin =3D NULL; + kfree(pin); + + return rc; +} + /** * zl3073x_dpll_pins_register - register all registerable DPLL pins * @zldpll: pointer to zl3073x_dpll structure @@ -1696,6 +1934,11 @@ zl3073x_dpll_pins_register(struct zl3073x_dpll *zldp= ll) list_add(&pin->list, &zldpll->pins); } =20 + /* Register NCO virtual input pin */ + rc =3D zl3073x_dpll_nco_pin_register(zldpll); + if (rc) + goto error; + return 0; =20 error: @@ -1731,8 +1974,8 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zld= pll) return rc; } =20 - rc =3D dpll_device_register(zldpll->dpll_dev, - zl3073x_prop_dpll_type_get(zldev, zldpll->id), + zldpll->type =3D zl3073x_prop_dpll_type_get(zldev, zldpll->id); + rc =3D dpll_device_register(zldpll->dpll_dev, zldpll->type, &zldpll->ops, zldpll); if (rc) { dpll_device_put(zldpll->dpll_dev, &zldpll->tracker); @@ -1843,6 +2086,14 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *= pin) return false; =20 chan =3D zl3073x_chan_state_get(zldpll->dev, zldpll->id); + if (zl3073x_chan_df_offset_get(chan) =3D=3D ZL_DPLL_DF_OFFSET_UNKNOWN) + return false; + + /* dpll_df_offset register has inverted sign per datasheet: + * f_offset =3D f_nom * (-df_offset) / 2^48 + * Input pin FFO is pin-vs-DPLL (opposite of DPLL-vs-reference), + * so the two inversions cancel out: ppt =3D df * 5^12 / 2^36 + */ ffo =3D mul_s64_u64_shr(zl3073x_chan_df_offset_get(chan), 244140625, 36); =20 @@ -1955,7 +2206,9 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpl= l) list_for_each_entry(pin, &zldpll->pins, list) { enum dpll_pin_operstate operstate; =20 - if (!zl3073x_dpll_is_input_pin(pin)) + /* Only physical input pins need monitoring */ + if (!zl3073x_dpll_is_input_pin(pin) || + zl3073x_dpll_is_nco_pin(pin)) continue; =20 rc =3D zl3073x_dpll_ref_operstate_get(pin, &operstate); @@ -1996,6 +2249,7 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpl= l) =20 list_for_each_entry(pin, &zldpll->pins, list) { if (zl3073x_dpll_is_input_pin(pin) && + !zl3073x_dpll_is_nco_pin(pin) && test_bit(pin->id, changed_pins)) dpll_pin_change_ntf(pin->dpll_pin); } diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h index 9f57c944a0077..faebc402ba1b7 100644 --- a/drivers/dpll/zl3073x/dpll.h +++ b/drivers/dpll/zl3073x/dpll.h @@ -19,6 +19,7 @@ * @dpll_dev: pointer to registered DPLL device * @tracker: tracking object for the acquired reference * @lock: per-DPLL mutex serializing all operations + * @type: DPLL type (PPS or EEC) * @lock_status: last saved DPLL lock status * @pins: list of pins */ @@ -32,6 +33,7 @@ struct zl3073x_dpll { struct dpll_device *dpll_dev; dpll_tracker tracker; struct mutex lock; + enum dpll_type type; enum dpll_lock_status lock_status; struct list_head pins; }; diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index 9578f00095282..b70ead7d4495b 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -5,6 +5,7 @@ =20 #include #include +#include =20 /* * Hardware limits for ZL3073x chip family @@ -17,6 +18,7 @@ #define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2) #define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \ ZL3073X_NUM_OUTPUT_PINS) +#define ZL3073X_NCO_PIN_ID ZL3073X_NUM_PINS =20 /* * Register address structure: @@ -164,10 +166,18 @@ #define ZL_DPLL_MODE_REFSEL_MODE_NCO 4 #define ZL_DPLL_MODE_REFSEL_REF GENMASK(7, 4) =20 +#define ZL_REG_DPLL_CTRL(_idx) \ + ZL_REG_IDX(_idx, 5, 0x05, 1, ZL3073X_MAX_CHANNELS, 4) +#define ZL_DPLL_CTRL_TIE_CLEAR BIT(0) +#define ZL_DPLL_CTRL_TOD_STEP_RST BIT(2) +#define ZL_DPLL_CTRL_NCO_AUTO_READ BIT(7) + #define ZL_REG_DPLL_DF_READ(_idx) \ ZL_REG_IDX(_idx, 5, 0x28, 1, ZL3073X_MAX_CHANNELS, 1) #define ZL_DPLL_DF_READ_SEM BIT(4) #define ZL_DPLL_DF_READ_REF_OFST BIT(3) +#define ZL_DPLL_DF_READ_CMD GENMASK(2, 0) +#define ZL_DPLL_DF_READ_CMD_ACC_I 4 =20 #define ZL_REG_DPLL_MEAS_CTRL ZL_REG(5, 0x50, 1) #define ZL_DPLL_MEAS_CTRL_EN BIT(0) @@ -190,6 +200,7 @@ #define ZL_REG_DPLL_DF_OFFSET_4 ZL_REG(7, 0x00, 6) #define ZL_REG_DPLL_DF_OFFSET(_idx) \ ((_idx) < 4 ? ZL_REG_DPLL_DF_OFFSET_03(_idx) : ZL_REG_DPLL_DF_OFFSET_4) +#define ZL_DPLL_DF_OFFSET_UNKNOWN S64_MIN =20 /*********************************** * Register Page 9, Synth and Output --=20 2.53.0