From nobody Fri Jun 12 17:16:08 2026 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.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 2079E40DFCB for ; Wed, 13 May 2026 14:57:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778684225; cv=none; b=aZvCh8hIBkvS5SM85f7i5PAHqAKZXusbuaCDHXhAKjygKlJ4y0NRrdcwudW5BaNN2QJoSsu2lhfrb2PAr+v0bI2j4r/UC/MxsNF0liPDHvpSFdsRHT5iPPMt1BEa7Q+CrRIQwo++3Mqh7tWMTQwiLheuKPVGphw39p2CbGH88NQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778684225; c=relaxed/simple; bh=WjkmBK4assTSqnUx8k2PfrhtvN8EVJqdkPymYJ9vx8o=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iK9JsJ7g1F4Gs/tuBj0ZcKA/d7j6sm5fZbyIrgG9kmD1e/cnxt4CJA8sKoHfsVUHmOiMvhMDWUXGbducwaSfE9k/AcIVjaXXUqQ434O1fB8VGrQULfjLMFXdorwUX7Ew+JPVIJ/4t0IGFv7orbmibCUGKBqRwNJh01CzM0xCokA= 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=Mp4weQb5; arc=none smtp.client-ip=170.10.133.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="Mp4weQb5" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1778684223; 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=Jeu3U5fuD7zLve/CUe59/D1xkdYPoJVdvUuUlFX8zRE=; b=Mp4weQb59/zAMVBGCUg8axsRP/bhj1ELltDyXivTghyUH8fm8Nlhm0avxpcinl4A/h1oC9 WIgZe94Eqx14nWzhv+Jbe5iYYcM3hGz2RZe5vUHRPlO1FsVeMGyHaiasv/ahQvTzJusDUy D7e0ZTWQ2nPUEWoilw2vqzQnRxOquA0= Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-70-uppo7RgYMFClc5GqLld2HQ-1; Wed, 13 May 2026 10:56:59 -0400 X-MC-Unique: uppo7RgYMFClc5GqLld2HQ-1 X-Mimecast-MFC-AGG-ID: uppo7RgYMFClc5GqLld2HQ_1778684218 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (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-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id C4B2E18005BE; Wed, 13 May 2026 14:56:57 +0000 (UTC) Received: from p16v.redhat.com (unknown [10.44.33.20]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 8D40B19560A3; Wed, 13 May 2026 14:56:52 +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 1/2] dpll: add DPLL_PIN_TYPE_INT_NCO pin type Date: Wed, 13 May 2026 16:56:44 +0200 Message-ID: <20260513145645.175451-2-ivecera@redhat.com> In-Reply-To: <20260513145645.175451-1-ivecera@redhat.com> References: <20260513145645.175451-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.12 Content-Type: text/plain; charset="utf-8" Add DPLL_PIN_TYPE_INT_NCO pin type for device internal numerically controlled oscillators. When connected as a DPLL input, the DPLL operates in NCO mode where the output frequency is controlled by the host. Update the fractional-frequency-offset-ppt attribute documentation to note that for INT_NCO pins this attribute represents the DPLL's delta frequency offset. Signed-off-by: Ivan Vecera --- Documentation/netlink/specs/dpll.yaml | 12 ++++++++++++ drivers/dpll/dpll_nl.c | 2 +- include/uapi/linux/dpll.h | 4 ++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/= specs/dpll.yaml index 91a172617b3a9..159de8ad8b00b 100644 --- a/Documentation/netlink/specs/dpll.yaml +++ b/Documentation/netlink/specs/dpll.yaml @@ -162,6 +162,12 @@ definitions: - name: gnss doc: GNSS recovered clock + - + name: int-nco + doc: | + Device internal numerically controlled oscillator. + When connected as a DPLL input, the DPLL operates in NCO mode + where output frequency is controlled by the host. render-max: true - type: enum @@ -453,6 +459,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 attribute represents + the DPLL's current delta frequency offset when the NCO pin + is connected. Value is in PPM (parts per million). This is a lower-precision version of fractional-frequency-offset-ppt. @@ -499,6 +508,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 attribute represents + the DPLL's current delta frequency offset when the NCO pin + is connected. 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..dab7ffbde11b7 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 operates in NCO mode where o= utput + * frequency is controlled by the host. */ 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 Fri Jun 12 17:16:08 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 2D40238D6BD for ; Wed, 13 May 2026 14:57:16 +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=1778684240; cv=none; b=JQlfvj2f3LUahRtn+8LE0P+yIWVsarc1ekfnDDjlxmJYpA9R84+dt4WN8GkQYDj42Tn+hmAkwXwbsGC4grC4QrfDVyFGxkJeKxbTBqLAgGR5wnaqqDK7mhTubuej2A2FA2eOS6UvnTeAjlwi5d25GIk67F/K+XujGPfeji0tATg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778684240; c=relaxed/simple; bh=Pnpg35y72RzTsUkwS80TgULTpf3F0V9tDHttKTAdaks=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BEHzyttbszv4bAf3n4P1xKxXTrqK2ptR4UbPBs56ncphHbTMHrOgjR3THsoWnjdEiZcAQfur7McbKLze/at+UtgQbRxzr6tb9kiC6zx/qU6FXWlf6oHDhEFLF6PVN7jgAnOozlq8iHIwr0KOPagl0FyBDeqZQdcvT2KP/Q6navM= 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=cQ8VxCuB; 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="cQ8VxCuB" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1778684235; 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=S3EVTOcgcej7sGfSJK1xAE8RWrY9IOXxmoABW8tdrf4=; b=cQ8VxCuBsb6jTiYcOYYVfNFid0rOtYcgFK94Sa2aaMoK6vioWOBrlT+7GOW1ua5HZZltbH fsV6kR/N0rC7fnpSnCisyfTTQGe6kDNKOMSL3/fDKK9WkmqFb8+ycaSbm7yXJI0glukxAS rbKEK11avWnc7ggUnamuBL9Q2E+vTfA= 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-177-qWIjckw-MhGK5ap7-18W-w-1; Wed, 13 May 2026 10:57:09 -0400 X-MC-Unique: qWIjckw-MhGK5ap7-18W-w-1 X-Mimecast-MFC-AGG-ID: qWIjckw-MhGK5ap7-18W-w_1778684223 Received: from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12]) (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 B3CBD19560BB; Wed, 13 May 2026 14:57:02 +0000 (UTC) Received: from p16v.redhat.com (unknown [10.44.33.20]) by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3326819560A2; Wed, 13 May 2026 14:56:57 +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 2/2] dpll: zl3073x: add NCO virtual input pin Date: Wed, 13 May 2026 16:56:45 +0200 Message-ID: <20260513145645.175451-3-ivecera@redhat.com> In-Reply-To: <20260513145645.175451-1-ivecera@redhat.com> References: <20260513145645.175451-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.12 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. The NCO pin is registered with the new DPLL_PIN_TYPE_INT_NCO type and reports DPLL_PIN_STATE_CONNECTED / ACTIVE when the channel is in NCO mode. When entering NCO mode the nco_auto_read bit in dpll_ctrl_x causes the hardware to automatically capture the current tracking offset into df_offset. This is refreshed immediately after the mode switch so userspace sees an up-to-date FFO value. When leaving NCO mode set tod_step_reset and tie_clear in dpll_ctrl_x to preserve the output 1PPS phase. PPS DPLLs set tie_clear=3D1 to re-align to the new reference; EEC DPLLs set tie_clear=3D0 to prevent an unwanted TIE write. Signed-off-by: Ivan Vecera --- drivers/dpll/zl3073x/chan.c | 14 +- drivers/dpll/zl3073x/chan.h | 24 ++++ drivers/dpll/zl3073x/core.c | 7 + drivers/dpll/zl3073x/dpll.c | 259 ++++++++++++++++++++++++++++++------ drivers/dpll/zl3073x/dpll.h | 2 + drivers/dpll/zl3073x/regs.h | 7 + 6 files changed, 274 insertions(+), 39 deletions(-) diff --git a/drivers/dpll/zl3073x/chan.c b/drivers/dpll/zl3073x/chan.c index 2fe3c3da84bb5..70db3dbfc4b11 100644 --- a/drivers/dpll/zl3073x/chan.c +++ b/drivers/dpll/zl3073x/chan.c @@ -71,6 +71,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) @@ -145,7 +149,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..aa89728433710 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]; ); @@ -152,6 +154,28 @@ 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_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_is_ho_ready - check if holdover is ready * @chan: pointer to channel state diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index b3345060490db..1326b431c7bb4 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -572,6 +572,13 @@ zl3073x_dev_chan_states_update(struct zl3073x_dev *zld= ev) int i, rc; =20 for (i =3D 0; i < zldev->info->num_channels; i++) { + const struct zl3073x_chan *chan; + + /* Skip channels in NCO mode - no reference tracking */ + chan =3D zl3073x_chan_state_get(zldev, i); + if (zl3073x_chan_mode_is_nco(chan)) + continue; + rc =3D zl3073x_chan_state_update(zldev, i); if (rc) dev_warn(zldev->dev, diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index cff85cdb9d0e5..0407c072b604c 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -81,6 +81,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 @@ -634,6 +646,18 @@ 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) { + /* Preserve output phase when leaving NCO: + * PPS needs tie_clear=3D1 to re-align to the new ref, + * EEC needs tie_clear=3D0 to prevent an unwanted TIE + * write. + */ + 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); + } + fallthrough; case ZL_DPLL_MODE_REFSEL_MODE_FREERUN: case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER: if (state =3D=3D DPLL_PIN_STATE_CONNECTED) { @@ -989,6 +1013,129 @@ zl3073x_dpll_output_pin_state_on_dpll_get(const stru= ct 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; + + 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; + + 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 *zldpll =3D dpll_priv; + struct zl3073x_chan chan; + int rc; + + chan =3D *zl3073x_chan_state_get(zldpll->dev, zldpll->id); + + switch (state) { + case DPLL_PIN_STATE_CONNECTED: + /* Already in NCO mode, nothing to do */ + if (zl3073x_chan_mode_is_nco(&chan)) + return 0; + /* NCO is only allowed in manual mode */ + if (zl3073x_chan_mode_is_auto(&chan)) { + NL_SET_ERR_MSG(extack, + "NCO pin cannot be connected in automatic mode"); + return -EINVAL; + } + /* Switch to NCO mode */ + zl3073x_chan_mode_set(&chan, ZL_DPLL_MODE_REFSEL_MODE_NCO); + break; + case DPLL_PIN_STATE_DISCONNECTED: + /* Not in NCO mode, nothing to do */ + if (!zl3073x_chan_mode_is_nco(&chan)) + return 0; + /* Preserve output phase when leaving NCO: + * PPS needs tie_clear=3D1 to re-align to the new ref, + * EEC needs tie_clear=3D0 to prevent an unwanted TIE write. + */ + 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); + /* Leave NCO, switch to freerun */ + zl3073x_chan_mode_set(&chan, ZL_DPLL_MODE_REFSEL_MODE_FREERUN); + break; + default: + NL_SET_ERR_MSG(extack, "invalid pin state for NCO pin"); + return -EINVAL; + } + + rc =3D zl3073x_chan_state_set(zldpll->dev, zldpll->id, &chan); + if (rc) + return rc; + + /* Refresh cached df_offset after entering NCO - nco_auto_read + * causes HW to capture the tracking offset. + */ + if (state =3D=3D DPLL_PIN_STATE_CONNECTED) { + rc =3D zl3073x_chan_state_update(zldpll->dev, zldpll->id); + if (rc) + return rc; + } + + return 0; +} + +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; + + chan =3D zl3073x_chan_state_get(zldpll->dev, zldpll->id); + if (!zl3073x_chan_mode_is_nco(chan)) + return -ENODATA; + + /* Convert df_offset (2^-48) to PPT: ppt =3D -df * 5^12 / 2^36 */ + ffo->ffo =3D -mul_s64_u64_shr(zl3073x_chan_df_offset_get(chan), + 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) @@ -1057,19 +1204,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; - - 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; @@ -1310,6 +1445,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, @@ -1457,7 +1601,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; @@ -1510,20 +1656,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); @@ -1564,6 +1703,57 @@ 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; + + /* Enable automatic df_offset capture on NCO entry */ + chan =3D *zl3073x_chan_state_get(zldpll->dev, zldpll->id); + FIELD_MODIFY(ZL_DPLL_CTRL_NCO_AUTO_READ, &chan.ctrl, 1); + rc =3D zl3073x_chan_state_set(zldpll->dev, zldpll->id, &chan); + 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 @@ -1609,6 +1799,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: @@ -1644,8 +1839,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); @@ -1815,10 +2010,8 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldp= ll) struct zl3073x_dev *zldev =3D zldpll->dev; enum dpll_lock_status lock_status; struct device *dev =3D zldev->dev; - const struct zl3073x_chan *chan; struct zl3073x_dpll_pin *pin; int rc; - u8 mode; =20 zldpll->check_count++; =20 @@ -1837,15 +2030,6 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldp= ll) dpll_device_change_ntf(zldpll->dpll_dev); } =20 - /* Input pin monitoring does make sense only in automatic - * or forced reference modes. - */ - chan =3D zl3073x_chan_state_get(zldev, zldpll->id); - mode =3D zl3073x_chan_mode_get(chan); - if (mode !=3D ZL_DPLL_MODE_REFSEL_MODE_AUTO && - mode !=3D ZL_DPLL_MODE_REFSEL_MODE_REFLOCK) - return; - /* Update phase offset latch registers for this DPLL if the phase * offset monitor feature is enabled. */ @@ -1863,10 +2047,9 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldp= ll) 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)) + /* 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); diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h index 434c32a7db123..0920e4d31ab35 100644 --- a/drivers/dpll/zl3073x/dpll.h +++ b/drivers/dpll/zl3073x/dpll.h @@ -19,6 +19,7 @@ * @ops: DPLL device operations for this instance * @dpll_dev: pointer to registered DPLL device * @tracker: tracking object for the acquired reference + * @type: DPLL type (PPS or EEC) * @lock_status: last saved DPLL lock status * @pins: list of pins * @change_work: device change notification work @@ -33,6 +34,7 @@ struct zl3073x_dpll { struct dpll_device_ops ops; struct dpll_device *dpll_dev; dpll_tracker tracker; + enum dpll_type type; enum dpll_lock_status lock_status; struct list_head pins; struct work_struct change_work; diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index 9578f00095282..8f1dd3a2ac214 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -17,6 +17,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,6 +165,12 @@ #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) --=20 2.53.0