From nobody Thu Oct 9 20:22:57 2025 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 1E6AC288C0E for ; Mon, 16 Jun 2025 20:14:29 +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=1750104871; cv=none; b=IcAHS3BNQ6woGkm0ZGihtdDbV4LjbBN9V4mygo4dB0iBZ9JD9PdtJdKKoYpfXIKYFtAtLkURkIav6A9pgNa9U8fYJav7dk0dlPMrw+rog7P5H5vwDRRNiv1HhbtohDaPzhE3Kftl6ipNONQZbc30WwuSGjnTh1I/1/sLqAllIss= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104871; c=relaxed/simple; bh=lURu+n3h2pYvpFjdMDI1YpZxq6fZG8n3PMjkDW9xgjw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Auz+bb0S7VgjbgdNV0LR64UXVdC3ontpfl1sXe6WaHkAhsVT/CjVIxbDANRT+xI8Uls9juc21K5jeIfqinACRR7NG/qcT/eJhIxQWTR/UZz3MLSc2RncFfLgjJVSluVBiwIcV+ttoxdi60D9qPKeMsLMEYq5zL5mpyyd20vNXyc= 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=d1YRDuON; 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="d1YRDuON" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104868; 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=wMzvfq27DYyR6il3XAVtdyPAzlfwmcDV4p4sg1R5VKY=; b=d1YRDuONPaqslZqMCM5q+Sn+ekIFg/lfBVX0ewFeu1qpEevMBsW+X+URL0Gzrp1r9lXj7D WaMJ2Cydvy3V/oWbIsooRTXZcdKgYWGFYm6sbEIra32p6hOfu+yrfU+JVTf4/MLa7grEEF WZgYTGLl8bJ0Tt27hBvKqSFqrM3PRlg= 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-477-O2VkEoGSPeWMhPEHRiTDsA-1; Mon, 16 Jun 2025 16:14:27 -0400 X-MC-Unique: O2VkEoGSPeWMhPEHRiTDsA-1 X-Mimecast-MFC-AGG-ID: O2VkEoGSPeWMhPEHRiTDsA_1750104863 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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 39517195608F; Mon, 16 Jun 2025 20:14:22 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 7B1DB30044D8; Mon, 16 Jun 2025 20:14:14 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Krzysztof Kozlowski , Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Prathosh Satish , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 01/14] dt-bindings: dpll: Add DPLL device and pin Date: Mon, 16 Jun 2025 22:13:51 +0200 Message-ID: <20250616201404.1412341-2-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Add a common DT schema for DPLL device and its associated pins. The DPLL (device phase-locked loop) is a device used for precise clock synchronization in networking and telecom hardware. The device includes one or more DPLLs (channels) and one or more physical input/output pins. Each DPLL channel is used either to provide a pulse-per-clock signal or to drive an Ethernet equipment clock. The input and output pins have the following properties: * label: specifies board label * connection type: specifies its usage depending on wiring * list of supported or allowed frequencies: depending on how the pin is connected and where) * embedded sync capability: indicates whether the pin supports this Reviewed-by: Krzysztof Kozlowski Signed-off-by: Ivan Vecera --- v8: * no change --- .../devicetree/bindings/dpll/dpll-device.yaml | 76 +++++++++++++++++++ .../devicetree/bindings/dpll/dpll-pin.yaml | 45 +++++++++++ MAINTAINERS | 2 + 3 files changed, 123 insertions(+) create mode 100644 Documentation/devicetree/bindings/dpll/dpll-device.yaml create mode 100644 Documentation/devicetree/bindings/dpll/dpll-pin.yaml diff --git a/Documentation/devicetree/bindings/dpll/dpll-device.yaml b/Docu= mentation/devicetree/bindings/dpll/dpll-device.yaml new file mode 100644 index 0000000000000..fb8d7a9a3693f --- /dev/null +++ b/Documentation/devicetree/bindings/dpll/dpll-device.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dpll/dpll-device.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Digital Phase-Locked Loop (DPLL) Device + +maintainers: + - Ivan Vecera + +description: + Digital Phase-Locked Loop (DPLL) device is used for precise clock + synchronization in networking and telecom hardware. The device can + have one or more channels (DPLLs) and one or more physical input and + output pins. Each DPLL channel can either produce pulse-per-clock signal + or drive ethernet equipment clock. The type of each channel can be + indicated by dpll-types property. + +properties: + $nodename: + pattern: "^dpll(@.*)?$" + + "#address-cells": + const: 0 + + "#size-cells": + const: 0 + + dpll-types: + description: List of DPLL channel types, one per DPLL instance. + $ref: /schemas/types.yaml#/definitions/non-unique-string-array + items: + enum: [pps, eec] + + input-pins: + type: object + description: DPLL input pins + unevaluatedProperties: false + + properties: + "#address-cells": + const: 1 + "#size-cells": + const: 0 + + patternProperties: + "^pin@[0-9a-f]+$": + $ref: /schemas/dpll/dpll-pin.yaml + unevaluatedProperties: false + + required: + - "#address-cells" + - "#size-cells" + + output-pins: + type: object + description: DPLL output pins + unevaluatedProperties: false + + properties: + "#address-cells": + const: 1 + "#size-cells": + const: 0 + + patternProperties: + "^pin@[0-9]+$": + $ref: /schemas/dpll/dpll-pin.yaml + unevaluatedProperties: false + + required: + - "#address-cells" + - "#size-cells" + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/dpll/dpll-pin.yaml b/Documen= tation/devicetree/bindings/dpll/dpll-pin.yaml new file mode 100644 index 0000000000000..51db93b77306f --- /dev/null +++ b/Documentation/devicetree/bindings/dpll/dpll-pin.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dpll/dpll-pin.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: DPLL Pin + +maintainers: + - Ivan Vecera + +description: | + The DPLL pin is either a physical input or output pin that is provided + by a DPLL( Digital Phase-Locked Loop) device. The pin is identified by + its physical order number that is stored in reg property and can have + an additional set of properties like supported (allowed) frequencies, + label, type and may support embedded sync. + + Note that the pin in this context has nothing to do with pinctrl. + +properties: + reg: + description: Hardware index of the DPLL pin. + maxItems: 1 + + connection-type: + description: Connection type of the pin + $ref: /schemas/types.yaml#/definitions/string + enum: [ext, gnss, int, mux, synce] + + esync-control: + description: Indicates whether the pin supports embedded sync function= ality. + type: boolean + + label: + description: String exposed as the pin board label + $ref: /schemas/types.yaml#/definitions/string + + supported-frequencies-hz: + description: List of supported frequencies for this pin, expressed in = Hz. + +required: + - reg + +additionalProperties: false diff --git a/MAINTAINERS b/MAINTAINERS index 507c5ff6f620a..e5b90ac03f39d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7324,6 +7324,8 @@ M: Arkadiusz Kubalewski M: Jiri Pirko L: netdev@vger.kernel.org S: Supported +F: Documentation/devicetree/bindings/dpll/dpll-device.yaml +F: Documentation/devicetree/bindings/dpll/dpll-pin.yaml F: Documentation/driver-api/dpll.rst F: drivers/dpll/* F: include/linux/dpll.h --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 DE15628A714 for ; Mon, 16 Jun 2025 20:14:36 +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=1750104878; cv=none; b=hw7suqU9oAjwsQn0zKTPpk1qjlZofsK11pe4O/pWd4RjtNyjK9hdgl3IvwsCV61MKYnQ9FwyxsvlqU+Fdj/Ertj7eMuHM81e5Rn8xqZGLO5QFdL8LjjRbSmG/uO8Lo6goAFnG4ggiXR6QFPxaCk8Vcj8bn8VPL5+G68i/4GK2sU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104878; c=relaxed/simple; bh=kP7sCxIkgE77aJqMYZ3BvMf06yDuAxed/D+Rs7aRy2Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=smsijL4pvy/ufw1fekLu5sM0jRPyHHbBJ9krtb11Uhu1NDE0vYEx49gTtGsXhAp8jWslZS2m3BhsTsaO6sqnFNyu7KMcc4Nc/MRm77ZrPDx23fF3O+0J5AUwwkvMXorjuwWl5sPFs+y6S6VZz+3M3w+6KEgctL6/BCxgZJ/pMEE= 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=bW/kEYUa; 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="bW/kEYUa" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104875; 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=vCZqqC+G2dbS5TS/FUu98apFIkbaphiOjY1kYdjuiH0=; b=bW/kEYUaT8svKOjv80O8KxRE4S0rnd191mYd+vzPhKxStPa1e/4vp56pQl4790ClokvUcg 7sxPeJNMSZWcK1gYnN4/jcaXJUgBw4WVJwr1nr2GQDmN0SUI1gJDEGDlFBhb/QVkygiqHL 3PxVpzOEUGTm9RikW3GndjForSkn8vw= Received: from mx-prod-mc-08.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-413-iSqHdsGuM2upS_DtZuK1fw-1; Mon, 16 Jun 2025 16:14:32 -0400 X-MC-Unique: iSqHdsGuM2upS_DtZuK1fw-1 X-Mimecast-MFC-AGG-ID: iSqHdsGuM2upS_DtZuK1fw_1750104870 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id EAC7D1809C81; Mon, 16 Jun 2025 20:14:29 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id ABD7930001B1; Mon, 16 Jun 2025 20:14:22 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Krzysztof Kozlowski , Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Prathosh Satish , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 02/14] dt-bindings: dpll: Add support for Microchip Azurite chip family Date: Mon, 16 Jun 2025 22:13:52 +0200 Message-ID: <20250616201404.1412341-3-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Add DT bindings for Microchip Azurite DPLL chip family. These chips provide up to 5 independent DPLL channels, 10 differential or single-ended inputs and 10 differential or 20 single-ended outputs. They can be connected via I2C or SPI busses. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Ivan Vecera --- v9: * no change --- .../bindings/dpll/microchip,zl30731.yaml | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 Documentation/devicetree/bindings/dpll/microchip,zl3073= 1.yaml diff --git a/Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml = b/Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml new file mode 100644 index 0000000000000..17747f754b845 --- /dev/null +++ b/Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml @@ -0,0 +1,115 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dpll/microchip,zl30731.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Azurite DPLL device + +maintainers: + - Ivan Vecera + +description: + Microchip Azurite DPLL (ZL3073x) is a family of DPLL devices that + provides up to 5 independent DPLL channels, up to 10 differential or + single-ended inputs and 10 differential or 20 single-ended outputs. + These devices support both I2C and SPI interfaces. + +properties: + compatible: + enum: + - microchip,zl30731 + - microchip,zl30732 + - microchip,zl30733 + - microchip,zl30734 + - microchip,zl30735 + + reg: + maxItems: 1 + +required: + - compatible + - reg + +allOf: + - $ref: /schemas/dpll/dpll-device.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + dpll@70 { + compatible =3D "microchip,zl30732"; + reg =3D <0x70>; + dpll-types =3D "pps", "eec"; + + input-pins { + #address-cells =3D <1>; + #size-cells =3D <0>; + + pin@0 { /* REF0P */ + reg =3D <0>; + connection-type =3D "ext"; + label =3D "Input 0"; + supported-frequencies-hz =3D /bits/ 64 <1 1000>; + }; + }; + + output-pins { + #address-cells =3D <1>; + #size-cells =3D <0>; + + pin@3 { /* OUT1N */ + reg =3D <3>; + connection-type =3D "gnss"; + esync-control; + label =3D "Output 1"; + supported-frequencies-hz =3D /bits/ 64 <1 10000>; + }; + }; + }; + }; + - | + spi { + #address-cells =3D <1>; + #size-cells =3D <0>; + + dpll@70 { + compatible =3D "microchip,zl30731"; + reg =3D <0x70>; + spi-max-frequency =3D <12500000>; + + dpll-types =3D "pps"; + + input-pins { + #address-cells =3D <1>; + #size-cells =3D <0>; + + pin@0 { /* REF0P */ + reg =3D <0>; + connection-type =3D "ext"; + label =3D "Input 0"; + supported-frequencies-hz =3D /bits/ 64 <1 1000>; + }; + }; + + output-pins { + #address-cells =3D <1>; + #size-cells =3D <0>; + + pin@3 { /* OUT1N */ + reg =3D <3>; + connection-type =3D "gnss"; + esync-control; + label =3D "Output 1"; + supported-frequencies-hz =3D /bits/ 64 <1 10000>; + }; + }; + }; + }; +... --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 DCD00288C1A for ; Mon, 16 Jun 2025 20:14:44 +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=1750104887; cv=none; b=blQJwukDHdRdeE55GIHeKPLkKL9R/ZWGeMwrqED0OlgzuMuppzKlNv+mQ1255nWoti3tijXJ7QFwTNeYwlIZo2vRhejJZi7AD6q6ILxytwm11tfv6phUIBQbfVmlkPJSMZk0/xObcfM0bcG7CeLZBx28mDW9stw+CMsCjTSCojA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104887; c=relaxed/simple; bh=0wjbF2cHKB/+wEv2qRk84wH5/cjQgG3yfvtIkZoW1/I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YCHohWtLNKNvpHM06stemhV8U4CvTQZcvTbeswmf26pSJEGhkHI8Z4x9Fu5s5EcZbeOTpSBWOn0P8yi2ZL0ZpUWdWx9CY1BOZGqiAN8fDDitN9kcklDFTkdn9p5I8isLrsfO1enaPnWmv5IGSMD51OBwduKKZdd9M/5bocMNtOY= 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=eSdpUGkJ; 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="eSdpUGkJ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104883; 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=bg0C6asPJ9i+3Won723rnqBSU17v7kBOFpnWvMchk3w=; b=eSdpUGkJnRyhPRVhpp/+4A1iIEF87r10EYLqQ6HokTZzyUOdmujhPdCjA7aNzVhjuR0ARM XofQmNJwkGZ9p3UP9il/BPV4uir3Ml8++rE+8Q4H4WLrU8w9gjZPd1QLFEsvz/g56MheeD XDsr7dhWPCEyvWzzwClEgnzxEfYl1L8= 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-614-awqBxb9GP_iddCMKWY4Rkw-1; Mon, 16 Jun 2025 16:14:40 -0400 X-MC-Unique: awqBxb9GP_iddCMKWY4Rkw-1 X-Mimecast-MFC-AGG-ID: awqBxb9GP_iddCMKWY4Rkw_1750104877 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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 AF6241800281; Mon, 16 Jun 2025 20:14:37 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 683F630044D8; Mon, 16 Jun 2025 20:14:30 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Prathosh Satish , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 03/14] dpll: Add basic Microchip ZL3073x support Date: Mon, 16 Jun 2025 22:13:53 +0200 Message-ID: <20250616201404.1412341-4-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Microchip Azurite ZL3073x represents chip family providing DPLL and optionally PHC (PTP) functionality. The chips can be connected be connected over I2C or SPI bus. They have the following characteristics: * up to 5 separate DPLL units (channels) * 5 synthesizers * 10 input pins (references) * 10 outputs * 20 output pins (output pin pair shares one output) * Each reference and output can operate in either differential or single-ended mode (differential mode uses 2 pins) * Each output is connected to one of the synthesizers * Each synthesizer is driven by one of the DPLL unit The device uses 7-bit addresses and 8-bits values. It exposes 8-, 16-, 32- and 48-bits registers in address range <0x000,0x77F>. Due to 7bit addressing, the range is organized into pages of 128 bytes, with each page containing a page selector register at address 0x7F. For reading/writing multi-byte registers, the device supports bulk transfers. Add basic functionality to access device registers and probe functionality for both I2C and SPI cases. Signed-off-by: Ivan Vecera --- MAINTAINERS | 8 + drivers/Kconfig | 4 +- drivers/dpll/Kconfig | 6 + drivers/dpll/Makefile | 2 + drivers/dpll/zl3073x/Kconfig | 34 +++ drivers/dpll/zl3073x/Makefile | 10 + drivers/dpll/zl3073x/core.c | 452 ++++++++++++++++++++++++++++++++++ drivers/dpll/zl3073x/core.h | 56 +++++ drivers/dpll/zl3073x/i2c.c | 93 +++++++ drivers/dpll/zl3073x/regs.h | 75 ++++++ drivers/dpll/zl3073x/spi.c | 93 +++++++ 11 files changed, 831 insertions(+), 2 deletions(-) create mode 100644 drivers/dpll/zl3073x/Kconfig create mode 100644 drivers/dpll/zl3073x/Makefile create mode 100644 drivers/dpll/zl3073x/core.c create mode 100644 drivers/dpll/zl3073x/core.h create mode 100644 drivers/dpll/zl3073x/i2c.c create mode 100644 drivers/dpll/zl3073x/regs.h create mode 100644 drivers/dpll/zl3073x/spi.c diff --git a/MAINTAINERS b/MAINTAINERS index e5b90ac03f39d..f3cdfa79b79ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16465,6 +16465,14 @@ L: linux-wireless@vger.kernel.org S: Supported F: drivers/net/wireless/microchip/ =20 +MICROCHIP ZL3073X DRIVER +M: Ivan Vecera +M: Prathosh Satish +L: netdev@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml +F: drivers/dpll/zl3073x/ + MICROSEMI MIPS SOCS M: Alexandre Belloni M: UNGLinuxDriver@microchip.com diff --git a/drivers/Kconfig b/drivers/Kconfig index 7c556c5ac4fdd..3d8c902c25610 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -77,6 +77,8 @@ source "drivers/pps/Kconfig" =20 source "drivers/ptp/Kconfig" =20 +source "drivers/dpll/Kconfig" + source "drivers/pinctrl/Kconfig" =20 source "drivers/gpio/Kconfig" @@ -245,6 +247,4 @@ source "drivers/hte/Kconfig" =20 source "drivers/cdx/Kconfig" =20 -source "drivers/dpll/Kconfig" - endmenu diff --git a/drivers/dpll/Kconfig b/drivers/dpll/Kconfig index 20607ed542435..ade872c915ac6 100644 --- a/drivers/dpll/Kconfig +++ b/drivers/dpll/Kconfig @@ -3,5 +3,11 @@ # Generic DPLL drivers configuration # =20 +menu "DPLL device support" + config DPLL bool + +source "drivers/dpll/zl3073x/Kconfig" + +endmenu diff --git a/drivers/dpll/Makefile b/drivers/dpll/Makefile index 2e5b278501105..9e7a3a3e592e8 100644 --- a/drivers/dpll/Makefile +++ b/drivers/dpll/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_DPLL) +=3D dpll.o dpll-y +=3D dpll_core.o dpll-y +=3D dpll_netlink.o dpll-y +=3D dpll_nl.o + +obj-$(CONFIG_ZL3073X) +=3D zl3073x/ diff --git a/drivers/dpll/zl3073x/Kconfig b/drivers/dpll/zl3073x/Kconfig new file mode 100644 index 0000000000000..217160df0f49a --- /dev/null +++ b/drivers/dpll/zl3073x/Kconfig @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config ZL3073X + tristate "Microchip Azurite DPLL/PTP/SyncE devices" + select DPLL + help + This driver supports Microchip Azurite DPLL/PTP/SyncE devices. + + To compile this driver as a module, choose M here. The module + will be called zl3073x. + +config ZL3073X_I2C + tristate "I2C bus implementation for Microchip Azurite devices" + depends on I2C && ZL3073X + select REGMAP_I2C + default m + help + This is I2C bus implementation for Microchip Azurite DPLL/PTP/SyncE + devices. + + To compile this driver as a module, choose M here: the module will + be called zl3073x_i2c. + +config ZL3073X_SPI + tristate "SPI bus implementation for Microchip Azurite devices" + depends on SPI && ZL3073X + select REGMAP_SPI + default m + help + This is SPI bus implementation for Microchip Azurite DPLL/PTP/SyncE + devices. + + To compile this driver as a module, choose M here: the module will + be called zl3073x_spi. diff --git a/drivers/dpll/zl3073x/Makefile b/drivers/dpll/zl3073x/Makefile new file mode 100644 index 0000000000000..8760f358f5447 --- /dev/null +++ b/drivers/dpll/zl3073x/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_ZL3073X) +=3D zl3073x.o +zl3073x-objs :=3D core.o + +obj-$(CONFIG_ZL3073X_I2C) +=3D zl3073x_i2c.o +zl3073x_i2c-objs :=3D i2c.o + +obj-$(CONFIG_ZL3073X_SPI) +=3D zl3073x_spi.o +zl3073x_spi-objs :=3D spi.o diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c new file mode 100644 index 0000000000000..5af053d3e199d --- /dev/null +++ b/drivers/dpll/zl3073x/core.c @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "regs.h" + +/* Chip IDs for zl30731 */ +static const u16 zl30731_ids[] =3D { + 0x0E93, + 0x1E93, + 0x2E93, +}; + +/* Chip IDs for zl30732 */ +static const u16 zl30732_ids[] =3D { + 0x0E30, + 0x0E94, + 0x1E94, + 0x1F60, + 0x2E94, + 0x3FC4, +}; + +/* Chip IDs for zl30733 */ +static const u16 zl30733_ids[] =3D { + 0x0E95, + 0x1E95, + 0x2E95, +}; + +/* Chip IDs for zl30734 */ +static const u16 zl30734_ids[] =3D { + 0x0E96, + 0x1E96, + 0x2E96, +}; + +/* Chip IDs for zl30735 */ +static const u16 zl30735_ids[] =3D { + 0x0E97, + 0x1E97, + 0x2E97, +}; + +const struct zl3073x_chip_info zl3073x_chip_info[] =3D { + [ZL30731] =3D { + .ids =3D zl30731_ids, + .num_ids =3D ARRAY_SIZE(zl30731_ids), + .num_channels =3D 1, + }, + [ZL30732] =3D { + .ids =3D zl30732_ids, + .num_ids =3D ARRAY_SIZE(zl30732_ids), + .num_channels =3D 2, + }, + [ZL30733] =3D { + .ids =3D zl30733_ids, + .num_ids =3D ARRAY_SIZE(zl30733_ids), + .num_channels =3D 3, + }, + [ZL30734] =3D { + .ids =3D zl30734_ids, + .num_ids =3D ARRAY_SIZE(zl30734_ids), + .num_channels =3D 4, + }, + [ZL30735] =3D { + .ids =3D zl30735_ids, + .num_ids =3D ARRAY_SIZE(zl30735_ids), + .num_channels =3D 5, + }, +}; +EXPORT_SYMBOL_NS_GPL(zl3073x_chip_info, "ZL3073X"); + +#define ZL_RANGE_OFFSET 0x80 +#define ZL_PAGE_SIZE 0x80 +#define ZL_NUM_PAGES 15 +#define ZL_PAGE_SEL 0x7F +#define ZL_PAGE_SEL_MASK GENMASK(3, 0) +#define ZL_NUM_REGS (ZL_NUM_PAGES * ZL_PAGE_SIZE) + +/* Regmap range configuration */ +static const struct regmap_range_cfg zl3073x_regmap_range =3D { + .range_min =3D ZL_RANGE_OFFSET, + .range_max =3D ZL_RANGE_OFFSET + ZL_NUM_REGS - 1, + .selector_reg =3D ZL_PAGE_SEL, + .selector_mask =3D ZL_PAGE_SEL_MASK, + .selector_shift =3D 0, + .window_start =3D 0, + .window_len =3D ZL_PAGE_SIZE, +}; + +static bool +zl3073x_is_volatile_reg(struct device *dev __maybe_unused, unsigned int re= g) +{ + /* Only page selector is non-volatile */ + return reg !=3D ZL_PAGE_SEL; +} + +const struct regmap_config zl3073x_regmap_config =3D { + .reg_bits =3D 8, + .val_bits =3D 8, + .max_register =3D ZL_RANGE_OFFSET + ZL_NUM_REGS - 1, + .ranges =3D &zl3073x_regmap_range, + .num_ranges =3D 1, + .cache_type =3D REGCACHE_MAPLE, + .volatile_reg =3D zl3073x_is_volatile_reg, +}; +EXPORT_SYMBOL_NS_GPL(zl3073x_regmap_config, "ZL3073X"); + +static bool +zl3073x_check_reg(struct zl3073x_dev *zldev, unsigned int reg, size_t size) +{ + /* Check the index is in valid range for indexed register */ + if (ZL_REG_OFFSET(reg) > ZL_REG_MAX_OFFSET(reg)) { + dev_err(zldev->dev, "Index out of range for reg 0x%04lx\n", + ZL_REG_ADDR(reg)); + return false; + } + /* Check the requested size corresponds to register size */ + if (ZL_REG_SIZE(reg) !=3D size) { + dev_err(zldev->dev, "Invalid size %zu for reg 0x%04lx\n", + size, ZL_REG_ADDR(reg)); + return false; + } + + return true; +} + +static int +zl3073x_read_reg(struct zl3073x_dev *zldev, unsigned int reg, void *val, + size_t size) +{ + int rc; + + if (!zl3073x_check_reg(zldev, reg, size)) + return -EINVAL; + + /* Map the register address to virtual range */ + reg =3D ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET; + + rc =3D regmap_bulk_read(zldev->regmap, reg, val, size); + if (rc) { + dev_err(zldev->dev, "Failed to read reg 0x%04x: %pe\n", reg, + ERR_PTR(rc)); + return rc; + } + + return 0; +} + +static int +zl3073x_write_reg(struct zl3073x_dev *zldev, unsigned int reg, const void = *val, + size_t size) +{ + int rc; + + if (!zl3073x_check_reg(zldev, reg, size)) + return -EINVAL; + + /* Map the register address to virtual range */ + reg =3D ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET; + + rc =3D regmap_bulk_write(zldev->regmap, reg, val, size); + if (rc) { + dev_err(zldev->dev, "Failed to write reg 0x%04x: %pe\n", reg, + ERR_PTR(rc)); + return rc; + } + + return 0; +} + +/** + * zl3073x_read_u8 - read value from 8bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Reads value from given 8bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_read_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 *val) +{ + return zl3073x_read_reg(zldev, reg, val, sizeof(*val)); +} + +/** + * zl3073x_write_u8 - write value to 16bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Writes value into given 8bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_write_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 val) +{ + return zl3073x_write_reg(zldev, reg, &val, sizeof(val)); +} + +/** + * zl3073x_read_u16 - read value from 16bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Reads value from given 16bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_read_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 *val) +{ + int rc; + + rc =3D zl3073x_read_reg(zldev, reg, val, sizeof(*val)); + if (!rc) + be16_to_cpus(val); + + return rc; +} + +/** + * zl3073x_write_u16 - write value to 16bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Writes value into given 16bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_write_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 val) +{ + cpu_to_be16s(&val); + + return zl3073x_write_reg(zldev, reg, &val, sizeof(val)); +} + +/** + * zl3073x_read_u32 - read value from 32bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Reads value from given 32bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_read_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 *val) +{ + int rc; + + rc =3D zl3073x_read_reg(zldev, reg, val, sizeof(*val)); + if (!rc) + be32_to_cpus(val); + + return rc; +} + +/** + * zl3073x_write_u32 - write value to 32bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Writes value into given 32bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_write_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 val) +{ + cpu_to_be32s(&val); + + return zl3073x_write_reg(zldev, reg, &val, sizeof(val)); +} + +/** + * zl3073x_read_u48 - read value from 48bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Reads value from given 48bit register. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_read_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 *val) +{ + u8 buf[6]; + int rc; + + rc =3D zl3073x_read_reg(zldev, reg, buf, sizeof(buf)); + if (!rc) + *val =3D get_unaligned_be48(buf); + + return rc; +} + +/** + * zl3073x_write_u48 - write value to 48bit register + * @zldev: zl3073x device pointer + * @reg: register to write to + * @val: value to write + * + * Writes value into given 48bit register. + * The value must be from the interval -S48_MIN to U48_MAX. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val) +{ + u8 buf[6]; + + /* Check the value belongs to + * Any value >=3D S48_MIN has bits 47..63 set. + */ + if (val > GENMASK_ULL(47, 0) && val < GENMASK_ULL(63, 47)) { + dev_err(zldev->dev, "Value 0x%0llx out of range\n", val); + return -EINVAL; + } + + put_unaligned_be48(val, buf); + + return zl3073x_write_reg(zldev, reg, buf, sizeof(buf)); +} + +/** + * zl3073x_poll_zero_u8 - wait for register to be cleared by device + * @zldev: zl3073x device pointer + * @reg: register to poll (has to be 8bit register) + * @mask: bit mask for polling + * + * 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) +{ + /* Register polling sleep & timeout */ +#define ZL_POLL_SLEEP_US 10 +#define ZL_POLL_TIMEOUT_US 2000000 + unsigned int val; + + /* Check the register is 8bit */ + if (ZL_REG_SIZE(reg) !=3D 1) { + dev_err(zldev->dev, "Invalid reg 0x%04lx size for polling\n", + ZL_REG_ADDR(reg)); + return -EINVAL; + } + + /* Map the register address to virtual range */ + reg =3D ZL_REG_ADDR(reg) + ZL_RANGE_OFFSET; + + return regmap_read_poll_timeout(zldev->regmap, reg, val, !(val & mask), + ZL_POLL_SLEEP_US, ZL_POLL_TIMEOUT_US); +} + +/** + * zl3073x_devm_alloc - allocates zl3073x device structure + * @dev: pointer to device structure + * + * Allocates zl3073x device structure as device resource. + * + * Return: pointer to zl3073x device on success, error pointer on error + */ +struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev) +{ + struct zl3073x_dev *zldev; + + zldev =3D devm_kzalloc(dev, sizeof(*zldev), GFP_KERNEL); + if (!zldev) + return ERR_PTR(-ENOMEM); + + zldev->dev =3D dev; + dev_set_drvdata(zldev->dev, zldev); + + return zldev; +} +EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, "ZL3073X"); + +/** + * zl3073x_dev_probe - initialize zl3073x device + * @zldev: pointer to zl3073x device + * @chip_info: chip info based on compatible + * + * Common initialization of zl3073x device structure. + * + * Returns: 0 on success, <0 on error + */ +int zl3073x_dev_probe(struct zl3073x_dev *zldev, + const struct zl3073x_chip_info *chip_info) +{ + u16 id, revision, fw_ver; + unsigned int i; + u32 cfg_ver; + int rc; + + /* Read chip ID */ + rc =3D zl3073x_read_u16(zldev, ZL_REG_ID, &id); + if (rc) + return rc; + + /* Check it matches */ + for (i =3D 0; i < chip_info->num_ids; i++) { + if (id =3D=3D chip_info->ids[i]) + break; + } + + if (i =3D=3D chip_info->num_ids) { + return dev_err_probe(zldev->dev, -ENODEV, + "Unknown or non-match chip ID: 0x%0x\n", + id); + } + + /* Read revision, firmware version and custom config version */ + rc =3D zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision); + if (rc) + return rc; + rc =3D zl3073x_read_u16(zldev, ZL_REG_FW_VER, &fw_ver); + if (rc) + return rc; + rc =3D zl3073x_read_u32(zldev, ZL_REG_CUSTOM_CONFIG_VER, &cfg_ver); + if (rc) + return rc; + + dev_dbg(zldev->dev, "ChipID(%X), ChipRev(%X), FwVer(%u)\n", id, + revision, fw_ver); + dev_dbg(zldev->dev, "Custom config version: %lu.%lu.%lu.%lu\n", + FIELD_GET(GENMASK(31, 24), cfg_ver), + FIELD_GET(GENMASK(23, 16), cfg_ver), + FIELD_GET(GENMASK(15, 8), cfg_ver), + FIELD_GET(GENMASK(7, 0), cfg_ver)); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(zl3073x_dev_probe, "ZL3073X"); + +MODULE_AUTHOR("Ivan Vecera "); +MODULE_DESCRIPTION("Microchip ZL3073x core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h new file mode 100644 index 0000000000000..75f68bc9a52ee --- /dev/null +++ b/drivers/dpll/zl3073x/core.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ZL3073X_H +#define _ZL3073X_H + +#include + +struct device; +struct regmap; + +/** + * struct zl3073x_dev - zl3073x device + * @dev: pointer to device + * @regmap: regmap to access device registers + */ +struct zl3073x_dev { + struct device *dev; + struct regmap *regmap; +}; + +enum zl3073x_chip_type { + ZL30731, + ZL30732, + ZL30733, + ZL30734, + ZL30735, +}; + +struct zl3073x_chip_info { + const u16 *ids; + size_t num_ids; + int num_channels; +}; + +extern const struct zl3073x_chip_info zl3073x_chip_info[]; +extern const struct regmap_config zl3073x_regmap_config; + +struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev); +int zl3073x_dev_probe(struct zl3073x_dev *zldev, + const struct zl3073x_chip_info *chip_info); + +/********************** + * Registers operations + **********************/ + +int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 m= ask); +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= ); +int zl3073x_read_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 *val= ); +int zl3073x_write_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 val); +int zl3073x_write_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 val= ); +int zl3073x_write_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 val= ); +int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val= ); + +#endif /* _ZL3073X_H */ diff --git a/drivers/dpll/zl3073x/i2c.c b/drivers/dpll/zl3073x/i2c.c new file mode 100644 index 0000000000000..bca1cd729895c --- /dev/null +++ b/drivers/dpll/zl3073x/i2c.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +#include "core.h" + +static int zl3073x_i2c_probe(struct i2c_client *client) +{ + struct device *dev =3D &client->dev; + struct zl3073x_dev *zldev; + + zldev =3D zl3073x_devm_alloc(dev); + if (IS_ERR(zldev)) + return PTR_ERR(zldev); + + zldev->regmap =3D devm_regmap_init_i2c(client, &zl3073x_regmap_config); + if (IS_ERR(zldev->regmap)) { + dev_err_probe(dev, PTR_ERR(zldev->regmap), + "Failed to initialize regmap\n"); + return PTR_ERR(zldev->regmap); + } + + return zl3073x_dev_probe(zldev, i2c_get_match_data(client)); +} + +static const struct i2c_device_id zl3073x_i2c_id[] =3D { + { + .name =3D "zl30731", + .driver_data =3D (kernel_ulong_t)&zl3073x_chip_info[ZL30731], + }, + { + .name =3D "zl30732", + .driver_data =3D (kernel_ulong_t)&zl3073x_chip_info[ZL30732], + }, + { + .name =3D "zl30733", + .driver_data =3D (kernel_ulong_t)&zl3073x_chip_info[ZL30733], + }, + { + .name =3D "zl30734", + .driver_data =3D (kernel_ulong_t)&zl3073x_chip_info[ZL30734], + }, + { + .name =3D "zl30735", + .driver_data =3D (kernel_ulong_t)&zl3073x_chip_info[ZL30735], + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, zl3073x_i2c_id); + +static const struct of_device_id zl3073x_i2c_of_match[] =3D { + { + .compatible =3D "microchip,zl30731", + .data =3D &zl3073x_chip_info[ZL30731], + }, + { + .compatible =3D "microchip,zl30732", + .data =3D &zl3073x_chip_info[ZL30732], + }, + { + .compatible =3D "microchip,zl30733", + .data =3D &zl3073x_chip_info[ZL30733], + }, + { + .compatible =3D "microchip,zl30734", + .data =3D &zl3073x_chip_info[ZL30734], + }, + { + .compatible =3D "microchip,zl30735", + .data =3D &zl3073x_chip_info[ZL30735], + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, zl3073x_i2c_of_match); + +static struct i2c_driver zl3073x_i2c_driver =3D { + .driver =3D { + .name =3D "zl3073x-i2c", + .of_match_table =3D zl3073x_i2c_of_match, + }, + .probe =3D zl3073x_i2c_probe, + .id_table =3D zl3073x_i2c_id, +}; +module_i2c_driver(zl3073x_i2c_driver); + +MODULE_AUTHOR("Ivan Vecera "); +MODULE_DESCRIPTION("Microchip ZL3073x I2C driver"); +MODULE_IMPORT_NS("ZL3073X"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h new file mode 100644 index 0000000000000..08bf595935ea1 --- /dev/null +++ b/drivers/dpll/zl3073x/regs.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ZL3073X_REGS_H +#define _ZL3073X_REGS_H + +#include +#include + +/* + * Register address structure: + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D + * 25 19 18 16 15 7 6 0 + * +------------------------------------------+ + * | max_offset | size | page | page_offset | + * +------------------------------------------+ + * + * page_offset ... <0x00..0x7F> + * page .......... HW page number + * size .......... register byte size (1, 2, 4 or 6) + * max_offset .... maximal offset for indexed registers + * (for non-indexed regs max_offset =3D=3D page_offset) + */ + +#define ZL_REG_OFFSET_MASK GENMASK(6, 0) +#define ZL_REG_PAGE_MASK GENMASK(15, 7) +#define ZL_REG_SIZE_MASK GENMASK(18, 16) +#define ZL_REG_MAX_OFFSET_MASK GENMASK(25, 19) +#define ZL_REG_ADDR_MASK GENMASK(15, 0) + +#define ZL_REG_OFFSET(_reg) FIELD_GET(ZL_REG_OFFSET_MASK, _reg) +#define ZL_REG_PAGE(_reg) FIELD_GET(ZL_REG_PAGE_MASK, _reg) +#define ZL_REG_MAX_OFFSET(_reg) FIELD_GET(ZL_REG_MAX_OFFSET_MASK, _reg) +#define ZL_REG_SIZE(_reg) FIELD_GET(ZL_REG_SIZE_MASK, _reg) +#define ZL_REG_ADDR(_reg) FIELD_GET(ZL_REG_ADDR_MASK, _reg) + +/** + * ZL_REG_IDX - define indexed register + * @_idx: index of register to access + * @_page: register page + * @_offset: register offset in page + * @_size: register byte size (1, 2, 4 or 6) + * @_items: number of register indices + * @_stride: stride between items in bytes + * + * All parameters except @_idx should be constant. + */ +#define ZL_REG_IDX(_idx, _page, _offset, _size, _items, _stride) \ + (FIELD_PREP(ZL_REG_OFFSET_MASK, \ + (_offset) + (_idx) * (_stride)) | \ + FIELD_PREP_CONST(ZL_REG_PAGE_MASK, _page) | \ + FIELD_PREP_CONST(ZL_REG_SIZE_MASK, _size) | \ + FIELD_PREP_CONST(ZL_REG_MAX_OFFSET_MASK, \ + (_offset) + ((_items) - 1) * (_stride))) + +/** + * ZL_REG - define simple (non-indexed) register + * @_page: register page + * @_offset: register offset in page + * @_size: register byte size (1, 2, 4 or 6) + * + * All parameters should be constant. + */ +#define ZL_REG(_page, _offset, _size) \ + ZL_REG_IDX(0, _page, _offset, _size, 1, 0) + +/************************** + * Register Page 0, General + **************************/ + +#define ZL_REG_ID ZL_REG(0, 0x01, 2) +#define ZL_REG_REVISION ZL_REG(0, 0x03, 2) +#define ZL_REG_FW_VER ZL_REG(0, 0x05, 2) +#define ZL_REG_CUSTOM_CONFIG_VER ZL_REG(0, 0x07, 4) + +#endif /* _ZL3073X_REGS_H */ diff --git a/drivers/dpll/zl3073x/spi.c b/drivers/dpll/zl3073x/spi.c new file mode 100644 index 0000000000000..219676da71b78 --- /dev/null +++ b/drivers/dpll/zl3073x/spi.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include + +#include "core.h" + +static int zl3073x_spi_probe(struct spi_device *spi) +{ + struct device *dev =3D &spi->dev; + struct zl3073x_dev *zldev; + + zldev =3D zl3073x_devm_alloc(dev); + if (IS_ERR(zldev)) + return PTR_ERR(zldev); + + zldev->regmap =3D devm_regmap_init_spi(spi, &zl3073x_regmap_config); + if (IS_ERR(zldev->regmap)) { + dev_err_probe(dev, PTR_ERR(zldev->regmap), + "Failed to initialize regmap\n"); + return PTR_ERR(zldev->regmap); + } + + return zl3073x_dev_probe(zldev, spi_get_device_match_data(spi)); +} + +static const struct spi_device_id zl3073x_spi_id[] =3D { + { + .name =3D "zl30731", + .driver_data =3D (kernel_ulong_t)&zl3073x_chip_info[ZL30731], + }, + { + .name =3D "zl30732", + .driver_data =3D (kernel_ulong_t)&zl3073x_chip_info[ZL30732], + }, + { + .name =3D "zl30733", + .driver_data =3D (kernel_ulong_t)&zl3073x_chip_info[ZL30733], + }, + { + .name =3D "zl30734", + .driver_data =3D (kernel_ulong_t)&zl3073x_chip_info[ZL30734], + }, + { + .name =3D "zl30735", + .driver_data =3D (kernel_ulong_t)&zl3073x_chip_info[ZL30735] + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, zl3073x_spi_id); + +static const struct of_device_id zl3073x_spi_of_match[] =3D { + { + .compatible =3D "microchip,zl30731", + .data =3D &zl3073x_chip_info[ZL30731] + }, + { + .compatible =3D "microchip,zl30732", + .data =3D &zl3073x_chip_info[ZL30732] + }, + { + .compatible =3D "microchip,zl30733", + .data =3D &zl3073x_chip_info[ZL30733] + }, + { + .compatible =3D "microchip,zl30734", + .data =3D &zl3073x_chip_info[ZL30734] + }, + { + .compatible =3D "microchip,zl30735", + .data =3D &zl3073x_chip_info[ZL30735] + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, zl3073x_spi_of_match); + +static struct spi_driver zl3073x_spi_driver =3D { + .driver =3D { + .name =3D "zl3073x-spi", + .of_match_table =3D zl3073x_spi_of_match, + }, + .probe =3D zl3073x_spi_probe, + .id_table =3D zl3073x_spi_id, +}; +module_spi_driver(zl3073x_spi_driver); + +MODULE_AUTHOR("Ivan Vecera "); +MODULE_DESCRIPTION("Microchip ZL3073x SPI driver"); +MODULE_IMPORT_NS("ZL3073X"); +MODULE_LICENSE("GPL"); --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 0C67B28A1EA for ; Mon, 16 Jun 2025 20:14:50 +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=1750104893; cv=none; b=K85cAnDzVGcuPK0osbJYNfr1c530t5CpVQZUPfQ4ceqHVm1u98APBGK1ZpEpX7jvUpiecHWZW1hYr+R/TaIMlmIb78KOK/InZzejOv2NLD2AGEqQB8RVvqpMWU9qeiECCe97G9tWTkO6W44QbeD+jbX+oINyAcSpbiOmdonBRP4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104893; c=relaxed/simple; bh=grPZsGPRnaQZuRkBbYpaLj/wYfwgSv7l5Z9xBBJX+Zo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=h2Jwkpgb+J9dT6zUIpW+7tUz/VdT5vjhgWBaMWinJBNK3mBbDa1noqVZ3VN1A4uokZW8kMutfvvP3d2lXXDsu6rcijLIPyaabRgXHVz7gKlKtfMyccV6x5mcB9LuDmxaAeY9W/yeiDU5m5Qwp2jYpELIX+UB7OSz63oR3QUMWG0= 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=ESYSr6BZ; 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="ESYSr6BZ" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104890; 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=hUL+h7O8Iu0fC5YKYEbNKqxo6BXzcFC+2VpUj/LFr9Y=; b=ESYSr6BZRpoH45I2WjVtj9qzTo06KUzwWFgsmAyW8t0M22cwPviPXGUBc97r35X7Wk7hAB txdrTFTLaCVmHdGxhHcDrIdl5jFovk/gWSK6Y9Fzj2AoesHltJ6hxjw5VDKWailMF3h6Jk ZJZIunfACYd70lbUHSVmUW+UsPkkAA0= Received: from mx-prod-mc-08.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-251-n51nzct5Oo2u1Jg-qoyYrA-1; Mon, 16 Jun 2025 16:14:47 -0400 X-MC-Unique: n51nzct5Oo2u1Jg-qoyYrA-1 X-Mimecast-MFC-AGG-ID: n51nzct5Oo2u1Jg-qoyYrA_1750104885 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 0557918002ED; Mon, 16 Jun 2025 20:14:45 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 2E7C430001B1; Mon, 16 Jun 2025 20:14:37 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Prathosh Satish , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 04/14] dpll: zl3073x: Add support for devlink device info Date: Mon, 16 Jun 2025 22:13:54 +0200 Message-ID: <20250616201404.1412341-5-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Use devlink_alloc() to allocate zl3073x_dev structure, register the device as a devlink device, and add devlink callback to provide device info. Sample output: # devlink dev info i2c/1-0070: driver zl3073x-i2c versions: fixed: asic.id 1E94 asic.rev 300 running: fw 7006 Signed-off-by: Ivan Vecera --- Documentation/networking/devlink/index.rst | 1 + Documentation/networking/devlink/zl3073x.rst | 37 +++++++ drivers/dpll/zl3073x/Kconfig | 2 + drivers/dpll/zl3073x/core.c | 108 ++++++++++++++++++- 4 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 Documentation/networking/devlink/zl3073x.rst diff --git a/Documentation/networking/devlink/index.rst b/Documentation/net= working/devlink/index.rst index 8319f43b5933d..250ae71f40236 100644 --- a/Documentation/networking/devlink/index.rst +++ b/Documentation/networking/devlink/index.rst @@ -98,3 +98,4 @@ parameters, info versions, and other features it supports. iosm octeontx2 sfc + zl3073x diff --git a/Documentation/networking/devlink/zl3073x.rst b/Documentation/n= etworking/devlink/zl3073x.rst new file mode 100644 index 0000000000000..9a6744fb2e866 --- /dev/null +++ b/Documentation/networking/devlink/zl3073x.rst @@ -0,0 +1,37 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +zl3073x devlink support +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +This document describes the devlink features implemented by the ``zl3073x`` +device driver. + +Info versions +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The ``zl3073x`` driver reports the following versions + +.. list-table:: devlink info versions implemented + :widths: 5 5 5 90 + + * - Name + - Type + - Example + - Description + * - ``asic.id`` + - fixed + - 1E94 + - Chip identification number + * - ``asic.rev`` + - fixed + - 300 + - Chip revision number + * - ``fw`` + - running + - 7006 + - Firmware version number + * - ``cfg.custom_ver`` + - running + - 1.3.0.1 + - Device configuration version customized by OEM diff --git a/drivers/dpll/zl3073x/Kconfig b/drivers/dpll/zl3073x/Kconfig index 217160df0f49a..8f3056b727aa0 100644 --- a/drivers/dpll/zl3073x/Kconfig +++ b/drivers/dpll/zl3073x/Kconfig @@ -2,7 +2,9 @@ =20 config ZL3073X tristate "Microchip Azurite DPLL/PTP/SyncE devices" + depends on NET select DPLL + select NET_DEVLINK help This driver supports Microchip Azurite DPLL/PTP/SyncE devices. =20 diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index 5af053d3e199d..3269cea8b4073 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -7,8 +7,11 @@ #include #include #include +#include #include +#include #include +#include =20 #include "core.h" #include "regs.h" @@ -367,6 +370,83 @@ int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, un= signed int reg, u8 mask) ZL_POLL_SLEEP_US, ZL_POLL_TIMEOUT_US); } =20 +/** + * zl3073x_devlink_info_get - Devlink device info callback + * @devlink: devlink structure pointer + * @req: devlink request pointer to store information + * @extack: netlink extack pointer to report errors + * + * Return: 0 on success, <0 on error + */ +static int zl3073x_devlink_info_get(struct devlink *devlink, + struct devlink_info_req *req, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dev *zldev =3D devlink_priv(devlink); + u16 id, revision, fw_ver; + char buf[16]; + u32 cfg_ver; + int rc; + + rc =3D zl3073x_read_u16(zldev, ZL_REG_ID, &id); + if (rc) + return rc; + + snprintf(buf, sizeof(buf), "%X", id); + rc =3D devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_ASIC_ID, + buf); + if (rc) + return rc; + + rc =3D zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision); + if (rc) + return rc; + + snprintf(buf, sizeof(buf), "%X", revision); + rc =3D devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_ASIC_REV, + buf); + if (rc) + return rc; + + rc =3D zl3073x_read_u16(zldev, ZL_REG_FW_VER, &fw_ver); + if (rc) + return rc; + + snprintf(buf, sizeof(buf), "%u", fw_ver); + rc =3D devlink_info_version_running_put(req, + DEVLINK_INFO_VERSION_GENERIC_FW, + buf); + if (rc) + return rc; + + rc =3D zl3073x_read_u32(zldev, ZL_REG_CUSTOM_CONFIG_VER, &cfg_ver); + if (rc) + return rc; + + /* No custom config version */ + if (cfg_ver =3D=3D U32_MAX) + return 0; + + snprintf(buf, sizeof(buf), "%lu.%lu.%lu.%lu", + FIELD_GET(GENMASK(31, 24), cfg_ver), + FIELD_GET(GENMASK(23, 16), cfg_ver), + FIELD_GET(GENMASK(15, 8), cfg_ver), + FIELD_GET(GENMASK(7, 0), cfg_ver)); + + return devlink_info_version_running_put(req, "cfg.custom_ver", buf); +} + +static const struct devlink_ops zl3073x_devlink_ops =3D { + .info_get =3D zl3073x_devlink_info_get, +}; + +static void zl3073x_devlink_free(void *ptr) +{ + devlink_free(ptr); +} + /** * zl3073x_devm_alloc - allocates zl3073x device structure * @dev: pointer to device structure @@ -378,11 +458,19 @@ int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, u= nsigned int reg, u8 mask) struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev) { struct zl3073x_dev *zldev; + struct devlink *devlink; + int rc; =20 - zldev =3D devm_kzalloc(dev, sizeof(*zldev), GFP_KERNEL); - if (!zldev) + devlink =3D devlink_alloc(&zl3073x_devlink_ops, sizeof(*zldev), dev); + if (!devlink) return ERR_PTR(-ENOMEM); =20 + /* Add devres action to free devlink device */ + rc =3D devm_add_action_or_reset(dev, zl3073x_devlink_free, devlink); + if (rc) + return ERR_PTR(rc); + + zldev =3D devlink_priv(devlink); zldev->dev =3D dev; dev_set_drvdata(zldev->dev, zldev); =20 @@ -390,6 +478,11 @@ struct zl3073x_dev *zl3073x_devm_alloc(struct device *= dev) } EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, "ZL3073X"); =20 +static void zl3073x_devlink_unregister(void *ptr) +{ + devlink_unregister(ptr); +} + /** * zl3073x_dev_probe - initialize zl3073x device * @zldev: pointer to zl3073x device @@ -403,6 +496,7 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev, const struct zl3073x_chip_info *chip_info) { u16 id, revision, fw_ver; + struct devlink *devlink; unsigned int i; u32 cfg_ver; int rc; @@ -443,6 +537,16 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev, FIELD_GET(GENMASK(15, 8), cfg_ver), FIELD_GET(GENMASK(7, 0), cfg_ver)); =20 + /* Register the device as devlink device */ + devlink =3D priv_to_devlink(zldev); + devlink_register(devlink); + + /* Add devres action to unregister devlink device */ + rc =3D devm_add_action_or_reset(zldev->dev, zl3073x_devlink_unregister, + devlink); + if (rc) + return rc; + return 0; } EXPORT_SYMBOL_NS_GPL(zl3073x_dev_probe, "ZL3073X"); --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 4079928A3FE for ; Mon, 16 Jun 2025 20:14:59 +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=1750104901; cv=none; b=iB/gBfiGHm3kdeQ3ir4LEMQDWmw3pUPrAyeOJWNrabJbVJ+9Bq6tckgCXf5Oa3k8oxH4sx3XHuf96O63esCFPEcZ5bR6Oe11X1WVu9iCyBjiQpWFn1UuxGt9C1nzT142JrRyiLigfSTa0D9qoB0YtgolUKYpQQJjqgeP60QaTCk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104901; c=relaxed/simple; bh=4KUzpSMWG3g9IuCPuhaljwtDZbmm+RMgcvIPQFtfMUw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SCGT/sk6r6B/+uAjEPMtqbdt4h+ZC6i1+dwN+Loj5yFyo3b3BuGjk61rXJGUNUE+VxE5soxM015MpsSfu3tggVBYDTcb2+QvTPCRGLE7cDQffonWM2Hh+40Co2Soq7Bp6URTWXXLBnzApSwN8QCKt+ujAUkSz2RotCIrMb2PB8w= 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=GOWveib9; 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="GOWveib9" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104899; 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=EMnYd1npq1nHTWpIVvhBB0Ev585m/kdvaOzQpI3Jbqs=; b=GOWveib9vc8wFd3STgNjScaEpAqAcuNpaX52kTUTlJe8GUOD5tQ4bwFz08bj9KHjzxIjVE Eru1fSAv0cyd4XdV1f6U92UR5yur0e57OlNrQKgTNTMvq0hZa9osAQ7TtvK7zXs22x0okn kGb7oi/CohpBQKAPAZmXCoE9iHL9vrA= Received: from mx-prod-mc-04.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-460-SHSfLIyZPvG5QBvYaKXO2w-1; Mon, 16 Jun 2025 16:14:54 -0400 X-MC-Unique: SHSfLIyZPvG5QBvYaKXO2w-1 X-Mimecast-MFC-AGG-ID: SHSfLIyZPvG5QBvYaKXO2w_1750104892 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id E84B219560B5; Mon, 16 Jun 2025 20:14:51 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 778C830001B1; Mon, 16 Jun 2025 20:14:45 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Prathosh Satish , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 05/14] dpll: zl3073x: Protect operations requiring multiple register accesses Date: Mon, 16 Jun 2025 22:13:55 +0200 Message-ID: <20250616201404.1412341-6-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Registers located on page 10 and above are called mailbox-type registers. Each page represents a mailbox and is used to read from and write to configuration of a specific object (DPLL, output, reference or synth). Each mailbox page contains a mask register, which selects an index of the target object to interact with and a semaphore register, which indicates the requested operation. The remaining registers within the page are latch registers, which are populated by the firmware during read operations or by the driver prior to write operations. Operations with these registers requires multiple register reads, writes and polls and all of them need to be done atomically. So add multiop_lock mutex to protect such operations and check the mutex is held by the caller when it's accessing registers from page 10 and above. Signed-off-by: Ivan Vecera --- drivers/dpll/zl3073x/core.c | 14 ++++++++++++++ drivers/dpll/zl3073x/core.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index 3269cea8b4073..60344761545d8 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -122,6 +122,12 @@ EXPORT_SYMBOL_NS_GPL(zl3073x_regmap_config, "ZL3073X"); static bool zl3073x_check_reg(struct zl3073x_dev *zldev, unsigned int reg, size_t size) { + /* Check that multiop lock is held when accessing registers + * from page 10 and above. + */ + if (ZL_REG_PAGE(reg) >=3D 10) + lockdep_assert_held(&zldev->multiop_lock); + /* Check the index is in valid range for indexed register */ if (ZL_REG_OFFSET(reg) > ZL_REG_MAX_OFFSET(reg)) { dev_err(zldev->dev, "Index out of range for reg 0x%04lx\n", @@ -537,6 +543,14 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev, FIELD_GET(GENMASK(15, 8), cfg_ver), FIELD_GET(GENMASK(7, 0), cfg_ver)); =20 + /* Initialize mutex for operations where multiple reads, writes + * and/or polls are required to be done atomically. + */ + rc =3D devm_mutex_init(zldev->dev, &zldev->multiop_lock); + if (rc) + return dev_err_probe(zldev->dev, rc, + "Failed to initialize mutex\n"); + /* Register the device as devlink device */ devlink =3D priv_to_devlink(zldev); devlink_register(devlink); diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h index 75f68bc9a52ee..1a77a69f85a26 100644 --- a/drivers/dpll/zl3073x/core.h +++ b/drivers/dpll/zl3073x/core.h @@ -3,6 +3,7 @@ #ifndef _ZL3073X_H #define _ZL3073X_H =20 +#include #include =20 struct device; @@ -12,10 +13,12 @@ struct regmap; * struct zl3073x_dev - zl3073x device * @dev: pointer to device * @regmap: regmap to access device registers + * @multiop_lock: to serialize multiple register operations */ struct zl3073x_dev { struct device *dev; struct regmap *regmap; + struct mutex multiop_lock; }; =20 enum zl3073x_chip_type { --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 DE49C28C2D5 for ; Mon, 16 Jun 2025 20:15:05 +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=1750104908; cv=none; b=H9Aq5h7Dx02KrueFd1AputUaPC9eb9jroD4wB1WFeiZCsuf+FB/lCLCl2giG2sZ9OS869WrjeMO+A8wp3Dx+KDlQd2JYzO4cWHQc0V9VfnZ0YHR7ko7FIuVfDeaQ8Yxe5RCOkUsna5AJ/dAhocU962CVwxJluMgtULzxRNsQhbY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104908; c=relaxed/simple; bh=ZGNB9siAAJP9Gl4k6gsSjNt6d1f1Hoy+OxtR8RzTr/g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HNJwlJbSnq1kp8MCyZQg2e6Yf/hNEG2++47XiPKT7ewJ7ME5yrwtmBy8iI5MY6l9AkN+qlYV7VV8/NERlqPItFafD26t5qByf8FznCBvEYeZhDGdumFOJ7njAxF8y43vTPJaOuew1lK8V1daPfbW7A1ZoCnHKIfOxQUf8vO/3+0= 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=TbMB+R60; 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="TbMB+R60" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104904; 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=Lf1ZL6+uvUlQBeKMMUfxqte2ezhtxfs68umwc2PZoA4=; b=TbMB+R60fbvfXJ3+HlfXcRgrdBOe8fvYrGqkDGwTM7m9+M/1Rv+aYtKhknnhOHXe52WvR5 e1/2OXiOkVPTxBObaX+Q3MKV855K5wl6dM37WQWWFBjdLM69mRdXWO5xzV1IrX2up8rEEv qOv7mo8PBt/jvyWiE1dN0jKnsymItKs= Received: from mx-prod-mc-02.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-532-S3qhGiw2OrePyyU8ZQKIsg-1; Mon, 16 Jun 2025 16:15:01 -0400 X-MC-Unique: S3qhGiw2OrePyyU8ZQKIsg-1 X-Mimecast-MFC-AGG-ID: S3qhGiw2OrePyyU8ZQKIsg_1750104899 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id DC0C519560A7; Mon, 16 Jun 2025 20:14:58 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6606330001B1; Mon, 16 Jun 2025 20:14:52 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Prathosh Satish , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 06/14] dpll: zl3073x: Fetch invariants during probe Date: Mon, 16 Jun 2025 22:13:56 +0200 Message-ID: <20250616201404.1412341-7-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Several configuration parameters will remain constant at runtime, so we can load them during probe to avoid excessive reads from the hardware. Read the following parameters from the device during probe and store them for later use: * enablement status and frequencies of the synthesizers and their associated DPLL channels * enablement status and type (single-ended or differential) of input pins * associated synthesizers, signal format, and enablement status of outputs Signed-off-by: Ivan Vecera --- v10: * using str_enabled_disabled() where possible --- drivers/dpll/zl3073x/core.c | 249 +++++++++++++++++++++++++++++++ drivers/dpll/zl3073x/core.h | 286 ++++++++++++++++++++++++++++++++++++ drivers/dpll/zl3073x/regs.h | 65 ++++++++ 3 files changed, 600 insertions(+) diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index 60344761545d8..9d9d1403645f3 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -6,10 +6,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include =20 @@ -376,6 +378,25 @@ int zl3073x_poll_zero_u8(struct zl3073x_dev *zldev, un= signed int reg, u8 mask) ZL_POLL_SLEEP_US, ZL_POLL_TIMEOUT_US); } =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 rc; + + /* Set mask for the operation */ + rc =3D zl3073x_write_u16(zldev, mask_reg, mask_val); + if (rc) + return rc; + + /* Trigger the operation */ + rc =3D zl3073x_write_u8(zldev, op_reg, op_val); + if (rc) + return rc; + + /* Wait for the operation to actually finish */ + return zl3073x_poll_zero_u8(zldev, op_reg, op_val); +} + /** * zl3073x_devlink_info_get - Devlink device info callback * @devlink: devlink structure pointer @@ -484,6 +505,229 @@ struct zl3073x_dev *zl3073x_devm_alloc(struct device = *dev) } EXPORT_SYMBOL_NS_GPL(zl3073x_devm_alloc, "ZL3073X"); =20 +/** + * zl3073x_ref_state_fetch - get input reference state + * @zldev: pointer to zl3073x_dev structure + * @index: input reference index to fetch state for + * + * Function fetches information for the given input reference that are + * invariant and stores them for later use. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index) +{ + struct zl3073x_ref *input =3D &zldev->ref[index]; + u8 ref_config; + int rc; + + /* If the input is differential then the configuration for N-pin + * reference is ignored and P-pin config is used for both. + */ + if (zl3073x_is_n_pin(index) && + zl3073x_ref_is_diff(zldev, index - 1)) { + input->enabled =3D zl3073x_ref_is_enabled(zldev, index - 1); + input->diff =3D true; + + return 0; + } + + guard(mutex)(&zldev->multiop_lock); + + /* Read reference configuration */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(index)); + if (rc) + return rc; + + /* Read ref_config register */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref_config); + if (rc) + return rc; + + input->enabled =3D FIELD_GET(ZL_REF_CONFIG_ENABLE, ref_config); + input->diff =3D FIELD_GET(ZL_REF_CONFIG_DIFF_EN, ref_config); + + dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index, + str_enabled_disabled(input->enabled), + input->diff ? "differential" : "single-ended"); + + return rc; +} + +/** + * zl3073x_out_state_fetch - get output state + * @zldev: pointer to zl3073x_dev structure + * @index: output index to fetch state for + * + * Function fetches information for the given output (not output pin) + * that are invariant and stores them for later use. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index) +{ + struct zl3073x_out *out =3D &zldev->out[index]; + u8 output_ctrl, output_mode; + int rc; + + /* Read output configuration */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &output_ctrl); + if (rc) + return rc; + + /* Store info about output enablement and synthesizer the output + * is connected to. + */ + out->enabled =3D FIELD_GET(ZL_OUTPUT_CTRL_EN, output_ctrl); + out->synth =3D FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, output_ctrl); + + dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index, + str_enabled_disabled(out->enabled), out->synth); + + guard(mutex)(&zldev->multiop_lock); + + /* Read output configuration */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(index)); + if (rc) + return rc; + + /* Read output_mode */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode); + if (rc) + return rc; + + /* Extract and store output signal format */ + out->signal_format =3D FIELD_GET(ZL_OUTPUT_MODE_SIGNAL_FORMAT, + output_mode); + + dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index, + out->signal_format); + + return rc; +} + +/** + * zl3073x_synth_state_fetch - get synth state + * @zldev: pointer to zl3073x_dev structure + * @index: synth index to fetch state for + * + * Function fetches information for the given synthesizer that are + * invariant and stores them for later use. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index) +{ + struct zl3073x_synth *synth =3D &zldev->synth[index]; + u16 base, m, n; + u8 synth_ctrl; + u32 mult; + int rc; + + /* Read synth control register */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_SYNTH_CTRL(index), &synth_ctrl); + if (rc) + return rc; + + /* Store info about synth enablement and DPLL channel the synth is + * driven by. + */ + synth->enabled =3D FIELD_GET(ZL_SYNTH_CTRL_EN, synth_ctrl); + synth->dpll =3D FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth_ctrl); + + dev_dbg(zldev->dev, "SYNTH%u is %s and driven by DPLL%u\n", index, + str_enabled_disabled(synth->enabled), synth->dpll); + + guard(mutex)(&zldev->multiop_lock); + + /* Read synth configuration */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD, + ZL_REG_SYNTH_MB_MASK, BIT(index)); + if (rc) + return rc; + + /* The output frequency is determined by the following formula: + * base * multiplier * numerator / denominator + * + * Read registers with these values + */ + rc =3D zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &base); + if (rc) + return rc; + + rc =3D zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &mult); + if (rc) + return rc; + + rc =3D zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &m); + if (rc) + return rc; + + rc =3D zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &n); + if (rc) + return rc; + + /* Check denominator for zero to avoid div by 0 */ + if (!n) { + dev_err(zldev->dev, + "Zero divisor for SYNTH%u retrieved from device\n", + index); + return -EINVAL; + } + + /* Compute and store synth frequency */ + zldev->synth[index].freq =3D div_u64(mul_u32_u32(base * m, mult), n); + + dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index, + zldev->synth[index].freq); + + return rc; +} + +static int +zl3073x_dev_state_fetch(struct zl3073x_dev *zldev) +{ + int rc; + u8 i; + + for (i =3D 0; i < ZL3073X_NUM_REFS; i++) { + rc =3D zl3073x_ref_state_fetch(zldev, i); + if (rc) { + dev_err(zldev->dev, + "Failed to fetch input state: %pe\n", + ERR_PTR(rc)); + return rc; + } + } + + for (i =3D 0; i < ZL3073X_NUM_SYNTHS; i++) { + rc =3D zl3073x_synth_state_fetch(zldev, i); + if (rc) { + dev_err(zldev->dev, + "Failed to fetch synth state: %pe\n", + ERR_PTR(rc)); + return rc; + } + } + + for (i =3D 0; i < ZL3073X_NUM_OUTS; i++) { + rc =3D zl3073x_out_state_fetch(zldev, i); + if (rc) { + dev_err(zldev->dev, + "Failed to fetch output state: %pe\n", + ERR_PTR(rc)); + return rc; + } + } + + return rc; +} + static void zl3073x_devlink_unregister(void *ptr) { devlink_unregister(ptr); @@ -551,6 +795,11 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev, return dev_err_probe(zldev->dev, rc, "Failed to initialize mutex\n"); =20 + /* Fetch device state */ + rc =3D zl3073x_dev_state_fetch(zldev); + if (rc) + return rc; + /* Register the device as devlink device */ devlink =3D priv_to_devlink(zldev); devlink_register(devlink); diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h index 1a77a69f85a26..0d052c02065e5 100644 --- a/drivers/dpll/zl3073x/core.h +++ b/drivers/dpll/zl3073x/core.h @@ -6,19 +6,70 @@ #include #include =20 +#include "regs.h" + struct device; struct regmap; =20 +/* + * Hardware limits for ZL3073x chip family + */ +#define ZL3073X_NUM_REFS 10 +#define ZL3073X_NUM_OUTS 10 +#define ZL3073X_NUM_SYNTHS 5 + +/** + * struct zl3073x_ref - input reference invariant info + * @enabled: input reference is enabled or disabled + * @diff: true if input reference is differential + */ +struct zl3073x_ref { + bool enabled; + bool diff; +}; + +/** + * struct zl3073x_out - output invariant info + * @enabled: out is enabled or disabled + * @synth: synthesizer the out is connected to + * @signal_format: out signal format + */ +struct zl3073x_out { + bool enabled; + u8 synth; + u8 signal_format; +}; + +/** + * struct zl3073x_synth - synthesizer invariant info + * @freq: synthesizer frequency + * @dpll: ID of DPLL the synthesizer is driven by + * @enabled: synth is enabled or disabled + */ +struct zl3073x_synth { + u32 freq; + u8 dpll; + bool enabled; +}; + /** * struct zl3073x_dev - zl3073x device * @dev: pointer to device * @regmap: regmap to access device registers * @multiop_lock: to serialize multiple register operations + * @ref: array of input references' invariants + * @out: array of outs' invariants + * @synth: array of synths' invariants */ struct zl3073x_dev { struct device *dev; struct regmap *regmap; struct mutex multiop_lock; + + /* Invariants */ + struct zl3073x_ref ref[ZL3073X_NUM_REFS]; + struct zl3073x_out out[ZL3073X_NUM_OUTS]; + struct zl3073x_synth synth[ZL3073X_NUM_SYNTHS]; }; =20 enum zl3073x_chip_type { @@ -46,6 +97,8 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev, * Registers operations **********************/ =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_read_u8(struct zl3073x_dev *zldev, unsigned int reg, u8 *val); int zl3073x_read_u16(struct zl3073x_dev *zldev, unsigned int reg, u16 *val= ); @@ -56,4 +109,237 @@ int zl3073x_write_u16(struct zl3073x_dev *zldev, unsig= ned int reg, u16 val); int zl3073x_write_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 val= ); int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val= ); =20 +static inline bool +zl3073x_is_n_pin(u8 id) +{ + /* P-pins ids are even while N-pins are odd */ + return id & 1; +} + +static inline bool +zl3073x_is_p_pin(u8 id) +{ + return !zl3073x_is_n_pin(id); +} + +/** + * zl3073x_input_pin_ref_get - get reference for given input pin + * @id: input pin id + * + * Return: reference id for the given input pin + */ +static inline u8 +zl3073x_input_pin_ref_get(u8 id) +{ + return id; +} + +/** + * zl3073x_output_pin_out_get - get output for the given output pin + * @id: output pin id + * + * Return: output id for the given output pin + */ +static inline u8 +zl3073x_output_pin_out_get(u8 id) +{ + /* Output pin pair shares the single output */ + return id / 2; +} + +/** + * zl3073x_ref_is_diff - check if the given input reference is differential + * @zldev: pointer to zl3073x device + * @index: input reference index + * + * Return: true if reference is differential, false if reference is single= -ended + */ +static inline bool +zl3073x_ref_is_diff(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->ref[index].diff; +} + +/** + * zl3073x_ref_is_enabled - check if the given input reference is enabled + * @zldev: pointer to zl3073x device + * @index: input reference index + * + * Return: true if input refernce is enabled, false otherwise + */ +static inline bool +zl3073x_ref_is_enabled(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->ref[index].enabled; +} + +/** + * zl3073x_synth_dpll_get - get DPLL ID the synth is driven by + * @zldev: pointer to zl3073x device + * @index: synth index + * + * Return: ID of DPLL the given synthetizer is driven by + */ +static inline u8 +zl3073x_synth_dpll_get(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->synth[index].dpll; +} + +/** + * zl3073x_synth_freq_get - get synth current freq + * @zldev: pointer to zl3073x device + * @index: synth index + * + * Return: frequency of given synthetizer + */ +static inline u32 +zl3073x_synth_freq_get(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->synth[index].freq; +} + +/** + * zl3073x_synth_is_enabled - check if the given synth is enabled + * @zldev: pointer to zl3073x device + * @index: synth index + * + * Return: true if synth is enabled, false otherwise + */ +static inline bool +zl3073x_synth_is_enabled(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->synth[index].enabled; +} + +/** + * zl3073x_out_synth_get - get synth connected to given output + * @zldev: pointer to zl3073x device + * @index: output index + * + * Return: index of synth connected to given output. + */ +static inline u8 +zl3073x_out_synth_get(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->out[index].synth; +} + +/** + * zl3073x_out_is_enabled - check if the given output is enabled + * @zldev: pointer to zl3073x device + * @index: output index + * + * Return: true if the output is enabled, false otherwise + */ +static inline bool +zl3073x_out_is_enabled(struct zl3073x_dev *zldev, u8 index) +{ + u8 synth; + + /* Output is enabled only if associated synth is enabled */ + synth =3D zl3073x_out_synth_get(zldev, index); + if (zl3073x_synth_is_enabled(zldev, synth)) + return zldev->out[index].enabled; + + return false; +} + +/** + * zl3073x_out_signal_format_get - get output signal format + * @zldev: pointer to zl3073x device + * @index: output index + * + * Return: signal format of given output + */ +static inline u8 +zl3073x_out_signal_format_get(struct zl3073x_dev *zldev, u8 index) +{ + return zldev->out[index].signal_format; +} + +/** + * zl3073x_out_dpll_get - get DPLL ID the output is driven by + * @zldev: pointer to zl3073x device + * @index: output index + * + * Return: ID of DPLL the given output is driven by + */ +static inline +u8 zl3073x_out_dpll_get(struct zl3073x_dev *zldev, u8 index) +{ + u8 synth; + + /* Get synthesizer connected to given output */ + synth =3D zl3073x_out_synth_get(zldev, index); + + /* Return DPLL that drives the synth */ + return zl3073x_synth_dpll_get(zldev, synth); +} + +/** + * zl3073x_out_is_diff - check if the given output is differential + * @zldev: pointer to zl3073x device + * @index: output index + * + * Return: true if output is differential, false if output is single-ended + */ +static inline bool +zl3073x_out_is_diff(struct zl3073x_dev *zldev, u8 index) +{ + switch (zl3073x_out_signal_format_get(zldev, index)) { + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS: + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF: + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM: + return true; + default: + break; + } + + return false; +} + +/** + * zl3073x_output_pin_is_enabled - check if the given output pin is enabled + * @zldev: pointer to zl3073x device + * @id: output pin id + * + * Checks if the output of the given output pin is enabled and also that + * its signal format also enables the given pin. + * + * Return: true if output pin is enabled, false if output pin is disabled + */ +static inline bool +zl3073x_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id) +{ + u8 output =3D zl3073x_output_pin_out_get(id); + + /* Check if the whole output is enabled */ + if (!zl3073x_out_is_enabled(zldev, output)) + return false; + + /* Check signal format */ + switch (zl3073x_out_signal_format_get(zldev, output)) { + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED: + /* Both output pins are disabled by signal format */ + return false; + + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_1P: + /* Output is one single ended P-pin output */ + if (zl3073x_is_n_pin(id)) + return false; + break; + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_1N: + /* Output is one single ended N-pin output */ + if (zl3073x_is_p_pin(id)) + return false; + break; + default: + /* For other format both pins are enabled */ + break; + } + + return true; +} + #endif /* _ZL3073X_H */ diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index 08bf595935ea1..753b42d8b2093 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -72,4 +72,69 @@ #define ZL_REG_FW_VER ZL_REG(0, 0x05, 2) #define ZL_REG_CUSTOM_CONFIG_VER ZL_REG(0, 0x07, 4) =20 +/*********************************** + * Register Page 9, Synth and Output + ***********************************/ + +#define ZL_REG_SYNTH_CTRL(_idx) \ + ZL_REG_IDX(_idx, 9, 0x00, 1, ZL3073X_NUM_SYNTHS, 1) +#define ZL_SYNTH_CTRL_EN BIT(0) +#define ZL_SYNTH_CTRL_DPLL_SEL GENMASK(6, 4) + +#define ZL_REG_OUTPUT_CTRL(_idx) \ + ZL_REG_IDX(_idx, 9, 0x28, 1, ZL3073X_NUM_OUTS, 1) +#define ZL_OUTPUT_CTRL_EN BIT(0) +#define ZL_OUTPUT_CTRL_SYNTH_SEL GENMASK(6, 4) + +/******************************* + * Register Page 10, Ref Mailbox + *******************************/ + +#define ZL_REG_REF_MB_MASK ZL_REG(10, 0x02, 2) + +#define ZL_REG_REF_MB_SEM ZL_REG(10, 0x04, 1) +#define ZL_REF_MB_SEM_WR BIT(0) +#define ZL_REF_MB_SEM_RD BIT(1) + +#define ZL_REG_REF_CONFIG ZL_REG(10, 0x0d, 1) +#define ZL_REF_CONFIG_ENABLE BIT(0) +#define ZL_REF_CONFIG_DIFF_EN BIT(2) + +/********************************* + * Register Page 13, Synth Mailbox + *********************************/ + +#define ZL_REG_SYNTH_MB_MASK ZL_REG(13, 0x02, 2) + +#define ZL_REG_SYNTH_MB_SEM ZL_REG(13, 0x04, 1) +#define ZL_SYNTH_MB_SEM_WR BIT(0) +#define ZL_SYNTH_MB_SEM_RD BIT(1) + +#define ZL_REG_SYNTH_FREQ_BASE ZL_REG(13, 0x06, 2) +#define ZL_REG_SYNTH_FREQ_MULT ZL_REG(13, 0x08, 4) +#define ZL_REG_SYNTH_FREQ_M ZL_REG(13, 0x0c, 2) +#define ZL_REG_SYNTH_FREQ_N ZL_REG(13, 0x0e, 2) + +/********************************** + * Register Page 14, Output Mailbox + **********************************/ +#define ZL_REG_OUTPUT_MB_MASK ZL_REG(14, 0x02, 2) + +#define ZL_REG_OUTPUT_MB_SEM ZL_REG(14, 0x04, 1) +#define ZL_OUTPUT_MB_SEM_WR BIT(0) +#define ZL_OUTPUT_MB_SEM_RD BIT(1) + +#define ZL_REG_OUTPUT_MODE ZL_REG(14, 0x05, 1) +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT GENMASK(7, 4) +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED 0 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS 1 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF 2 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM 3 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2 4 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_1P 5 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_1N 6 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_INV 7 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV 12 +#define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV 15 + #endif /* _ZL3073X_REGS_H */ --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 9FAC428C844 for ; Mon, 16 Jun 2025 20:15:12 +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=1750104914; cv=none; b=doPnBpJ/swGTDSYqA0wsfR1+0JddrOd7ouxR5RmL6sn+UwGeodXWSPiltQwbVaVgECdGgk1LjO/L9Qr0fp/jiB7gafQEs7OiUZFMeQKEu2nVbB/p66jcumtS25coatfOpkFsvAKUWdRvGCw2dj3OnhtbTeYqK05VMGHGvHBg7os= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104914; c=relaxed/simple; bh=oHZbb1rJLMK/tA9SmXNvYNtEnDhZ66dzgATbWxqSwxE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=aGatijRhAj80AiBLgjEEHPvt4HOF6wIwTwU1q1mb/UA9Zng0PfXqkMZdBF9JWKXquNk85jg/48V8fO+EU2Qv1p6x2HNE4cbMMV9dRFUGMKNdQbnp5CTzp5xs+EVEBgZZb9r5rQ8YNg6pMTEfnsoSGZy78KuwnRkBgS/d70/Q3CQ= 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=b0TRa3gC; 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="b0TRa3gC" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104911; 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=NTJH2oEMnZkAgKTN2kihe9Bkl6Baj4XXA5GAcS4y8jg=; b=b0TRa3gC0iB67/7DzmvQCfhLmFFEHbPWm4IWqCs6L3euJ6f4D5Isb1VV2XuFT71pS8tVxX obvTkjnAtgYpU5J3yWC7TcnGbMT+vEmnU7V/3iFIPbSTuBiov4k88OJ3Plf4ZtX0iNkXmz XZJiN5Rod0apslP6lvpkqblLYENtx7c= Received: from mx-prod-mc-08.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-573-N4jgjWktN4ykds3D4sJ9uA-1; Mon, 16 Jun 2025 16:15:08 -0400 X-MC-Unique: N4jgjWktN4ykds3D4sJ9uA-1 X-Mimecast-MFC-AGG-ID: N4jgjWktN4ykds3D4sJ9uA_1750104905 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id BC15B18089B8; Mon, 16 Jun 2025 20:15:05 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3BD0130044DA; Mon, 16 Jun 2025 20:14:59 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Prathosh Satish , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 07/14] dpll: zl3073x: Add clock_id field Date: Mon, 16 Jun 2025 22:13:57 +0200 Message-ID: <20250616201404.1412341-8-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Add .clock_id to zl3073x_dev structure that will be used by later commits introducing DPLL feature. The clock ID is required for DPLL device registration. To generate this ID, use chip ID read during device initialization. In case where multiple zl3073x based chips are present, the chip ID is shifted and lower bits are filled by an unique value - using the I2C device address for I2C connections and the chip-select value for SPI connections. Signed-off-by: Ivan Vecera --- drivers/dpll/zl3073x/core.c | 6 +++++- drivers/dpll/zl3073x/core.h | 4 +++- drivers/dpll/zl3073x/i2c.c | 4 +++- drivers/dpll/zl3073x/spi.c | 4 +++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index 9d9d1403645f3..ab578c729ac40 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -737,13 +737,14 @@ static void zl3073x_devlink_unregister(void *ptr) * zl3073x_dev_probe - initialize zl3073x device * @zldev: pointer to zl3073x device * @chip_info: chip info based on compatible + * @dev_id: device ID to be used as part of clock ID * * Common initialization of zl3073x device structure. * * Returns: 0 on success, <0 on error */ int zl3073x_dev_probe(struct zl3073x_dev *zldev, - const struct zl3073x_chip_info *chip_info) + const struct zl3073x_chip_info *chip_info, u8 dev_id) { u16 id, revision, fw_ver; struct devlink *devlink; @@ -787,6 +788,9 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev, FIELD_GET(GENMASK(15, 8), cfg_ver), FIELD_GET(GENMASK(7, 0), cfg_ver)); =20 + /* Use chip ID and given dev ID as clock ID */ + zldev->clock_id =3D ((u64)id << 8) | dev_id; + /* Initialize mutex for operations where multiple reads, writes * and/or polls are required to be done atomically. */ diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h index 0d052c02065e5..fcf142f3f8cb6 100644 --- a/drivers/dpll/zl3073x/core.h +++ b/drivers/dpll/zl3073x/core.h @@ -57,6 +57,7 @@ struct zl3073x_synth { * @dev: pointer to device * @regmap: regmap to access device registers * @multiop_lock: to serialize multiple register operations + * @clock_id: clock id of the device * @ref: array of input references' invariants * @out: array of outs' invariants * @synth: array of synths' invariants @@ -65,6 +66,7 @@ struct zl3073x_dev { struct device *dev; struct regmap *regmap; struct mutex multiop_lock; + u64 clock_id; =20 /* Invariants */ struct zl3073x_ref ref[ZL3073X_NUM_REFS]; @@ -91,7 +93,7 @@ extern const struct regmap_config zl3073x_regmap_config; =20 struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev); int zl3073x_dev_probe(struct zl3073x_dev *zldev, - const struct zl3073x_chip_info *chip_info); + const struct zl3073x_chip_info *chip_info, u8 dev_id); =20 /********************** * Registers operations diff --git a/drivers/dpll/zl3073x/i2c.c b/drivers/dpll/zl3073x/i2c.c index bca1cd729895c..82cc929e543d7 100644 --- a/drivers/dpll/zl3073x/i2c.c +++ b/drivers/dpll/zl3073x/i2c.c @@ -24,7 +24,9 @@ static int zl3073x_i2c_probe(struct i2c_client *client) return PTR_ERR(zldev->regmap); } =20 - return zl3073x_dev_probe(zldev, i2c_get_match_data(client)); + /* Initialize device and use I2C address as dev ID */ + return zl3073x_dev_probe(zldev, i2c_get_match_data(client), + client->addr); } =20 static const struct i2c_device_id zl3073x_i2c_id[] =3D { diff --git a/drivers/dpll/zl3073x/spi.c b/drivers/dpll/zl3073x/spi.c index 219676da71b78..6327d4c9c8d94 100644 --- a/drivers/dpll/zl3073x/spi.c +++ b/drivers/dpll/zl3073x/spi.c @@ -24,7 +24,9 @@ static int zl3073x_spi_probe(struct spi_device *spi) return PTR_ERR(zldev->regmap); } =20 - return zl3073x_dev_probe(zldev, spi_get_device_match_data(spi)); + /* Initialize device and use SPI chip select value as dev ID */ + return zl3073x_dev_probe(zldev, spi_get_device_match_data(spi), + spi_get_chipselect(spi, 0)); } =20 static const struct spi_device_id zl3073x_spi_id[] =3D { --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 0E60F28B3F1 for ; Mon, 16 Jun 2025 20:15:21 +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=1750104924; cv=none; b=TkxKn/39hC6H2hgYZysW9U0otTzxcoB49UJUMaDOj+sSnHwi2nYIzOsPJjAneDswqbAFT2uKohHq8C7ps2s7s/l1jC1ZIO3QyXgpcyh+h20soEVyIzOd47d+H5Tfw4TghqmdnBu0Q43mYZ8ytmviNf+4cacqYZiIlTApudSU9QM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104924; c=relaxed/simple; bh=uKUUJXncYmKa1DV/YXbxZ+K+/KL/wUbHzyiPrrg9eAM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bX7P+Nug84yihIXze3FIVp5ffnm+HC3TsVqMrJbCNI6pc9jN199nmL1O0Tev/U5J0jp6IOK+2Qnn5cGLob97yzEZIpTazK0bUj2Vc7XtIScsISSKuS9wjzVyURe04YuXFww4YKuHlpbeodudGKyzgWcjgNM4u/PJQD8rvfu3dag= 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=INiKvWaA; 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="INiKvWaA" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104921; 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=N3H4mw6MbwkMyOzHHvslvOX+nZRjucBH43FQyAawdfM=; b=INiKvWaAFSsszzHa85TwYBjw+NDlwkMZZNpIVmQmol3eVff7udgLLzp2kgsC2gIa93YWfp 7fHDZgVSsTkfPI6mj+2dQDSCfrPkAaUANacr7PzvAzrK2Rv6AXGxDtEYfBqnj6pEHBwiwT fHDd3viGGrIaMuTUylepISlN92o83FY= 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-274-QMahyjwFNeKh1Fj7nEz1mw-1; Mon, 16 Jun 2025 16:15:16 -0400 X-MC-Unique: QMahyjwFNeKh1Fj7nEz1mw-1 X-Mimecast-MFC-AGG-ID: QMahyjwFNeKh1Fj7nEz1mw_1750104913 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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 94591180AD40; Mon, 16 Jun 2025 20:15:12 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 3A1B430001B1; Mon, 16 Jun 2025 20:15:05 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Prathosh Satish , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 08/14] dpll: zl3073x: Read DPLL types and pin properties from system firmware Date: Mon, 16 Jun 2025 22:13:58 +0200 Message-ID: <20250616201404.1412341-9-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Add support for reading of DPLL types and optional pin properties from the system firmware (DT, ACPI...). The DPLL types are stored in property 'dpll-types' as string array and possible values 'pps' and 'eec' are mapped to DPLL enums DPLL_TYPE_PPS and DPLL_TYPE_EEC. The pin properties are stored under 'input-pins' and 'output-pins' sub-nodes and the following ones are supported: * reg integer that specifies pin index * label string that is used by driver as board label * connection-type string that indicates pin connection type * supported-frequencies-hz array of u64 values what frequencies are supported / allowed for given pin with respect to hardware wiring Do not blindly trust system firmware and filter out frequencies that cannot be configured/represented in device (input frequencies have to be factorized by one of the base frequencies and output frequencies have to divide configured synthesizer frequency). Signed-off-by: Ivan Vecera --- drivers/dpll/zl3073x/Makefile | 2 +- drivers/dpll/zl3073x/core.c | 41 ++++ drivers/dpll/zl3073x/core.h | 7 + drivers/dpll/zl3073x/prop.c | 354 ++++++++++++++++++++++++++++++++++ drivers/dpll/zl3073x/prop.h | 34 ++++ 5 files changed, 437 insertions(+), 1 deletion(-) create mode 100644 drivers/dpll/zl3073x/prop.c create mode 100644 drivers/dpll/zl3073x/prop.h diff --git a/drivers/dpll/zl3073x/Makefile b/drivers/dpll/zl3073x/Makefile index 8760f358f5447..56d7d9feeaf0d 100644 --- a/drivers/dpll/zl3073x/Makefile +++ b/drivers/dpll/zl3073x/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 =20 obj-$(CONFIG_ZL3073X) +=3D zl3073x.o -zl3073x-objs :=3D core.o +zl3073x-objs :=3D core.o prop.o =20 obj-$(CONFIG_ZL3073X_I2C) +=3D zl3073x_i2c.o zl3073x_i2c-objs :=3D i2c.o diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index ab578c729ac40..3fd7257221338 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -121,6 +121,47 @@ const struct regmap_config zl3073x_regmap_config =3D { }; EXPORT_SYMBOL_NS_GPL(zl3073x_regmap_config, "ZL3073X"); =20 +/** + * zl3073x_ref_freq_factorize - factorize given frequency + * @freq: input frequency + * @base: base frequency + * @mult: multiplier + * + * Checks if the given frequency can be factorized using one of the + * supported base frequencies. If so the base frequency and multiplier + * are stored into appropriate parameters if they are not NULL. + * + * Return: 0 on success, -EINVAL if the frequency cannot be factorized + */ +int +zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult) +{ + static const u16 base_freqs[] =3D { + 1, 2, 4, 5, 8, 10, 16, 20, 25, 32, 40, 50, 64, 80, 100, 125, + 128, 160, 200, 250, 256, 320, 400, 500, 625, 640, 800, 1000, + 1250, 1280, 1600, 2000, 2500, 3125, 3200, 4000, 5000, 6250, + 6400, 8000, 10000, 12500, 15625, 16000, 20000, 25000, 31250, + 32000, 40000, 50000, 62500, + }; + u32 div; + int i; + + for (i =3D 0; i < ARRAY_SIZE(base_freqs); i++) { + div =3D freq / base_freqs[i]; + + if (div <=3D U16_MAX && (freq % base_freqs[i]) =3D=3D 0) { + if (base) + *base =3D base_freqs[i]; + if (mult) + *mult =3D div; + + return 0; + } + } + + return -EINVAL; +} + static bool zl3073x_check_reg(struct zl3073x_dev *zldev, unsigned int reg, size_t size) { diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h index fcf142f3f8cb6..4fcce761fc5f2 100644 --- a/drivers/dpll/zl3073x/core.h +++ b/drivers/dpll/zl3073x/core.h @@ -14,6 +14,7 @@ struct regmap; /* * Hardware limits for ZL3073x chip family */ +#define ZL3073X_MAX_CHANNELS 5 #define ZL3073X_NUM_REFS 10 #define ZL3073X_NUM_OUTS 10 #define ZL3073X_NUM_SYNTHS 5 @@ -111,6 +112,12 @@ int zl3073x_write_u16(struct zl3073x_dev *zldev, unsig= ned int reg, u16 val); int zl3073x_write_u32(struct zl3073x_dev *zldev, unsigned int reg, u32 val= ); int zl3073x_write_u48(struct zl3073x_dev *zldev, unsigned int reg, u64 val= ); =20 +/***************** + * Misc operations + *****************/ + +int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult); + static inline bool zl3073x_is_n_pin(u8 id) { diff --git a/drivers/dpll/zl3073x/prop.c b/drivers/dpll/zl3073x/prop.c new file mode 100644 index 0000000000000..bc8b78cfb5ae0 --- /dev/null +++ b/drivers/dpll/zl3073x/prop.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "prop.h" + +/** + * zl3073x_pin_check_freq - verify frequency for given pin + * @zldev: pointer to zl3073x device + * @dir: pin direction + * @id: pin index + * @freq: frequency to check + * + * The function checks the given frequency is valid for the device. For in= put + * pins it checks that the frequency can be factorized using supported base + * frequencies. For output pins it checks that the frequency divides conne= cted + * synth frequency without remainder. + * + * Return: true if the frequency is valid, false if not. + */ +static bool +zl3073x_pin_check_freq(struct zl3073x_dev *zldev, enum dpll_pin_direction = dir, + u8 id, u64 freq) +{ + if (freq > U32_MAX) + goto err_inv_freq; + + if (dir =3D=3D DPLL_PIN_DIRECTION_INPUT) { + int rc; + + /* Check if the frequency can be factorized */ + rc =3D zl3073x_ref_freq_factorize(freq, NULL, NULL); + if (rc) + goto err_inv_freq; + } else { + u32 synth_freq; + u8 out, synth; + + /* Get output pin synthesizer */ + out =3D zl3073x_output_pin_out_get(id); + synth =3D zl3073x_out_synth_get(zldev, out); + + /* Get synth frequency */ + synth_freq =3D zl3073x_synth_freq_get(zldev, synth); + + /* Check the frequency divides synth frequency */ + if (synth_freq % (u32)freq) + goto err_inv_freq; + } + + return true; + +err_inv_freq: + dev_warn(zldev->dev, + "Unsupported frequency %llu Hz in firmware node\n", freq); + + return false; +} + +/** + * zl3073x_prop_pin_package_label_set - get package label for the pin + * @zldev: pointer to zl3073x device + * @props: pointer to pin properties + * @dir: pin direction + * @id: pin index + * + * Generates package label string and stores it into pin properties struct= ure. + * + * Possible formats: + * REF - differential input reference + * REFP & REFN - single-ended input reference (P or N pin) + * OUT - differential output + * OUTP & OUTN - single-ended output (P or N pin) + */ +static void +zl3073x_prop_pin_package_label_set(struct zl3073x_dev *zldev, + struct zl3073x_pin_props *props, + enum dpll_pin_direction dir, u8 id) +{ + const char *prefix, *suffix; + bool is_diff; + + if (dir =3D=3D DPLL_PIN_DIRECTION_INPUT) { + u8 ref; + + prefix =3D "REF"; + ref =3D zl3073x_input_pin_ref_get(id); + is_diff =3D zl3073x_ref_is_diff(zldev, ref); + } else { + u8 out; + + prefix =3D "OUT"; + out =3D zl3073x_output_pin_out_get(id); + is_diff =3D zl3073x_out_is_diff(zldev, out); + } + + if (!is_diff) + suffix =3D zl3073x_is_p_pin(id) ? "P" : "N"; + else + suffix =3D ""; /* No suffix for differential one */ + + snprintf(props->package_label, sizeof(props->package_label), "%s%u%s", + prefix, id / 2, suffix); + + /* Set package_label pointer in DPLL core properties to generated + * string. + */ + props->dpll_props.package_label =3D props->package_label; +} + +/** + * zl3073x_prop_pin_fwnode_get - get fwnode for given pin + * @zldev: pointer to zl3073x device + * @props: pointer to pin properties + * @dir: pin direction + * @id: pin index + * + * Return: 0 on success, -ENOENT if the firmware node does not exist + */ +static int +zl3073x_prop_pin_fwnode_get(struct zl3073x_dev *zldev, + struct zl3073x_pin_props *props, + enum dpll_pin_direction dir, u8 id) +{ + struct fwnode_handle *pins_node, *pin_node; + const char *node_name; + + if (dir =3D=3D DPLL_PIN_DIRECTION_INPUT) + node_name =3D "input-pins"; + else + node_name =3D "output-pins"; + + /* Get node containing input or output pins */ + pins_node =3D device_get_named_child_node(zldev->dev, node_name); + if (!pins_node) { + dev_dbg(zldev->dev, "'%s' sub-node is missing\n", node_name); + return -ENOENT; + } + + /* Enumerate child pin nodes and find the requested one */ + fwnode_for_each_child_node(pins_node, pin_node) { + u32 reg; + + if (fwnode_property_read_u32(pin_node, "reg", ®)) + continue; + + if (id =3D=3D reg) + break; + } + + /* Release pin parent node */ + fwnode_handle_put(pins_node); + + /* Save found node */ + props->fwnode =3D pin_node; + + dev_dbg(zldev->dev, "Firmware node for %s %sfound\n", + props->package_label, pin_node ? "" : "NOT "); + + return pin_node ? 0 : -ENOENT; +} + +/** + * zl3073x_pin_props_get - get pin properties + * @zldev: pointer to zl3073x device + * @dir: pin direction + * @index: pin index + * + * The function looks for firmware node for the given pin if it is provided + * by the system firmware (DT or ACPI), allocates pin properties structure, + * generates package label string according pin type and optionally fetches + * board label, connection type, supported frequencies and esync capability + * from the firmware node if it does exist. + * + * Pointer that is returned by this function should be freed using + * @zl3073x_pin_props_put(). + * + * Return: + * * pointer to allocated pin properties structure on success + * * error pointer in case of error + */ +struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev, + enum dpll_pin_direction dir, + u8 index) +{ + struct dpll_pin_frequency *ranges; + struct zl3073x_pin_props *props; + int i, j, num_freqs, rc; + const char *type; + u64 *freqs; + + props =3D kzalloc(sizeof(*props), GFP_KERNEL); + if (!props) + return ERR_PTR(-ENOMEM); + + /* Set default pin type */ + if (dir =3D=3D DPLL_PIN_DIRECTION_INPUT) + props->dpll_props.type =3D DPLL_PIN_TYPE_EXT; + else + props->dpll_props.type =3D DPLL_PIN_TYPE_GNSS; + + props->dpll_props.phase_range.min =3D S32_MIN; + props->dpll_props.phase_range.max =3D S32_MAX; + + zl3073x_prop_pin_package_label_set(zldev, props, dir, index); + + /* Get firmware node for the given pin */ + rc =3D zl3073x_prop_pin_fwnode_get(zldev, props, dir, index); + if (rc) + return props; /* Return if it does not exist */ + + /* Look for label property and store the value as board label */ + fwnode_property_read_string(props->fwnode, "label", + &props->dpll_props.board_label); + + /* Look for pin type property and translate its value to DPLL + * pin type enum if it is present. + */ + if (!fwnode_property_read_string(props->fwnode, "connection-type", + &type)) { + if (!strcmp(type, "ext")) + props->dpll_props.type =3D DPLL_PIN_TYPE_EXT; + else if (!strcmp(type, "gnss")) + props->dpll_props.type =3D DPLL_PIN_TYPE_GNSS; + else if (!strcmp(type, "int")) + props->dpll_props.type =3D DPLL_PIN_TYPE_INT_OSCILLATOR; + else if (!strcmp(type, "synce")) + props->dpll_props.type =3D DPLL_PIN_TYPE_SYNCE_ETH_PORT; + else + dev_warn(zldev->dev, + "Unknown or unsupported pin type '%s'\n", + type); + } + + /* Check if the pin supports embedded sync control */ + props->esync_control =3D fwnode_property_read_bool(props->fwnode, + "esync-control"); + + /* Read supported frequencies property if it is specified */ + num_freqs =3D fwnode_property_count_u64(props->fwnode, + "supported-frequencies-hz"); + if (num_freqs <=3D 0) + /* Return if the property does not exist or number is 0 */ + return props; + + /* The firmware node specifies list of supported frequencies while + * DPLL core pin properties requires list of frequency ranges. + * So read the frequency list into temporary array. + */ + freqs =3D kcalloc(num_freqs, sizeof(*freqs), GFP_KERNEL); + if (!freqs) { + rc =3D -ENOMEM; + goto err_alloc_freqs; + } + + /* Read frequencies list from firmware node */ + fwnode_property_read_u64_array(props->fwnode, + "supported-frequencies-hz", freqs, + num_freqs); + + /* Allocate frequency ranges list and fill it */ + ranges =3D kcalloc(num_freqs, sizeof(*ranges), GFP_KERNEL); + if (!ranges) { + rc =3D -ENOMEM; + goto err_alloc_ranges; + } + + /* Convert list of frequencies to list of frequency ranges but + * filter-out frequencies that are not representable by device + */ + for (i =3D 0, j =3D 0; i < num_freqs; i++) { + struct dpll_pin_frequency freq =3D DPLL_PIN_FREQUENCY(freqs[i]); + + if (zl3073x_pin_check_freq(zldev, dir, index, freqs[i])) { + ranges[j] =3D freq; + j++; + } + } + + /* Save number of freq ranges and pointer to them into pin properties */ + props->dpll_props.freq_supported =3D ranges; + props->dpll_props.freq_supported_num =3D j; + + /* Free temporary array */ + kfree(freqs); + + return props; + +err_alloc_ranges: + kfree(freqs); +err_alloc_freqs: + fwnode_handle_put(props->fwnode); + kfree(props); + + return ERR_PTR(rc); +} + +/** + * zl3073x_pin_props_put - release pin properties + * @props: pin properties to free + * + * The function deallocates given pin properties structure. + */ +void zl3073x_pin_props_put(struct zl3073x_pin_props *props) +{ + /* Free supported frequency ranges list if it is present */ + kfree(props->dpll_props.freq_supported); + + /* Put firmware handle if it is present */ + if (props->fwnode) + fwnode_handle_put(props->fwnode); + + kfree(props); +} + +/** + * zl3073x_prop_dpll_type_get - get DPLL channel type + * @zldev: pointer to zl3073x device + * @index: DPLL channel index + * + * Return: DPLL type for given DPLL channel + */ +enum dpll_type +zl3073x_prop_dpll_type_get(struct zl3073x_dev *zldev, u8 index) +{ + const char *types[ZL3073X_MAX_CHANNELS]; + int count; + + /* Read dpll types property from firmware */ + count =3D device_property_read_string_array(zldev->dev, "dpll-types", + types, ARRAY_SIZE(types)); + + /* Return default if property or entry for given channel is missing */ + if (index >=3D count) + return DPLL_TYPE_PPS; + + if (!strcmp(types[index], "pps")) + return DPLL_TYPE_PPS; + else if (!strcmp(types[index], "eec")) + return DPLL_TYPE_EEC; + + dev_info(zldev->dev, "Unknown DPLL type '%s', using default\n", + types[index]); + + return DPLL_TYPE_PPS; /* Default */ +} diff --git a/drivers/dpll/zl3073x/prop.h b/drivers/dpll/zl3073x/prop.h new file mode 100644 index 0000000000000..721a18f05938b --- /dev/null +++ b/drivers/dpll/zl3073x/prop.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ZL3073X_PROP_H +#define _ZL3073X_PROP_H + +#include + +#include "core.h" + +struct fwnode_handle; + +/** + * struct zl3073x_pin_props - pin properties + * @fwnode: pin firmware node + * @dpll_props: DPLL core pin properties + * @package_label: pin package label + * @esync_control: embedded sync support + */ +struct zl3073x_pin_props { + struct fwnode_handle *fwnode; + struct dpll_pin_properties dpll_props; + char package_label[8]; + bool esync_control; +}; + +enum dpll_type zl3073x_prop_dpll_type_get(struct zl3073x_dev *zldev, u8 in= dex); + +struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev, + enum dpll_pin_direction dir, + u8 index); + +void zl3073x_pin_props_put(struct zl3073x_pin_props *props); + +#endif /* _ZL3073X_PROP_H */ --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 04B5028A70A for ; Mon, 16 Jun 2025 20:15:26 +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=1750104930; cv=none; b=XfabnFUm8Alx3bOrU9t8xF6DFtYzAPaH2QVTo0+C2N2HQ0c78emngh58BWL/djvRhHje1adN2aJbo++dNH4UcGGwP++BGIqpVO16rw4QTcWo+IafEU49juVeZM0MMAOBbDcdtnPdoo6qPDs2f6tlldRWcUBqt4FqWnhzSpaHZFE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104930; c=relaxed/simple; bh=bYlsr6V0iz4gTzm02/UH5mUW1kllxT/E992WHQhZTPc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KcO9+dSBQ8dm+xbzIYwZFCxHxJEiaozOlgs4NyYTkVNZnVv/A47VSDLXwQl9yiUPBEOqTkPfkva0XtDMMk5XpiyfiGBAZGfvJpwVNw5tvRVqkb7C/siyuBuOXqmSj9UiTd1e++Ie5P2zBK7cvrGoFog6k/xFw/Szs6CGjlNDEdA= 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=NmgcOA6t; 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="NmgcOA6t" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104926; 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=pNkEgvoEBRIEi9E++HDP2VW2hGixTmbhM9Sbr/cR2xA=; b=NmgcOA6tjfzsB+xHQIJ3421h6a+fulcTJS61RHUtnNSTRWxTfy/CT4cbGmlHZ4+4LHBdqq 4rWAi5bHv6fPMFxgQz8PmZ6cfXuEEr8Mft7jymWFHqCEf1WfSLnH8iOo0xe4wz6oC1ilVw ZTDlc79WF5hqFzqhrXepK6XrR3MTdf0= Received: from mx-prod-mc-08.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-581-PlfX_G6jOEeIQHlkQnWPJQ-1; Mon, 16 Jun 2025 16:15:22 -0400 X-MC-Unique: PlfX_G6jOEeIQHlkQnWPJQ-1 X-Mimecast-MFC-AGG-ID: PlfX_G6jOEeIQHlkQnWPJQ_1750104920 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D4FD718011CD; Mon, 16 Jun 2025 20:15:19 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1392830001B1; Mon, 16 Jun 2025 20:15:12 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Prathosh Satish , Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 09/14] dpll: zl3073x: Register DPLL devices and pins Date: Mon, 16 Jun 2025 22:13:59 +0200 Message-ID: <20250616201404.1412341-10-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Enumerate all available DPLL channels and registers a DPLL device for each of them. Check all input references and outputs and register DPLL pins for them. Number of registered DPLL pins depends on configuration of references and outputs. If the reference or output is configured as differential one then only one DPLL pin is registered. Both references and outputs can be also disabled from firmware configuration and in this case no DPLL pins are registered. All registrable references are registered to all available DPLL devices with exception of DPLLs that are configured in NCO (numerically controlled oscillator) mode. In this mode DPLL channel acts as PHC and cannot be locked to any reference. Device outputs are connected to one of synthesizers and each synthesizer is driven by some DPLL channel. So output pins belonging to given output are registered to DPLL device that drives associated synthesizer. Finally add kworker task to monitor async changes on all DPLL channels and input pins and to notify about them DPLL core. Output pins are not monitored as their parameters are not changed asynchronously by the device. Co-developed-by: Prathosh Satish Signed-off-by: Prathosh Satish Signed-off-by: Ivan Vecera --- v11: * fixed uninitialized 'rc' in error-path v10: * fixed comparisons to NULL --- drivers/dpll/zl3073x/Makefile | 2 +- drivers/dpll/zl3073x/core.c | 104 ++++ drivers/dpll/zl3073x/core.h | 17 + drivers/dpll/zl3073x/dpll.c | 898 ++++++++++++++++++++++++++++++++++ drivers/dpll/zl3073x/dpll.h | 42 ++ drivers/dpll/zl3073x/regs.h | 56 +++ 6 files changed, 1118 insertions(+), 1 deletion(-) create mode 100644 drivers/dpll/zl3073x/dpll.c create mode 100644 drivers/dpll/zl3073x/dpll.h diff --git a/drivers/dpll/zl3073x/Makefile b/drivers/dpll/zl3073x/Makefile index 56d7d9feeaf0d..5ccddb6194878 100644 --- a/drivers/dpll/zl3073x/Makefile +++ b/drivers/dpll/zl3073x/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 =20 obj-$(CONFIG_ZL3073X) +=3D zl3073x.o -zl3073x-objs :=3D core.o prop.o +zl3073x-objs :=3D core.o dpll.o prop.o =20 obj-$(CONFIG_ZL3073X_I2C) +=3D zl3073x_i2c.o zl3073x_i2c-objs :=3D i2c.o diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c index 3fd7257221338..bd05e40c3984b 100644 --- a/drivers/dpll/zl3073x/core.c +++ b/drivers/dpll/zl3073x/core.c @@ -16,6 +16,7 @@ #include =20 #include "core.h" +#include "dpll.h" #include "regs.h" =20 /* Chip IDs for zl30731 */ @@ -769,6 +770,104 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev) return rc; } =20 +static void +zl3073x_dev_periodic_work(struct kthread_work *work) +{ + struct zl3073x_dev *zldev =3D container_of(work, struct zl3073x_dev, + work.work); + struct zl3073x_dpll *zldpll; + + list_for_each_entry(zldpll, &zldev->dplls, list) + zl3073x_dpll_changes_check(zldpll); + + /* Run twice a second */ + kthread_queue_delayed_work(zldev->kworker, &zldev->work, + msecs_to_jiffies(500)); +} + +static void zl3073x_dev_dpll_fini(void *ptr) +{ + struct zl3073x_dpll *zldpll, *next; + struct zl3073x_dev *zldev =3D ptr; + + /* Stop monitoring thread */ + if (zldev->kworker) { + kthread_cancel_delayed_work_sync(&zldev->work); + kthread_destroy_worker(zldev->kworker); + zldev->kworker =3D NULL; + } + + /* Release DPLLs */ + list_for_each_entry_safe(zldpll, next, &zldev->dplls, list) { + zl3073x_dpll_unregister(zldpll); + list_del(&zldpll->list); + zl3073x_dpll_free(zldpll); + } +} + +static int +zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls) +{ + struct kthread_worker *kworker; + struct zl3073x_dpll *zldpll; + unsigned int i; + int rc; + + INIT_LIST_HEAD(&zldev->dplls); + + /* Initialize all DPLLs */ + for (i =3D 0; i < num_dplls; i++) { + zldpll =3D zl3073x_dpll_alloc(zldev, i); + if (IS_ERR(zldpll)) { + dev_err_probe(zldev->dev, PTR_ERR(zldpll), + "Failed to alloc DPLL%u\n", i); + rc =3D PTR_ERR(zldpll); + goto error; + } + + rc =3D zl3073x_dpll_register(zldpll); + if (rc) { + dev_err_probe(zldev->dev, rc, + "Failed to register DPLL%u\n", i); + zl3073x_dpll_free(zldpll); + goto error; + } + + list_add(&zldpll->list, &zldev->dplls); + } + + /* Perform initial firmware fine phase correction */ + rc =3D zl3073x_dpll_init_fine_phase_adjust(zldev); + if (rc) { + dev_err_probe(zldev->dev, rc, + "Failed to init fine phase correction\n"); + goto error; + } + + /* Initialize monitoring thread */ + kthread_init_delayed_work(&zldev->work, zl3073x_dev_periodic_work); + kworker =3D kthread_run_worker(0, "zl3073x-%s", dev_name(zldev->dev)); + if (IS_ERR(kworker)) { + rc =3D PTR_ERR(kworker); + goto error; + } + + zldev->kworker =3D kworker; + kthread_queue_delayed_work(zldev->kworker, &zldev->work, 0); + + /* Add devres action to release DPLL related resources */ + rc =3D devm_add_action_or_reset(zldev->dev, zl3073x_dev_dpll_fini, zldev); + if (rc) + goto error; + + return 0; + +error: + zl3073x_dev_dpll_fini(zldev); + + return rc; +} + static void zl3073x_devlink_unregister(void *ptr) { devlink_unregister(ptr); @@ -845,6 +944,11 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev, if (rc) return rc; =20 + /* Register DPLL channels */ + rc =3D zl3073x_devm_dpll_init(zldev, chip_info->num_channels); + if (rc) + return rc; + /* Register the device as devlink device */ devlink =3D priv_to_devlink(zldev); devlink_register(devlink); diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h index 4fcce761fc5f2..adae94944376a 100644 --- a/drivers/dpll/zl3073x/core.h +++ b/drivers/dpll/zl3073x/core.h @@ -3,6 +3,8 @@ #ifndef _ZL3073X_H #define _ZL3073X_H =20 +#include +#include #include #include =20 @@ -10,6 +12,7 @@ =20 struct device; struct regmap; +struct zl3073x_dpll; =20 /* * Hardware limits for ZL3073x chip family @@ -18,6 +21,10 @@ struct regmap; #define ZL3073X_NUM_REFS 10 #define ZL3073X_NUM_OUTS 10 #define ZL3073X_NUM_SYNTHS 5 +#define ZL3073X_NUM_INPUT_PINS ZL3073X_NUM_REFS +#define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2) +#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \ + ZL3073X_NUM_OUTPUT_PINS) =20 /** * struct zl3073x_ref - input reference invariant info @@ -62,6 +69,9 @@ struct zl3073x_synth { * @ref: array of input references' invariants * @out: array of outs' invariants * @synth: array of synths' invariants + * @dplls: list of DPLLs + * @kworker: thread for periodic work + * @work: periodic work */ struct zl3073x_dev { struct device *dev; @@ -73,6 +83,13 @@ struct zl3073x_dev { struct zl3073x_ref ref[ZL3073X_NUM_REFS]; struct zl3073x_out out[ZL3073X_NUM_OUTS]; struct zl3073x_synth synth[ZL3073X_NUM_SYNTHS]; + + /* DPLL channels */ + struct list_head dplls; + + /* Monitor */ + struct kthread_worker *kworker; + struct kthread_delayed_work work; }; =20 enum zl3073x_chip_type { diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c new file mode 100644 index 0000000000000..56ca5c8dd9155 --- /dev/null +++ b/drivers/dpll/zl3073x/dpll.c @@ -0,0 +1,898 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "dpll.h" +#include "prop.h" +#include "regs.h" + +#define ZL3073X_DPLL_REF_NONE ZL3073X_NUM_REFS +#define ZL3073X_DPLL_REF_IS_VALID(_ref) ((_ref) !=3D ZL3073X_DPLL_REF_NONE) + +/** + * struct zl3073x_dpll_pin - DPLL pin + * @list: this DPLL pin list entry + * @dpll: DPLL the pin is registered to + * @dpll_pin: pointer to registered dpll_pin + * @label: package label + * @dir: pin direction + * @id: pin id + * @prio: pin priority <0, 14> + * @selectable: pin is selectable in automatic mode + * @pin_state: last saved pin state + */ +struct zl3073x_dpll_pin { + struct list_head list; + struct zl3073x_dpll *dpll; + struct dpll_pin *dpll_pin; + char label[8]; + enum dpll_pin_direction dir; + u8 id; + u8 prio; + bool selectable; + enum dpll_pin_state pin_state; +}; + +/** + * zl3073x_dpll_is_input_pin - check if the pin is input one + * @pin: pin to check + * + * Return: true if pin is input, false if pin is output. + */ +static bool +zl3073x_dpll_is_input_pin(struct zl3073x_dpll_pin *pin) +{ + return pin->dir =3D=3D DPLL_PIN_DIRECTION_INPUT; +} + +/** + * zl3073x_dpll_is_p_pin - check if the pin is P-pin + * @pin: pin to check + * + * Return: true if the pin is P-pin, false if it is N-pin + */ +static bool +zl3073x_dpll_is_p_pin(struct zl3073x_dpll_pin *pin) +{ + return zl3073x_is_p_pin(pin->id); +} + +static int +zl3073x_dpll_pin_direction_get(const struct dpll_pin *dpll_pin, void *pin_= priv, + const struct dpll_device *dpll, void *dpll_priv, + enum dpll_pin_direction *direction, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll_pin *pin =3D pin_priv; + + *direction =3D pin->dir; + + return 0; +} + +/** + * zl3073x_dpll_selected_ref_get - get currently selected reference + * @zldpll: pointer to zl3073x_dpll + * @ref: place to store selected reference + * + * Check for currently selected reference the DPLL should be locked to + * and stores its index to given @ref. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_selected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref) +{ + struct zl3073x_dev *zldev =3D zldpll->dev; + u8 state, value; + int rc; + + switch (zldpll->refsel_mode) { + case ZL_DPLL_MODE_REFSEL_MODE_AUTO: + /* For automatic mode read refsel_status register */ + rc =3D zl3073x_read_u8(zldev, + ZL_REG_DPLL_REFSEL_STATUS(zldpll->id), + &value); + if (rc) + return rc; + + /* Extract reference state */ + state =3D FIELD_GET(ZL_DPLL_REFSEL_STATUS_STATE, value); + + /* Return the reference only if the DPLL is locked to it */ + if (state =3D=3D ZL_DPLL_REFSEL_STATUS_STATE_LOCK) + *ref =3D FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, value); + else + *ref =3D ZL3073X_DPLL_REF_NONE; + break; + case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK: + /* For manual mode return stored value */ + *ref =3D zldpll->forced_ref; + break; + default: + /* For other modes like NCO, freerun... there is no input ref */ + *ref =3D ZL3073X_DPLL_REF_NONE; + break; + } + + return 0; +} + +/** + * zl3073x_dpll_connected_ref_get - get currently connected reference + * @zldpll: pointer to zl3073x_dpll + * @ref: place to store selected reference + * + * Looks for currently connected the DPLL is locked to and stores its index + * to given @ref. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_connected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref) +{ + struct zl3073x_dev *zldev =3D zldpll->dev; + int rc; + + /* Get currently selected input reference */ + rc =3D zl3073x_dpll_selected_ref_get(zldpll, ref); + if (rc) + return rc; + + if (ZL3073X_DPLL_REF_IS_VALID(*ref)) { + u8 ref_status; + + /* Read the reference monitor status */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(*ref), + &ref_status); + if (rc) + return rc; + + /* If the monitor indicates an error nothing is connected */ + if (ref_status !=3D ZL_REF_MON_STATUS_OK) + *ref =3D ZL3073X_DPLL_REF_NONE; + } + + return 0; +} + +/** + * zl3073x_dpll_ref_prio_get - get priority for given input pin + * @pin: pointer to pin + * @prio: place to store priority + * + * Reads current priority for the given input pin and stores the value + * to @prio. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_ref_prio_get(struct zl3073x_dpll_pin *pin, u8 *prio) +{ + struct zl3073x_dpll *zldpll =3D pin->dpll; + struct zl3073x_dev *zldev =3D zldpll->dev; + u8 ref, ref_prio; + int rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Read DPLL configuration */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD, + ZL_REG_DPLL_MB_MASK, BIT(zldpll->id)); + if (rc) + return rc; + + /* Read reference priority - one value for P&N pins (4 bits/pin) */ + ref =3D zl3073x_input_pin_ref_get(pin->id); + rc =3D zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2), + &ref_prio); + if (rc) + return rc; + + /* Select nibble according pin type */ + if (zl3073x_dpll_is_p_pin(pin)) + *prio =3D FIELD_GET(ZL_DPLL_REF_PRIO_REF_P, ref_prio); + else + *prio =3D FIELD_GET(ZL_DPLL_REF_PRIO_REF_N, ref_prio); + + return rc; +} + +/** + * zl3073x_dpll_ref_state_get - get status for given input pin + * @pin: pointer to pin + * @state: place to store status + * + * Checks current status for the given input pin and stores the value + * to @state. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin, + enum dpll_pin_state *state) +{ + struct zl3073x_dpll *zldpll =3D pin->dpll; + struct zl3073x_dev *zldev =3D zldpll->dev; + u8 ref, ref_conn, status; + int rc; + + ref =3D zl3073x_input_pin_ref_get(pin->id); + + /* Get currently connected reference */ + rc =3D zl3073x_dpll_connected_ref_get(zldpll, &ref_conn); + if (rc) + return rc; + + if (ref =3D=3D ref_conn) { + *state =3D DPLL_PIN_STATE_CONNECTED; + return 0; + } + + /* If the DPLL is running in automatic mode and the reference is + * selectable and its monitor does not report any error then report + * pin as selectable. + */ + if (zldpll->refsel_mode =3D=3D ZL_DPLL_MODE_REFSEL_MODE_AUTO && + pin->selectable) { + /* Read reference monitor status */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), + &status); + if (rc) + return rc; + + /* If the monitor indicates errors report the reference + * as disconnected + */ + if (status =3D=3D ZL_REF_MON_STATUS_OK) { + *state =3D DPLL_PIN_STATE_SELECTABLE; + return 0; + } + } + + /* Otherwise report the pin as disconnected */ + *state =3D DPLL_PIN_STATE_DISCONNECTED; + + return 0; +} + +static int +zl3073x_dpll_input_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_pin *pin =3D pin_priv; + + return zl3073x_dpll_ref_state_get(pin, state); +} + +static int +zl3073x_dpll_output_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) +{ + /* If the output pin is registered then it is always connected */ + *state =3D DPLL_PIN_STATE_CONNECTED; + + return 0; +} + +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; + struct zl3073x_dev *zldev =3D zldpll->dev; + u8 mon_status; + int rc; + + rc =3D zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(zldpll->id), + &mon_status); + + if (rc) + return rc; + + if (FIELD_GET(ZL_DPLL_MON_STATUS_LOCK, mon_status)) { + if (FIELD_GET(ZL_DPLL_MON_STATUS_HO_READY, mon_status)) + *status =3D DPLL_LOCK_STATUS_LOCKED_HO_ACQ; + else + *status =3D DPLL_LOCK_STATUS_LOCKED; + } else if (FIELD_GET(ZL_DPLL_MON_STATUS_HO, mon_status)) { + *status =3D DPLL_LOCK_STATUS_HOLDOVER; + } else { + *status =3D DPLL_LOCK_STATUS_UNLOCKED; + } + + return rc; +} + +static int +zl3073x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv, + enum dpll_mode *mode, struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + + switch (zldpll->refsel_mode) { + case ZL_DPLL_MODE_REFSEL_MODE_FREERUN: + case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER: + case ZL_DPLL_MODE_REFSEL_MODE_NCO: + case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK: + /* Use MANUAL for device FREERUN, HOLDOVER, NCO and + * REFLOCK modes + */ + *mode =3D DPLL_MODE_MANUAL; + break; + case ZL_DPLL_MODE_REFSEL_MODE_AUTO: + /* Use AUTO for device AUTO mode */ + *mode =3D DPLL_MODE_AUTOMATIC; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops =3D { + .direction_get =3D zl3073x_dpll_pin_direction_get, + .state_on_dpll_get =3D zl3073x_dpll_input_pin_state_on_dpll_get, +}; + +static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops =3D { + .direction_get =3D zl3073x_dpll_pin_direction_get, + .state_on_dpll_get =3D zl3073x_dpll_output_pin_state_on_dpll_get, +}; + +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, +}; + +/** + * zl3073x_dpll_pin_alloc - allocate DPLL pin + * @zldpll: pointer to zl3073x_dpll + * @dir: pin direction + * @id: pin id + * + * Allocates and initializes zl3073x_dpll_pin structure for given + * pin id and direction. + * + * Return: pointer to allocated structure on success, error pointer on err= or + */ +static struct zl3073x_dpll_pin * +zl3073x_dpll_pin_alloc(struct zl3073x_dpll *zldpll, enum dpll_pin_directio= n dir, + u8 id) +{ + struct zl3073x_dpll_pin *pin; + + pin =3D kzalloc(sizeof(*pin), GFP_KERNEL); + if (!pin) + return ERR_PTR(-ENOMEM); + + pin->dpll =3D zldpll; + pin->dir =3D dir; + pin->id =3D id; + + return pin; +} + +/** + * zl3073x_dpll_pin_free - deallocate DPLL pin + * @pin: pin to free + * + * Deallocates DPLL pin previously allocated by @zl3073x_dpll_pin_alloc. + */ +static void +zl3073x_dpll_pin_free(struct zl3073x_dpll_pin *pin) +{ + WARN(pin->dpll_pin, "DPLL pin is still registered\n"); + + kfree(pin); +} + +/** + * zl3073x_dpll_pin_register - register DPLL pin + * @pin: pointer to DPLL pin + * @index: absolute pin index for registration + * + * Registers given DPLL pin into DPLL sub-system. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index) +{ + struct zl3073x_dpll *zldpll =3D pin->dpll; + struct zl3073x_pin_props *props; + const struct dpll_pin_ops *ops; + int rc; + + /* Get pin properties */ + props =3D zl3073x_pin_props_get(zldpll->dev, pin->dir, pin->id); + if (IS_ERR(props)) + return PTR_ERR(props); + + /* Save package label */ + strscpy(pin->label, props->package_label); + + if (zl3073x_dpll_is_input_pin(pin)) { + rc =3D zl3073x_dpll_ref_prio_get(pin, &pin->prio); + if (rc) + goto err_prio_get; + + if (pin->prio =3D=3D ZL_DPLL_REF_PRIO_NONE) { + /* Clamp prio to max value & mark pin non-selectable */ + pin->prio =3D ZL_DPLL_REF_PRIO_MAX; + pin->selectable =3D false; + } else { + /* Mark pin as selectable */ + pin->selectable =3D true; + } + } + + /* Create or get existing DPLL pin */ + pin->dpll_pin =3D dpll_pin_get(zldpll->dev->clock_id, index, THIS_MODULE, + &props->dpll_props); + if (IS_ERR(pin->dpll_pin)) { + rc =3D PTR_ERR(pin->dpll_pin); + goto err_pin_get; + } + + if (zl3073x_dpll_is_input_pin(pin)) + ops =3D &zl3073x_dpll_input_pin_ops; + else + ops =3D &zl3073x_dpll_output_pin_ops; + + /* Register the pin */ + rc =3D dpll_pin_register(zldpll->dpll_dev, pin->dpll_pin, ops, pin); + if (rc) + goto err_register; + + /* Free pin properties */ + zl3073x_pin_props_put(props); + + return 0; + +err_register: + dpll_pin_put(pin->dpll_pin); +err_prio_get: + pin->dpll_pin =3D NULL; +err_pin_get: + zl3073x_pin_props_put(props); + + return rc; +} + +/** + * zl3073x_dpll_pin_unregister - unregister DPLL pin + * @pin: pointer to DPLL pin + * + * Unregisters pin previously registered by @zl3073x_dpll_pin_register. + */ +static void +zl3073x_dpll_pin_unregister(struct zl3073x_dpll_pin *pin) +{ + struct zl3073x_dpll *zldpll =3D pin->dpll; + const struct dpll_pin_ops *ops; + + WARN(!pin->dpll_pin, "DPLL pin is not registered\n"); + + if (zl3073x_dpll_is_input_pin(pin)) + ops =3D &zl3073x_dpll_input_pin_ops; + else + ops =3D &zl3073x_dpll_output_pin_ops; + + /* Unregister the pin */ + dpll_pin_unregister(zldpll->dpll_dev, pin->dpll_pin, ops, pin); + + dpll_pin_put(pin->dpll_pin); + pin->dpll_pin =3D NULL; +} + +/** + * zl3073x_dpll_pins_unregister - unregister all registered DPLL pins + * @zldpll: pointer to zl3073x_dpll structure + * + * Enumerates all DPLL pins registered to given DPLL device and + * unregisters them. + */ +static void +zl3073x_dpll_pins_unregister(struct zl3073x_dpll *zldpll) +{ + struct zl3073x_dpll_pin *pin, *next; + + list_for_each_entry_safe(pin, next, &zldpll->pins, list) { + zl3073x_dpll_pin_unregister(pin); + list_del(&pin->list); + zl3073x_dpll_pin_free(pin); + } +} + +/** + * zl3073x_dpll_pin_is_registrable - check if the pin is registrable + * @zldpll: pointer to zl3073x_dpll structure + * @dir: pin direction + * @index: pin index + * + * Checks if the given pin can be registered to given DPLL. For both + * directions the pin can be registered if it is enabled. In case of + * differential signal type only P-pin is reported as registrable. + * And additionally for the output pin, the pin can be registered only + * if it is connected to synthesizer that is driven by given DPLL. + * + * Return: true if the pin is registrable, false if not + */ +static bool +zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll *zldpll, + enum dpll_pin_direction dir, u8 index) +{ + struct zl3073x_dev *zldev =3D zldpll->dev; + bool is_diff, is_enabled; + const char *name; + + if (dir =3D=3D DPLL_PIN_DIRECTION_INPUT) { + u8 ref =3D zl3073x_input_pin_ref_get(index); + + name =3D "REF"; + + /* Skip the pin if the DPLL is running in NCO mode */ + if (zldpll->refsel_mode =3D=3D ZL_DPLL_MODE_REFSEL_MODE_NCO) + return false; + + is_diff =3D zl3073x_ref_is_diff(zldev, ref); + is_enabled =3D zl3073x_ref_is_enabled(zldev, ref); + } else { + /* Output P&N pair shares single HW output */ + u8 out =3D zl3073x_output_pin_out_get(index); + + name =3D "OUT"; + + /* Skip the pin if it is connected to different DPLL channel */ + if (zl3073x_out_dpll_get(zldev, out) !=3D zldpll->id) { + dev_dbg(zldev->dev, + "%s%u is driven by different DPLL\n", name, + out); + + return false; + } + + is_diff =3D zl3073x_out_is_diff(zldev, out); + is_enabled =3D zl3073x_out_is_enabled(zldev, out); + } + + /* Skip N-pin if the corresponding input/output is differential */ + if (is_diff && zl3073x_is_n_pin(index)) { + dev_dbg(zldev->dev, "%s%u is differential, skipping N-pin\n", + name, index / 2); + + return false; + } + + /* Skip the pin if it is disabled */ + if (!is_enabled) { + dev_dbg(zldev->dev, "%s%u%c is disabled\n", name, index / 2, + zl3073x_is_p_pin(index) ? 'P' : 'N'); + + return false; + } + + return true; +} + +/** + * zl3073x_dpll_pins_register - register all registerable DPLL pins + * @zldpll: pointer to zl3073x_dpll structure + * + * Enumerates all possible input/output pins and registers all of them + * that are registrable. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_pins_register(struct zl3073x_dpll *zldpll) +{ + struct zl3073x_dpll_pin *pin; + enum dpll_pin_direction dir; + u8 id, index; + int rc; + + /* Process input pins */ + for (index =3D 0; index < ZL3073X_NUM_PINS; index++) { + /* First input pins and then output pins */ + if (index < ZL3073X_NUM_INPUT_PINS) { + id =3D index; + dir =3D DPLL_PIN_DIRECTION_INPUT; + } else { + id =3D index - ZL3073X_NUM_INPUT_PINS; + dir =3D DPLL_PIN_DIRECTION_OUTPUT; + } + + /* Check if the pin registrable to this DPLL */ + if (!zl3073x_dpll_pin_is_registrable(zldpll, dir, id)) + continue; + + pin =3D zl3073x_dpll_pin_alloc(zldpll, dir, id); + if (IS_ERR(pin)) { + rc =3D PTR_ERR(pin); + goto error; + } + + rc =3D zl3073x_dpll_pin_register(pin, index); + if (rc) + goto error; + + list_add(&pin->list, &zldpll->pins); + } + + return 0; + +error: + zl3073x_dpll_pins_unregister(zldpll); + + return rc; +} + +/** + * zl3073x_dpll_device_register - register DPLL device + * @zldpll: pointer to zl3073x_dpll structure + * + * Registers given DPLL device into DPLL sub-system. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll) +{ + struct zl3073x_dev *zldev =3D zldpll->dev; + u8 dpll_mode_refsel; + int rc; + + /* Read DPLL mode and forcibly selected reference */ + rc =3D zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(zldpll->id), + &dpll_mode_refsel); + if (rc) + return rc; + + /* Extract mode and selected input reference */ + zldpll->refsel_mode =3D FIELD_GET(ZL_DPLL_MODE_REFSEL_MODE, + dpll_mode_refsel); + zldpll->forced_ref =3D FIELD_GET(ZL_DPLL_MODE_REFSEL_REF, + dpll_mode_refsel); + + zldpll->dpll_dev =3D dpll_device_get(zldev->clock_id, zldpll->id, + THIS_MODULE); + if (IS_ERR(zldpll->dpll_dev)) { + rc =3D PTR_ERR(zldpll->dpll_dev); + zldpll->dpll_dev =3D NULL; + + return rc; + } + + rc =3D dpll_device_register(zldpll->dpll_dev, + zl3073x_prop_dpll_type_get(zldev, zldpll->id), + &zl3073x_dpll_device_ops, zldpll); + if (rc) { + dpll_device_put(zldpll->dpll_dev); + zldpll->dpll_dev =3D NULL; + } + + return rc; +} + +/** + * zl3073x_dpll_device_unregister - unregister DPLL device + * @zldpll: pointer to zl3073x_dpll structure + * + * Unregisters given DPLL device from DPLL sub-system previously registered + * by @zl3073x_dpll_device_register. + */ +static void +zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll) +{ + WARN(!zldpll->dpll_dev, "DPLL device is not registered\n"); + + dpll_device_unregister(zldpll->dpll_dev, &zl3073x_dpll_device_ops, + zldpll); + dpll_device_put(zldpll->dpll_dev); + zldpll->dpll_dev =3D NULL; +} + +/** + * zl3073x_dpll_changes_check - check for changes and send notifications + * @zldpll: pointer to zl3073x_dpll structure + * + * Checks for changes on given DPLL device and its registered DPLL pins + * and sends notifications about them. + * + * This function is periodically called from @zl3073x_dev_periodic_work. + */ +void +zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll) +{ + struct zl3073x_dev *zldev =3D zldpll->dev; + enum dpll_lock_status lock_status; + struct device *dev =3D zldev->dev; + struct zl3073x_dpll_pin *pin; + int rc; + + /* Get current lock status for the DPLL */ + rc =3D zl3073x_dpll_lock_status_get(zldpll->dpll_dev, zldpll, + &lock_status, NULL, NULL); + if (rc) { + dev_err(dev, "Failed to get DPLL%u lock status: %pe\n", + zldpll->id, ERR_PTR(rc)); + return; + } + + /* 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); + } + + /* Input pin monitoring does make sense only in automatic + * or forced reference modes. + */ + if (zldpll->refsel_mode !=3D ZL_DPLL_MODE_REFSEL_MODE_AUTO && + zldpll->refsel_mode !=3D ZL_DPLL_MODE_REFSEL_MODE_REFLOCK) + return; + + list_for_each_entry(pin, &zldpll->pins, list) { + enum dpll_pin_state state; + + /* Output pins change checks are not necessary because output + * states are constant. + */ + if (!zl3073x_dpll_is_input_pin(pin)) + continue; + + rc =3D zl3073x_dpll_ref_state_get(pin, &state); + if (rc) { + dev_err(dev, + "Failed to get %s on DPLL%u state: %pe\n", + pin->label, zldpll->id, ERR_PTR(rc)); + return; + } + + if (state !=3D pin->pin_state) { + dev_dbg(dev, "%s state changed: %u->%u\n", pin->label, + pin->pin_state, state); + pin->pin_state =3D state; + dpll_pin_change_ntf(pin->dpll_pin); + } + } +} + +/** + * zl3073x_dpll_init_fine_phase_adjust - do initial fine phase adjustments + * @zldev: pointer to zl3073x device + * + * Performs initial fine phase adjustments needed per datasheet. + * + * Return: 0 on success, <0 on error + */ +int +zl3073x_dpll_init_fine_phase_adjust(struct zl3073x_dev *zldev) +{ + int rc; + + rc =3D zl3073x_write_u8(zldev, ZL_REG_SYNTH_PHASE_SHIFT_MASK, 0x1f); + if (rc) + return rc; + + rc =3D zl3073x_write_u8(zldev, ZL_REG_SYNTH_PHASE_SHIFT_INTVL, 0x01); + if (rc) + return rc; + + rc =3D zl3073x_write_u16(zldev, ZL_REG_SYNTH_PHASE_SHIFT_DATA, 0xffff); + if (rc) + return rc; + + rc =3D zl3073x_write_u8(zldev, ZL_REG_SYNTH_PHASE_SHIFT_CTRL, 0x01); + if (rc) + return rc; + + return rc; +} + +/** + * zl3073x_dpll_alloc - allocate DPLL device + * @zldev: pointer to zl3073x device + * @ch: DPLL channel number + * + * Allocates DPLL device structure for given DPLL channel. + * + * Return: pointer to DPLL device on success, error pointer on error + */ +struct zl3073x_dpll * +zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch) +{ + struct zl3073x_dpll *zldpll; + + zldpll =3D kzalloc(sizeof(*zldpll), GFP_KERNEL); + if (!zldpll) + return ERR_PTR(-ENOMEM); + + zldpll->dev =3D zldev; + zldpll->id =3D ch; + INIT_LIST_HEAD(&zldpll->pins); + + return zldpll; +} + +/** + * zl3073x_dpll_free - free DPLL device + * @zldpll: pointer to zl3073x_dpll structure + * + * Deallocates given DPLL device previously allocated by @zl3073x_dpll_all= oc. + */ +void +zl3073x_dpll_free(struct zl3073x_dpll *zldpll) +{ + WARN(zldpll->dpll_dev, "DPLL device is still registered\n"); + + kfree(zldpll); +} + +/** + * zl3073x_dpll_register - register DPLL device and all its pins + * @zldpll: pointer to zl3073x_dpll structure + * + * Registers given DPLL device and all its pins into DPLL sub-system. + * + * Return: 0 on success, <0 on error + */ +int +zl3073x_dpll_register(struct zl3073x_dpll *zldpll) +{ + int rc; + + rc =3D zl3073x_dpll_device_register(zldpll); + if (rc) + return rc; + + rc =3D zl3073x_dpll_pins_register(zldpll); + if (rc) { + zl3073x_dpll_device_unregister(zldpll); + return rc; + } + + return 0; +} + +/** + * zl3073x_dpll_unregister - unregister DPLL device and its pins + * @zldpll: pointer to zl3073x_dpll structure + * + * Unregisters given DPLL device and all its pins from DPLL sub-system + * previously registered by @zl3073x_dpll_register. + */ +void +zl3073x_dpll_unregister(struct zl3073x_dpll *zldpll) +{ + /* Unregister all pins and dpll */ + zl3073x_dpll_pins_unregister(zldpll); + zl3073x_dpll_device_unregister(zldpll); +} diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h new file mode 100644 index 0000000000000..db7388cc377fd --- /dev/null +++ b/drivers/dpll/zl3073x/dpll.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ZL3073X_DPLL_H +#define _ZL3073X_DPLL_H + +#include +#include + +#include "core.h" + +/** + * struct zl3073x_dpll - ZL3073x DPLL sub-device structure + * @list: this DPLL list entry + * @dev: pointer to multi-function parent device + * @id: DPLL index + * @refsel_mode: reference selection mode + * @forced_ref: selected reference in forced reference lock mode + * @dpll_dev: pointer to registered DPLL device + * @lock_status: last saved DPLL lock status + * @pins: list of pins + */ +struct zl3073x_dpll { + struct list_head list; + struct zl3073x_dev *dev; + u8 id; + u8 refsel_mode; + u8 forced_ref; + struct dpll_device *dpll_dev; + enum dpll_lock_status lock_status; + struct list_head pins; +}; + +struct zl3073x_dpll *zl3073x_dpll_alloc(struct zl3073x_dev *zldev, u8 ch); +void zl3073x_dpll_free(struct zl3073x_dpll *zldpll); + +int zl3073x_dpll_register(struct zl3073x_dpll *zldpll); +void zl3073x_dpll_unregister(struct zl3073x_dpll *zldpll); + +int zl3073x_dpll_init_fine_phase_adjust(struct zl3073x_dev *zldev); +void zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll); + +#endif /* _ZL3073X_DPLL_H */ diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index 753b42d8b2093..34e905053a1ef 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -72,6 +72,40 @@ #define ZL_REG_FW_VER ZL_REG(0, 0x05, 2) #define ZL_REG_CUSTOM_CONFIG_VER ZL_REG(0, 0x07, 4) =20 +/************************* + * Register Page 2, Status + *************************/ + +#define ZL_REG_REF_MON_STATUS(_idx) \ + ZL_REG_IDX(_idx, 2, 0x02, 1, ZL3073X_NUM_REFS, 1) +#define ZL_REF_MON_STATUS_OK 0 /* all bits zeroed */ + +#define ZL_REG_DPLL_MON_STATUS(_idx) \ + ZL_REG_IDX(_idx, 2, 0x10, 1, ZL3073X_MAX_CHANNELS, 1) +#define ZL_DPLL_MON_STATUS_LOCK BIT(0) +#define ZL_DPLL_MON_STATUS_HO BIT(1) +#define ZL_DPLL_MON_STATUS_HO_READY BIT(2) + +#define ZL_REG_DPLL_REFSEL_STATUS(_idx) \ + ZL_REG_IDX(_idx, 2, 0x30, 1, ZL3073X_MAX_CHANNELS, 1) +#define ZL_DPLL_REFSEL_STATUS_REFSEL GENMASK(3, 0) +#define ZL_DPLL_REFSEL_STATUS_STATE GENMASK(6, 4) +#define ZL_DPLL_REFSEL_STATUS_STATE_LOCK 4 + +/*********************** + * Register Page 5, DPLL + ***********************/ + +#define ZL_REG_DPLL_MODE_REFSEL(_idx) \ + ZL_REG_IDX(_idx, 5, 0x04, 1, ZL3073X_MAX_CHANNELS, 4) +#define ZL_DPLL_MODE_REFSEL_MODE GENMASK(2, 0) +#define ZL_DPLL_MODE_REFSEL_MODE_FREERUN 0 +#define ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER 1 +#define ZL_DPLL_MODE_REFSEL_MODE_REFLOCK 2 +#define ZL_DPLL_MODE_REFSEL_MODE_AUTO 3 +#define ZL_DPLL_MODE_REFSEL_MODE_NCO 4 +#define ZL_DPLL_MODE_REFSEL_REF GENMASK(7, 4) + /*********************************** * Register Page 9, Synth and Output ***********************************/ @@ -81,6 +115,11 @@ #define ZL_SYNTH_CTRL_EN BIT(0) #define ZL_SYNTH_CTRL_DPLL_SEL GENMASK(6, 4) =20 +#define ZL_REG_SYNTH_PHASE_SHIFT_CTRL ZL_REG(9, 0x1e, 1) +#define ZL_REG_SYNTH_PHASE_SHIFT_MASK ZL_REG(9, 0x1f, 1) +#define ZL_REG_SYNTH_PHASE_SHIFT_INTVL ZL_REG(9, 0x20, 1) +#define ZL_REG_SYNTH_PHASE_SHIFT_DATA ZL_REG(9, 0x21, 2) + #define ZL_REG_OUTPUT_CTRL(_idx) \ ZL_REG_IDX(_idx, 9, 0x28, 1, ZL3073X_NUM_OUTS, 1) #define ZL_OUTPUT_CTRL_EN BIT(0) @@ -100,6 +139,23 @@ #define ZL_REF_CONFIG_ENABLE BIT(0) #define ZL_REF_CONFIG_DIFF_EN BIT(2) =20 +/******************************** + * Register Page 12, DPLL Mailbox + ********************************/ + +#define ZL_REG_DPLL_MB_MASK ZL_REG(12, 0x02, 2) + +#define ZL_REG_DPLL_MB_SEM ZL_REG(12, 0x04, 1) +#define ZL_DPLL_MB_SEM_WR BIT(0) +#define ZL_DPLL_MB_SEM_RD BIT(1) + +#define ZL_REG_DPLL_REF_PRIO(_idx) \ + ZL_REG_IDX(_idx, 12, 0x52, 1, ZL3073X_NUM_REFS / 2, 1) +#define ZL_DPLL_REF_PRIO_REF_P GENMASK(3, 0) +#define ZL_DPLL_REF_PRIO_REF_N GENMASK(7, 4) +#define ZL_DPLL_REF_PRIO_MAX 14 +#define ZL_DPLL_REF_PRIO_NONE 15 + /********************************* * Register Page 13, Synth Mailbox *********************************/ --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 98EBD27454 for ; Mon, 16 Jun 2025 20:15:34 +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=1750104936; cv=none; b=uaeeFXQLQvditsNfoWq/OU1bdRgqbSXhlM2g/EfmBXWV49hxZYYZBrxybLyRfH0BKoeK9OcC5qbuEiPStxlQcDDEo2fagvN6AZgHJQ0vS6MNnQjH3r9WvO/r+kJN3eUiej5F0WnkGKKkzSlUQraLck/YdHd0eiEu+9x8scLqRUI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104936; c=relaxed/simple; bh=d1q/HA9Pr5ZKcepknO7dXUZ4YXuc5K07/Z4NO8cX60k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=jYOxAPHyUJANct55T6B+hPWDBcNA0n2TEKH3ecXDmVZ0AuvxZ8/VY4wcXnQJVDZfCKZLvfbEbavyqCKAIrstNNpvnZC7wn/DHaSl4+TYGmw/1DnXG7nk9BKcPcxPQe2u0lp63i4OAUOci7dyDjWD5LyohynAqPNtbQuiS/kKnxs= 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=cJn81z94; 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="cJn81z94" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104933; 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=PC33ycQpyMMM+DSnzjKCYZzW6We8j70qodfbGCwwodk=; b=cJn81z9449ofYg7BlxUUjuPRjLXSZ+GvOee/wbBVFPcTMEvQvtEgIJ69CCbJciaNsMXIQb U9vmFNDQqVy4MLR1KC6SWXuFjDl4iRJ1Y2CKHUoFaiYdGqB/JsZCFr+lmc7u3roPCFXk2p hqMgLvd8w2bOD+l/twmEb33lhSi50GQ= 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-594-tnvad7ByNSiYLHM2zzS99g-1; Mon, 16 Jun 2025 16:15:29 -0400 X-MC-Unique: tnvad7ByNSiYLHM2zzS99g-1 X-Mimecast-MFC-AGG-ID: tnvad7ByNSiYLHM2zzS99g_1750104926 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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 B0CEA1800296; Mon, 16 Jun 2025 20:15:26 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 52F9B30001B1; Mon, 16 Jun 2025 20:15:20 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Prathosh Satish , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 10/14] dpll: zl3073x: Implement input pin selection in manual mode Date: Mon, 16 Jun 2025 22:14:00 +0200 Message-ID: <20250616201404.1412341-11-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Implement input pin state setting if the DPLL is running in manual mode. The driver indicates manual mode if the DPLL mode is one of ref-lock, forced-holdover, freerun. Use these modes to implement input pin state change between connected and disconnected states. When the user set the particular pin as connected the driver marks this input pin as forced reference and switches the DPLL mode to ref-lock. When the use set the pin as disconnected the driver switches the DPLL to freerun or forced holdover mode. The switch to holdover mode is done if the DPLL has holdover capability (e.g is currently locked with holdover acquired). Signed-off-by: Ivan Vecera --- drivers/dpll/zl3073x/dpll.c | 118 ++++++++++++++++++++++++++++++++++++ drivers/dpll/zl3073x/prop.c | 9 ++- 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 56ca5c8dd9155..89da2c34609eb 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -132,6 +132,81 @@ zl3073x_dpll_selected_ref_get(struct zl3073x_dpll *zld= pll, u8 *ref) return 0; } =20 +/** + * zl3073x_dpll_selected_ref_set - select reference in manual mode + * @zldpll: pointer to zl3073x_dpll + * @ref: input reference to be selected + * + * Selects the given reference for the DPLL channel it should be + * locked to. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_selected_ref_set(struct zl3073x_dpll *zldpll, u8 ref) +{ + struct zl3073x_dev *zldev =3D zldpll->dev; + u8 mode, mode_refsel; + int rc; + + mode =3D zldpll->refsel_mode; + + switch (mode) { + case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK: + /* Manual mode with ref selected */ + if (ref =3D=3D ZL3073X_DPLL_REF_NONE) { + switch (zldpll->lock_status) { + case DPLL_LOCK_STATUS_LOCKED_HO_ACQ: + case DPLL_LOCK_STATUS_HOLDOVER: + /* Switch to forced holdover */ + mode =3D ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER; + break; + default: + /* Switch to freerun */ + mode =3D ZL_DPLL_MODE_REFSEL_MODE_FREERUN; + break; + } + /* Keep selected reference */ + ref =3D zldpll->forced_ref; + } else if (ref =3D=3D zldpll->forced_ref) { + /* No register update - same mode and same ref */ + return 0; + } + break; + case ZL_DPLL_MODE_REFSEL_MODE_FREERUN: + case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER: + /* Manual mode without no ref */ + if (ref =3D=3D ZL3073X_DPLL_REF_NONE) + /* No register update - keep current mode */ + return 0; + + /* Switch to reflock mode and update ref selection */ + mode =3D ZL_DPLL_MODE_REFSEL_MODE_REFLOCK; + break; + default: + /* For other modes like automatic or NCO ref cannot be selected + * manually + */ + return -EOPNOTSUPP; + } + + /* Build mode_refsel value */ + mode_refsel =3D FIELD_PREP(ZL_DPLL_MODE_REFSEL_MODE, mode) | + FIELD_PREP(ZL_DPLL_MODE_REFSEL_REF, ref); + + /* Update dpll_mode_refsel register */ + rc =3D zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(zldpll->id), + mode_refsel); + if (rc) + return rc; + + /* Store new mode and forced reference */ + zldpll->refsel_mode =3D mode; + zldpll->forced_ref =3D ref; + + return rc; +} + /** * zl3073x_dpll_connected_ref_get - get currently connected reference * @zldpll: pointer to zl3073x_dpll @@ -283,6 +358,48 @@ zl3073x_dpll_input_pin_state_on_dpll_get(const struct = dpll_pin *dpll_pin, return zl3073x_dpll_ref_state_get(pin, state); } =20 +static int +zl3073x_dpll_input_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_dpll_pin *pin =3D pin_priv; + u8 new_ref; + int rc; + + switch (zldpll->refsel_mode) { + case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK: + case ZL_DPLL_MODE_REFSEL_MODE_FREERUN: + case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER: + if (state =3D=3D DPLL_PIN_STATE_CONNECTED) { + /* Choose the pin as new selected reference */ + new_ref =3D zl3073x_input_pin_ref_get(pin->id); + } else if (state =3D=3D DPLL_PIN_STATE_DISCONNECTED) { + /* No reference */ + new_ref =3D ZL3073X_DPLL_REF_NONE; + } else { + NL_SET_ERR_MSG_MOD(extack, + "Invalid pin state for manual mode"); + return -EINVAL; + } + + rc =3D zl3073x_dpll_selected_ref_set(zldpll, new_ref); + break; + default: + /* In other modes we cannot change input reference */ + NL_SET_ERR_MSG(extack, + "Pin state cannot be changed in current mode"); + rc =3D -EOPNOTSUPP; + break; + } + + return rc; +} + static int zl3073x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin, void *pin_priv, @@ -358,6 +475,7 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll, v= oid *dpll_priv, static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops =3D { .direction_get =3D zl3073x_dpll_pin_direction_get, .state_on_dpll_get =3D zl3073x_dpll_input_pin_state_on_dpll_get, + .state_on_dpll_set =3D zl3073x_dpll_input_pin_state_on_dpll_set, }; =20 static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops =3D { diff --git a/drivers/dpll/zl3073x/prop.c b/drivers/dpll/zl3073x/prop.c index bc8b78cfb5ae0..c3224e78cbf01 100644 --- a/drivers/dpll/zl3073x/prop.c +++ b/drivers/dpll/zl3073x/prop.c @@ -201,11 +201,14 @@ struct zl3073x_pin_props *zl3073x_pin_props_get(struc= t zl3073x_dev *zldev, if (!props) return ERR_PTR(-ENOMEM); =20 - /* Set default pin type */ - if (dir =3D=3D DPLL_PIN_DIRECTION_INPUT) + /* Set default pin type and capabilities */ + if (dir =3D=3D DPLL_PIN_DIRECTION_INPUT) { props->dpll_props.type =3D DPLL_PIN_TYPE_EXT; - else + props->dpll_props.capabilities =3D + DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE; + } else { props->dpll_props.type =3D DPLL_PIN_TYPE_GNSS; + } =20 props->dpll_props.phase_range.min =3D S32_MIN; props->dpll_props.phase_range.max =3D S32_MAX; --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 9B12328BA8C for ; Mon, 16 Jun 2025 20:15:41 +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=1750104944; cv=none; b=EFGe5U8newDimpo4dCfBr961Xoh/gpNDlCbRWOEpUX8K9R4WYKgQKm2y/x1NFfy5SZ77IhanHBlyp/+Sj6ULrMMlnm53CwG+tHDwxHQjWDyJN5EeZSKGhjt8ZkE+N+7Ln/sBXXPsVANGjDCQNKwWejTLuVlh33gb6r2WYDa3rp4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104944; c=relaxed/simple; bh=gvFlxcFtzyBUDDOMREWUtXBfCFunJy5FTlfD6HzbhQM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=iXVGXto3ptWydBitWYfVq6DKQQhlSmtJDw8Y9vCB8yFV/qyO/+KhWlWePcNfxAYa47Htl37+/OeK+BKuq05NZ8JrIiAKs4FvFFkR5fZxeAMDm3IdZDIJG+vaj6oW1zhcNwpsvklGo1yEfqW4CzfOIiO11iTCWy6R4J5v9TOHQ3U= 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=G/GHcSYx; 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="G/GHcSYx" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104940; 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=QKv0PnDhV0wNFfVbM7vA/OmMJ7dFD+u1OrtyTP7l5fA=; b=G/GHcSYxykvzo/sargZ4bRC7FvhBC6dMcSDbovJtE3rn0zCJgSPBpno+6Od0KU0YVozKiH d1lEKsRaa2eeiki3m1B0jB/0+OKhPGGpCO8CSzXi0KDwmpXgdBVyw5DF+dxDWep9Xj0odQ OZz+mBTT0QEkCdSf5RKBVEAIVO/22v0= Received: from mx-prod-mc-08.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-175-wBckmOCWNPStQqomgr1swA-1; Mon, 16 Jun 2025 16:15:36 -0400 X-MC-Unique: wBckmOCWNPStQqomgr1swA-1 X-Mimecast-MFC-AGG-ID: wBckmOCWNPStQqomgr1swA_1750104933 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8DB2418089B7; Mon, 16 Jun 2025 20:15:33 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 305C930001B1; Mon, 16 Jun 2025 20:15:26 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Prathosh Satish , Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 11/14] dpll: zl3073x: Add support to get/set priority on input pins Date: Mon, 16 Jun 2025 22:14:01 +0200 Message-ID: <20250616201404.1412341-12-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Add support for getting and setting input pin priority. Implement required callbacks and set appropriate capability for input pins. Although the pin priority make sense only if the DPLL is running in automatic mode we have to expose this capability unconditionally because input pins (references) are shared between all DPLLs where one of them can run in automatic mode while the other one not. Co-developed-by: Prathosh Satish Signed-off-by: Prathosh Satish Signed-off-by: Ivan Vecera --- drivers/dpll/zl3073x/dpll.c | 88 +++++++++++++++++++++++++++++++++++++ drivers/dpll/zl3073x/prop.c | 1 + 2 files changed, 89 insertions(+) diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index 89da2c34609eb..e67ef37479910 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -287,6 +287,56 @@ zl3073x_dpll_ref_prio_get(struct zl3073x_dpll_pin *pin= , u8 *prio) return rc; } =20 +/** + * zl3073x_dpll_ref_prio_set - set priority for given input pin + * @pin: pointer to pin + * @prio: place to store priority + * + * Sets priority for the given input pin. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_ref_prio_set(struct zl3073x_dpll_pin *pin, u8 prio) +{ + struct zl3073x_dpll *zldpll =3D pin->dpll; + struct zl3073x_dev *zldev =3D zldpll->dev; + u8 ref, ref_prio; + int rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Read DPLL configuration into mailbox */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD, + ZL_REG_DPLL_MB_MASK, BIT(zldpll->id)); + if (rc) + return rc; + + /* Read reference priority - one value shared between P&N pins */ + ref =3D zl3073x_input_pin_ref_get(pin->id); + rc =3D zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2), &ref_prio); + if (rc) + return rc; + + /* Update nibble according pin type */ + if (zl3073x_dpll_is_p_pin(pin)) { + ref_prio &=3D ~ZL_DPLL_REF_PRIO_REF_P; + ref_prio |=3D FIELD_PREP(ZL_DPLL_REF_PRIO_REF_P, prio); + } else { + ref_prio &=3D ~ZL_DPLL_REF_PRIO_REF_N; + ref_prio |=3D FIELD_PREP(ZL_DPLL_REF_PRIO_REF_N, prio); + } + + /* Update reference priority */ + rc =3D zl3073x_write_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2), ref_prio); + if (rc) + return rc; + + /* Commit configuration */ + return zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR, + ZL_REG_DPLL_MB_MASK, BIT(zldpll->id)); +} + /** * zl3073x_dpll_ref_state_get - get status for given input pin * @pin: pointer to pin @@ -400,6 +450,42 @@ zl3073x_dpll_input_pin_state_on_dpll_set(const struct = dpll_pin *dpll_pin, return rc; } =20 +static int +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_pin *pin =3D pin_priv; + + *prio =3D pin->prio; + + return 0; +} + +static int +zl3073x_dpll_input_pin_prio_set(const struct dpll_pin *dpll_pin, void *pin= _priv, + const struct dpll_device *dpll, void *dpll_priv, + u32 prio, struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll_pin *pin =3D pin_priv; + int rc; + + if (prio > ZL_DPLL_REF_PRIO_MAX) + return -EINVAL; + + /* If the pin is selectable then update HW registers */ + if (pin->selectable) { + rc =3D zl3073x_dpll_ref_prio_set(pin, prio); + if (rc) + return rc; + } + + /* Save priority */ + pin->prio =3D prio; + + return 0; +} + static int zl3073x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin, void *pin_priv, @@ -474,6 +560,8 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll, v= oid *dpll_priv, =20 static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops =3D { .direction_get =3D zl3073x_dpll_pin_direction_get, + .prio_get =3D zl3073x_dpll_input_pin_prio_get, + .prio_set =3D zl3073x_dpll_input_pin_prio_set, .state_on_dpll_get =3D zl3073x_dpll_input_pin_state_on_dpll_get, .state_on_dpll_set =3D zl3073x_dpll_input_pin_state_on_dpll_set, }; diff --git a/drivers/dpll/zl3073x/prop.c b/drivers/dpll/zl3073x/prop.c index c3224e78cbf01..4cf7e8aefcb37 100644 --- a/drivers/dpll/zl3073x/prop.c +++ b/drivers/dpll/zl3073x/prop.c @@ -205,6 +205,7 @@ struct zl3073x_pin_props *zl3073x_pin_props_get(struct = zl3073x_dev *zldev, if (dir =3D=3D DPLL_PIN_DIRECTION_INPUT) { props->dpll_props.type =3D DPLL_PIN_TYPE_EXT; props->dpll_props.capabilities =3D + DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE | DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE; } else { props->dpll_props.type =3D DPLL_PIN_TYPE_GNSS; --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 6CC2B28F955 for ; Mon, 16 Jun 2025 20:15:48 +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=1750104949; cv=none; b=IOJ1ovKB2TexpF60idhNNhetJ+HFpyvQnewbVGwuTVgu1r4dXVVfYYdQUohd8eiEIYv8OnBTEKQ0zC8W7u94wfkXnrz8j7KYmOR7Axiu1FOXPjAtI/Otjm/gtqeXJ+t8K3BCh6rDkMAtyBIH2mVdjzTkv/GL1vHJpPom9cdQirI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104949; c=relaxed/simple; bh=DmT4Bise/vCChRzplVSqDd2BI/p2bI1iSO9roexnDw0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cTLK9z14IVNKo9qz35H4n0kijU/LXXZMgM4neF87BEACaPRPJpGarhLTRhTkB2DYZEE+S/n2i1b0uaOLZzVjPO4CJ4clSv8mRMtg7VrejOphXJXRZGgtDPSHPxBQUQDDm4Pzfnd/UIs1ik8GL3H0Br2cn1TxUsSbQVYmnKjE+HY= 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=EVvOgOYa; 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="EVvOgOYa" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104947; 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=4XK1DvpuFfEECjOWlQarzmEVhfydKQ9Mv1sIMUipbGI=; b=EVvOgOYaE1fEpaq9hAJb5zuDkN7AaP8Rxd+vHzQi3PVJ3QeVdX8/SqX6uZaUZnz2Utgm6u ntGIgkG1KvnGxXb8wDGET6QqGdPHNZzOaoNCunNL6GJhlEV4ttAqRoa7F1jtoCufNVzz2D cZTaGH+ZAfKBE8HKYyG51MGNFH5HpH4= Received: from mx-prod-mc-08.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-344-zrXkJmGlMBSrTAX00TRVpA-1; Mon, 16 Jun 2025 16:15:43 -0400 X-MC-Unique: zrXkJmGlMBSrTAX00TRVpA-1 X-Mimecast-MFC-AGG-ID: zrXkJmGlMBSrTAX00TRVpA_1750104940 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 840F118089B6; Mon, 16 Jun 2025 20:15:40 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 0CF2130001B1; Mon, 16 Jun 2025 20:15:33 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Prathosh Satish , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 12/14] dpll: zl3073x: Implement input pin state setting in automatic mode Date: Mon, 16 Jun 2025 22:14:02 +0200 Message-ID: <20250616201404.1412341-13-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Implement input pin state setting when the DPLL is running in automatic mode. Unlike manual mode, the DPLL mode switching is not used here and the implementation uses special priority value (15) to make the given pin non-selectable. When the user sets state of the pin as disconnected the driver internally sets its priority in HW to 15 that prevents the DPLL to choose this input pin. Conversely, if the pin status is set to selectable, the driver sets the pin priority in HW to the original saved value. Signed-off-by: Ivan Vecera --- drivers/dpll/zl3073x/dpll.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index e67ef37479910..f78a5b209fce7 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -439,6 +439,38 @@ zl3073x_dpll_input_pin_state_on_dpll_set(const struct = dpll_pin *dpll_pin, =20 rc =3D zl3073x_dpll_selected_ref_set(zldpll, new_ref); break; + + case ZL_DPLL_MODE_REFSEL_MODE_AUTO: + if (state =3D=3D DPLL_PIN_STATE_SELECTABLE) { + if (pin->selectable) + return 0; /* Pin is already selectable */ + + /* Restore pin priority in HW */ + rc =3D zl3073x_dpll_ref_prio_set(pin, pin->prio); + if (rc) + return rc; + + /* Mark pin as selectable */ + pin->selectable =3D true; + } else if (state =3D=3D DPLL_PIN_STATE_DISCONNECTED) { + if (!pin->selectable) + return 0; /* Pin is already disconnected */ + + /* Set pin priority to none in HW */ + rc =3D zl3073x_dpll_ref_prio_set(pin, + ZL_DPLL_REF_PRIO_NONE); + if (rc) + return rc; + + /* Mark pin as non-selectable */ + pin->selectable =3D false; + } else { + NL_SET_ERR_MSG(extack, + "Invalid pin state for automatic mode"); + return -EINVAL; + } + break; + default: /* In other modes we cannot change input reference */ NL_SET_ERR_MSG(extack, --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 C1E4E28FFDF for ; Mon, 16 Jun 2025 20:15:53 +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=1750104955; cv=none; b=I9n85WZOkiP0ystLe89Em7LfHPw57bWJAjcv5cFG3P/ziashTBak9ICRja1ufgD8sry7pjIkETqT8otvWkbBkfjLuXVC1WA7Fa+Am5Sodh4jkjLrC0Xz/JV87Qg+lcUIT2JdndNTJETFqXhF2yNODT3olmadROlpZWE5IqqBsEg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104955; c=relaxed/simple; bh=Db4IofzkZqbGTQ09xe21VxSlGN+utnyyzSSveC5gSvE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DAXFoEj8KyTPmptZDC/iBo+o8HQMUWYLieVD+LGL84wtVNLc1npo7+jJGFse/lFyPwQlHE9w73o+jcnoJcWEeX5u7puEY21gd5Ji3vQD+PvGN9hQdwHDkWAiZbprjyh6I5uwZXbkp7Qr/DIvC+1RXQa27eJ+Ql55XUUVV+Gaeis= 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=QK+LhyR7; 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="QK+LhyR7" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104953; 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=z3eVfQgz8jHuO3fK/JB6oG8TpR0kCM5lP7nBadwe16A=; b=QK+LhyR7xKyHQ9VfhEx+qv8ulT6387yvdGi3ToAt/Bl2sjxa/+/K6kMT7yOpF5PT2JAsFK 8ZXcEzL6n/BE0uLMhS7gVqS/Pz76uqh2YrTdgT6pqN+fExrrfZON3VGdzhIQb62bZtNfe6 16XZbgdWnuKqeosVsm3OzrSUweHgMWg= Received: from mx-prod-mc-04.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-331-CN2VI_F4NlOjssTUqH6ULA-1; Mon, 16 Jun 2025 16:15:49 -0400 X-MC-Unique: CN2VI_F4NlOjssTUqH6ULA-1 X-Mimecast-MFC-AGG-ID: CN2VI_F4NlOjssTUqH6ULA_1750104947 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 65CF619560B3; Mon, 16 Jun 2025 20:15:47 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D9C7E30001B1; Mon, 16 Jun 2025 20:15:40 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Prathosh Satish , Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 13/14] dpll: zl3073x: Add support to get/set frequency on input pins Date: Mon, 16 Jun 2025 22:14:03 +0200 Message-ID: <20250616201404.1412341-14-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Add support to get/set frequency on input pins. The frequency for input pins (references) is computed in the device according this formula: freq =3D base_freq * multiplier * (nominator / denominator) where the base_freq comes from the list of supported base frequencies and other parameters are arbitrary numbers. All these parameters are 16-bit unsigned integers. Co-developed-by: Prathosh Satish Signed-off-by: Prathosh Satish Signed-off-by: Ivan Vecera --- drivers/dpll/zl3073x/dpll.c | 124 ++++++++++++++++++++++++++++++++++++ drivers/dpll/zl3073x/regs.h | 5 ++ 2 files changed, 129 insertions(+) diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index f78a5b209fce7..a110109a738c3 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -84,6 +85,127 @@ zl3073x_dpll_pin_direction_get(const struct dpll_pin *d= pll_pin, void *pin_priv, return 0; } =20 +/** + * zl3073x_dpll_input_ref_frequency_get - get input reference frequency + * @zldpll: pointer to zl3073x_dpll + * @ref_id: reference id + * @frequency: pointer to variable to store frequency + * + * Reads frequency of given input reference. + * + * Return: 0 on success, <0 on error + */ +static int +zl3073x_dpll_input_ref_frequency_get(struct zl3073x_dpll *zldpll, u8 ref_i= d, + u32 *frequency) +{ + struct zl3073x_dev *zldev =3D zldpll->dev; + u16 base, mult, num, denom; + int rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Read reference configuration */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(ref_id)); + if (rc) + return rc; + + /* Read registers to compute resulting frequency */ + rc =3D zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_BASE, &base); + if (rc) + return rc; + rc =3D zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_MULT, &mult); + if (rc) + return rc; + rc =3D zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_M, &num); + if (rc) + return rc; + rc =3D zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_N, &denom); + if (rc) + return rc; + + /* Sanity check that HW has not returned zero denominator */ + if (!denom) { + dev_err(zldev->dev, + "Zero divisor for ref %u frequency got from device\n", + ref_id); + return -EINVAL; + } + + /* Compute the frequency */ + *frequency =3D base * mult * num / denom; + + return rc; +} + +static int +zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, u64 *frequency, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dpll_pin *pin =3D pin_priv; + u32 ref_freq; + u8 ref; + int rc; + + /* Read and return ref frequency */ + ref =3D zl3073x_input_pin_ref_get(pin->id); + rc =3D zl3073x_dpll_input_ref_frequency_get(zldpll, ref, &ref_freq); + if (!rc) + *frequency =3D ref_freq; + + return rc; +} + +static int +zl3073x_dpll_input_pin_frequency_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, u64 frequency, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dev *zldev =3D zldpll->dev; + struct zl3073x_dpll_pin *pin =3D pin_priv; + u16 base, mult; + u8 ref; + int rc; + + /* Get base frequency and multiplier for the requested frequency */ + rc =3D zl3073x_ref_freq_factorize(frequency, &base, &mult); + if (rc) + return rc; + + guard(mutex)(&zldev->multiop_lock); + + /* Load reference configuration */ + ref =3D zl3073x_input_pin_ref_get(pin->id); + rc =3D zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD, + ZL_REG_REF_MB_MASK, BIT(ref)); + + /* Update base frequency, multiplier, numerator & denominator */ + rc =3D zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_BASE, base); + if (rc) + return rc; + rc =3D zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_MULT, mult); + if (rc) + return rc; + rc =3D zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_M, 1); + if (rc) + return rc; + rc =3D zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_N, 1); + if (rc) + return rc; + + /* Commit reference configuration */ + return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR, + ZL_REG_REF_MB_MASK, BIT(ref)); +} + /** * zl3073x_dpll_selected_ref_get - get currently selected reference * @zldpll: pointer to zl3073x_dpll @@ -592,6 +714,8 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll, v= oid *dpll_priv, =20 static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops =3D { .direction_get =3D zl3073x_dpll_pin_direction_get, + .frequency_get =3D zl3073x_dpll_input_pin_frequency_get, + .frequency_set =3D zl3073x_dpll_input_pin_frequency_set, .prio_get =3D zl3073x_dpll_input_pin_prio_get, .prio_set =3D zl3073x_dpll_input_pin_prio_set, .state_on_dpll_get =3D zl3073x_dpll_input_pin_state_on_dpll_get, diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index 34e905053a1ef..09dd314663dff 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -135,6 +135,11 @@ #define ZL_REF_MB_SEM_WR BIT(0) #define ZL_REF_MB_SEM_RD BIT(1) =20 +#define ZL_REG_REF_FREQ_BASE ZL_REG(10, 0x05, 2) +#define ZL_REG_REF_FREQ_MULT ZL_REG(10, 0x07, 2) +#define ZL_REG_REF_RATIO_M ZL_REG(10, 0x09, 2) +#define ZL_REG_REF_RATIO_N ZL_REG(10, 0x0b, 2) + #define ZL_REG_REF_CONFIG ZL_REG(10, 0x0d, 1) #define ZL_REF_CONFIG_ENABLE BIT(0) #define ZL_REF_CONFIG_DIFF_EN BIT(2) --=20 2.49.0 From nobody Thu Oct 9 20:22:57 2025 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 0D5DA289836 for ; Mon, 16 Jun 2025 20:15:59 +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=1750104961; cv=none; b=p/9SRVvyBvL6SePTKqPcOaOrIJkBKi5NecUqi72VwFiIqFe11aqilmt5NJrxw8FWcpnwuHEk4Kug1aXz/0dlS751Yf46DfdViNQ0Qqis/qV6kHPAKhMgIpN+g6WygXUSyFUtwlyKwD7zCP5r5YrtatFHJIkcXtUE8qNoJDdp9DM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750104961; c=relaxed/simple; bh=oT2X5a9db3fvX4h+pfQea3+mOSJ3D/971Mhcdm02WP8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YOmS3YZCkVWzSS8LqvI6cXkEUe/S6o9YMxs87JP+/rdPBmSM5unPWjK+mbK6xvL2t4AKFVMj29qcmJYhbEeKN4JCLagdoIVFvfeKVjRciKCvxRUGWO2gdcGg5FUB4Tr1JtCxBRhvwDaxYWEeHeZByi2Kl20d+ItROIxuiY9bCS4= 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=gTlYHs8T; 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="gTlYHs8T" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1750104959; 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=5e7j5Lsxu01XP+JZK7FayPbs2oiwzPSDVUbmSjN7Gns=; b=gTlYHs8TaZpvjfWakMR1y0DpRCJKLOTNj+g+yhgKvx+c1rLoJyT+5L1PuiSW3O4sMOtBzg 1n4Vp2mm5DA6ZhHbMg6+HJCLBoPiwzp+MOENTQfbAr0cLVbIKEgGlg+Yx1BodosUoSNX0l iVSIOU36bMWVBNCyYLlHybKA3VwBUd0= 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-655-VxfjwL0JPCeatWu2opHOYw-1; Mon, 16 Jun 2025 16:15:56 -0400 X-MC-Unique: VxfjwL0JPCeatWu2opHOYw-1 X-Mimecast-MFC-AGG-ID: VxfjwL0JPCeatWu2opHOYw_1750104954 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.4]) (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 3C33E1956095; Mon, 16 Jun 2025 20:15:54 +0000 (UTC) Received: from p16v.luc.cera.cz (unknown [10.45.224.53]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D886F30001B1; Mon, 16 Jun 2025 20:15:47 +0000 (UTC) From: Ivan Vecera To: netdev@vger.kernel.org Cc: Prathosh Satish , Vadim Fedorenko , Arkadiusz Kubalewski , Jiri Pirko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Jason Gunthorpe , Shannon Nelson , Dave Jiang , Jonathan Cameron , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Michal Schmidt , Petr Oros Subject: [PATCH net-next v11 14/14] dpll: zl3073x: Add support to get/set frequency on output pins Date: Mon, 16 Jun 2025 22:14:04 +0200 Message-ID: <20250616201404.1412341-15-ivecera@redhat.com> In-Reply-To: <20250616201404.1412341-1-ivecera@redhat.com> References: <20250616201404.1412341-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.4.1 on 10.30.177.4 Content-Type: text/plain; charset="utf-8" Add support to get/set frequencies on output pins. The frequency for output pin is determined by the frequency of synthesizer the output pin is connected to and divisor of the output to which is the given pin belongs. The resulting frequency of the P-pin and the N-pin from this output pair depends on the signal format of this output pair. The device supports so-called N-divided signal formats where for the N-pin there is an additional divisor. The frequencies for both pins from such output pair are computed: P-pin-freq =3D synth_freq / output_div N-pin-freq =3D synth_freq / output_div / n_div For other signal-format types both P and N pin have the same frequency based only synth frequency and output divisor. Implement output pin callbacks to get and set frequency. The frequency setting for the output non-N-divided signal format is simple as we have to compute just new output divisor. For N-divided formats it is more complex because by changing of output divisor we change frequency for both P and N pins. In this case if we are changing frequency for P-pin we have to compute also new N-divisor for N-pin to keep its current frequency. From this and the above it follows that the frequency of the N-pin cannot be higher than the frequency of the P-pin and the callback must take this limitation into account. Co-developed-by: Prathosh Satish Signed-off-by: Prathosh Satish Signed-off-by: Ivan Vecera --- drivers/dpll/zl3073x/dpll.c | 234 ++++++++++++++++++++++++++++++++++++ drivers/dpll/zl3073x/regs.h | 5 + 2 files changed, 239 insertions(+) diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c index a110109a738c3..bd4b4af2223d3 100644 --- a/drivers/dpll/zl3073x/dpll.c +++ b/drivers/dpll/zl3073x/dpll.c @@ -640,6 +640,238 @@ zl3073x_dpll_input_pin_prio_set(const struct dpll_pin= *dpll_pin, void *pin_priv, return 0; } =20 +static int +zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, u64 *frequency, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dev *zldev =3D zldpll->dev; + struct zl3073x_dpll_pin *pin =3D pin_priv; + struct device *dev =3D zldev->dev; + u8 out, signal_format, synth; + u32 output_div, synth_freq; + int rc; + + out =3D zl3073x_output_pin_out_get(pin->id); + synth =3D zl3073x_out_synth_get(zldev, out); + synth_freq =3D zl3073x_synth_freq_get(zldev, synth); + + guard(mutex)(&zldev->multiop_lock); + + /* Read output configuration into mailbox */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + if (rc) + return rc; + + rc =3D zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div); + if (rc) + return rc; + + /* Check output divisor for zero */ + if (!output_div) { + dev_err(dev, "Zero divisor for output %u got from device\n", + out); + return -EINVAL; + } + + /* Read used signal format for the given output */ + signal_format =3D zl3073x_out_signal_format_get(zldev, out); + + switch (signal_format) { + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV: + case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV: + /* In case of divided format we have to distiguish between + * given output pin type. + */ + if (zl3073x_dpll_is_p_pin(pin)) { + /* For P-pin the resulting frequency is computed as + * simple division of synth frequency and output + * divisor. + */ + *frequency =3D synth_freq / output_div; + } else { + /* For N-pin we have to divide additionally by + * divisor stored in esync_period output mailbox + * register that is used as N-pin divisor for these + * modes. + */ + u32 ndiv; + + rc =3D zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, + &ndiv); + if (rc) + return rc; + + /* Check N-pin divisor for zero */ + if (!ndiv) { + dev_err(dev, + "Zero N-pin divisor for output %u got from device\n", + out); + return -EINVAL; + } + + /* Compute final divisor for N-pin */ + *frequency =3D synth_freq / output_div / ndiv; + } + break; + default: + /* In other modes the resulting frequency is computed as + * division of synth frequency and output divisor. + */ + *frequency =3D synth_freq / output_div; + break; + } + + return rc; +} + +static int +zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin, + void *pin_priv, + const struct dpll_device *dpll, + void *dpll_priv, u64 frequency, + struct netlink_ext_ack *extack) +{ + struct zl3073x_dpll *zldpll =3D dpll_priv; + struct zl3073x_dev *zldev =3D zldpll->dev; + struct zl3073x_dpll_pin *pin =3D pin_priv; + struct device *dev =3D zldev->dev; + u32 output_n_freq, output_p_freq; + u8 out, signal_format, synth; + u32 cur_div, new_div, ndiv; + u32 synth_freq; + int rc; + + out =3D zl3073x_output_pin_out_get(pin->id); + synth =3D zl3073x_out_synth_get(zldev, out); + synth_freq =3D zl3073x_synth_freq_get(zldev, synth); + + /* Check the requested frequency divides synth frequency without + * remainder. + */ + if (synth_freq % (u32)frequency) { + dev_err(dev, "The requested frequency must divide %u Hz\n", + synth_freq); + return -EINVAL; + } + new_div =3D synth_freq / (u32)frequency; + + /* Get used signal format for the given output */ + signal_format =3D zl3073x_out_signal_format_get(zldev, out); + + guard(mutex)(&zldev->multiop_lock); + + /* Load output configuration */ + rc =3D zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + if (rc) + return rc; + + /* Check signal format */ + if (signal_format !=3D ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV && + signal_format !=3D ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV) { + /* For non N-divided signal formats the frequency is computed + * as division of synth frequency and output divisor. + */ + rc =3D zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, new_div); + if (rc) + return rc; + + /* For 50/50 duty cycle the divisor is equal to width */ + rc =3D zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, new_div); + if (rc) + return rc; + + /* Commit output configuration */ + return zl3073x_mb_op(zldev, + ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); + } + + /* For N-divided signal format get current divisor */ + rc =3D zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &cur_div); + if (rc) + return rc; + + /* Check output divisor for zero */ + if (!cur_div) { + dev_err(dev, "Zero divisor for output %u got from device\n", + out); + return -EINVAL; + } + + /* Get N-pin divisor (shares the same register with esync */ + rc =3D zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, &ndiv); + if (rc) + return rc; + + /* Check N-pin divisor for zero */ + if (!ndiv) { + dev_err(dev, + "Zero N-pin divisor for output %u got from device\n", + out); + return -EINVAL; + } + + /* Compute current output frequency for P-pin */ + output_p_freq =3D synth_freq / cur_div; + + /* Compute current N-pin frequency */ + output_n_freq =3D output_p_freq / ndiv; + + if (zl3073x_dpll_is_p_pin(pin)) { + /* We are going to change output frequency for P-pin but + * if the requested frequency is less than current N-pin + * frequency then indicate a failure as we are not able + * to compute N-pin divisor to keep its frequency unchanged. + */ + if (frequency <=3D output_n_freq) + return -EINVAL; + + /* Update the output divisor */ + rc =3D zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, new_div); + if (rc) + return rc; + + /* For 50/50 duty cycle the divisor is equal to width */ + rc =3D zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, new_div); + if (rc) + return rc; + + /* Compute new divisor for N-pin */ + ndiv =3D (u32)frequency / output_n_freq; + } else { + /* We are going to change frequency of N-pin but if + * the requested freq is greater or equal than freq of P-pin + * in the output pair we cannot compute divisor for the N-pin. + * In this case indicate a failure. + */ + if (output_p_freq <=3D frequency) + return -EINVAL; + + /* Compute new divisor for N-pin */ + ndiv =3D output_p_freq / (u32)frequency; + } + + /* Update divisor for the N-pin */ + rc =3D zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, ndiv); + if (rc) + return rc; + + /* For 50/50 duty cycle the divisor is equal to width */ + rc =3D zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, ndiv); + if (rc) + return rc; + + /* Commit output configuration */ + return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR, + ZL_REG_OUTPUT_MB_MASK, BIT(out)); +} + static int zl3073x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin, void *pin_priv, @@ -724,6 +956,8 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin= _ops =3D { =20 static const struct dpll_pin_ops zl3073x_dpll_output_pin_ops =3D { .direction_get =3D zl3073x_dpll_pin_direction_get, + .frequency_get =3D zl3073x_dpll_output_pin_frequency_get, + .frequency_set =3D zl3073x_dpll_output_pin_frequency_set, .state_on_dpll_get =3D zl3073x_dpll_output_pin_state_on_dpll_get, }; =20 diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h index 09dd314663dff..9c62906de095d 100644 --- a/drivers/dpll/zl3073x/regs.h +++ b/drivers/dpll/zl3073x/regs.h @@ -198,4 +198,9 @@ #define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV 12 #define ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV 15 =20 +#define ZL_REG_OUTPUT_DIV ZL_REG(14, 0x0c, 4) +#define ZL_REG_OUTPUT_WIDTH ZL_REG(14, 0x10, 4) +#define ZL_REG_OUTPUT_ESYNC_PERIOD ZL_REG(14, 0x14, 4) +#define ZL_REG_OUTPUT_ESYNC_WIDTH ZL_REG(14, 0x18, 4) + #endif /* _ZL3073X_REGS_H */ --=20 2.49.0