Skip to contentSkip to conversation

Cirrus Logic CS2600 clock device

PATCH Superseded Richard Fitzgerald 6 participants 13 messages Jun 30 – Jul 20

Subsystems (2)

v6 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

v6 cover Cirrus Logic CS2600 clock device

This patch set provides Common Clock Framework support for the Cirrus Logic CS2600 Fractional-N Clock Synthesizer and Multiplier.

KUnit testing is included in the series.

Major changes:

v6 changes:
- Convert DT smart mode selection from an enum property to two booleans.
- Fix DT clock names to match the datasheet names.
- Add DT property to explicitly flag that the internal oscillator should be used instead of inferring it from having a variable definition of clock-names.
- Add DT support for setting BCLK invert, FSYNC invert, and FSYNC duty cycles.
- More DT examples
- Merged and factored-out duplicated code in the driver.
- Fixed rounding errors that caused the calculated frequency to drift when it was passed into clock core and then passed back to the driver.
- Fixed driver bugs where the functions for one clock relied on the register values of other clocks being set already, which is not guaranteed.
- Fix a bug where the (mandatory) REF_CLK_IN wasn't requested and enabled when using CLK_IN as the frequency reference in manual mode.
- Lots of code cleanup in the driver
- Fix driver bugs.
- Added KUnit test.

v5 changes:
- Change dt-binding to dt-bindings in commit message
- Add minItems to clocks
- Remove unneeded comments for the cirrus,aux-output-source enums

v4 changes:
- Change DT discription to add more detial.
- Move reg property to keep consistent with the required list.
- Add enum to string DT inputs and change the input values to lower case.
- Change unevauatedProperties to additionalProperties
- Reove black line.

v3 changes:
- Changed clock-names in device tree to an emum since the both clocks are not always required.
- Changed cirrus,aux-output-source to a string input.
- Added cirrus,clock-mode as a string input.
- Fixed ordering in DT example.
- Removed dt-bindings .h file.
- Add in_range to allow for acceptable ranges for clock rates.

v2 changes:
- Remove | from after "discription" in DT binding.
- Remove discription from "clocks" in DT binding.
- Change ref_clk to xti
- Clarify auxiliary clock pin
- Change DT example to not have "reg" for the bus
- Change DT example to change i2c@0 to i2c
- Remove "OUTPUT" from the clock ouputs in the dt binding include file.
- Remove AUX_OUT defines
- Use do_div when dividing u64 by a u32
- Include the dt bindings include file
- Use the includes from the dt bindings include file aux output.
- Move of_device_id and i2c_device_id tables next to i2c_driver struct.

Paul Handrigan (2):
dt-bindings: clock: cs2600: Add support for the CS2600
clk: cs2600: Add Fractional-N clock driver

Richard Fitzgerald (1):
clk: cs2600: Add KUnit test for CS2600 driver

 .../bindings/clock/cirrus,cs2600.yaml         |  262 ++
 MAINTAINERS                                   |    8 +
 drivers/clk/Kconfig                           |    1 +
 drivers/clk/Makefile                          |    1 +
 drivers/clk/cirrus/Kconfig                    |   23 +
 drivers/clk/cirrus/Makefile                   |   27 +
 drivers/clk/cirrus/clk-cs2600-kunit.c         | 2495 +++++++++++++++++
 drivers/clk/cirrus/clk-cs2600-kunit.h         |   18 +
 drivers/clk/cirrus/clk-cs2600.c               | 1274 +++++++++
 drivers/clk/cirrus/clk-cs2600.h               |  163 ++
 drivers/clk/cirrus/kunit_clk_cs2600.dtsi      |   40 +
 .../clk/cirrus/kunit_clk_cs2600_assigned.dtso |   27 +
 .../kunit_clk_cs2600_aux1_out_clk_in.dtso     |   17 +
 ...it_clk_cs2600_aux1_out_clk_in_missing.dtso |   17 +
 ...kunit_clk_cs2600_aux1_out_freq_unlock.dtso |   17 +
 ...unit_clk_cs2600_aux1_out_phase_unlock.dtso |   17 +
 .../kunit_clk_cs2600_aux1_out_ref_clk_in.dtso |   17 +
 .../cirrus/kunit_clk_cs2600_bclk_invert.dtso  |   17 +
 .../cirrus/kunit_clk_cs2600_fsync_duty_1.dtso |   17 +
 .../cirrus/kunit_clk_cs2600_fsync_duty_2.dtso |   17 +
 .../kunit_clk_cs2600_fsync_duty_32.dtso       |   17 +
 .../cirrus/kunit_clk_cs2600_fsync_invert.dtso |   17 +
 .../kunit_clk_cs2600_manual_intosc_clkin.dtso |   17 +
 .../kunit_clk_cs2600_manual_intosc_only.dtso  |   17 +
 .../kunit_clk_cs2600_manual_refclk_clkin.dtso |   16 +
 .../kunit_clk_cs2600_manual_refclk_only.dtso  |   16 +
 .../kunit_clk_cs2600_name_output_clocks.dtso  |   23 +
 ...it_clk_cs2600_smart_clkin_only_intosc.dtso |   19 +
 ...it_clk_cs2600_smart_clkin_only_refclk.dtso |   18 +
 .../cirrus/kunit_clk_cs2600_smart_intosc.dtso |   18 +
 .../cirrus/kunit_clk_cs2600_smart_refclk.dtso |   17 +
 .../dt-bindings/clock/cirrus,cs2600-clock.h   |   18 +
 32 files changed, 4688 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/cirrus,cs2600.yaml
 create mode 100644 drivers/clk/cirrus/Kconfig
 create mode 100644 drivers/clk/cirrus/Makefile
 create mode 100644 drivers/clk/cirrus/clk-cs2600-kunit.c
 create mode 100644 drivers/clk/cirrus/clk-cs2600-kunit.h
 create mode 100644 drivers/clk/cirrus/clk-cs2600.c
 create mode 100644 drivers/clk/cirrus/clk-cs2600.h
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600.dtsi
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_assigned.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_clk_in.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_clk_in_missing.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_freq_unlock.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_phase_unlock.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_ref_clk_in.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_bclk_invert.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_fsync_duty_1.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_fsync_duty_2.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_fsync_duty_32.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_fsync_invert.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_manual_intosc_clkin.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_manual_intosc_only.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_manual_refclk_clkin.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_manual_refclk_only.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_name_output_clocks.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_smart_clkin_only_intosc.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_smart_clkin_only_refclk.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_smart_intosc.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_smart_refclk.dtso
 create mode 100644 include/dt-bindings/clock/cirrus,cs2600-clock.h
Signature · 1 line
2.47.3
#1 lore raw

PATCH v6 1/3 dt-bindings: clock: cs2600: Add support for the CS2600

From: Paul Handrigan <[email protected]>

Add device tree schema for the Cirrus Logic CS2600 clock generator.

The majority of the schema is typical clock, power and I2C properties.

Passes dt_binding_check:
make dt_binding_check DT_SCHEMA_FILES=clock/cirrus,cs2600.yaml

  SCHEMA  Documentation/devicetree/bindings/processed-schema.json
  CHKDT   ./Documentation/devicetree/bindings
  LINT    ./Documentation/devicetree/bindings
  STYLE   ./Documentation/devicetree/bindings
  DTEX    Documentation/devicetree/bindings/clock/cirrus,cs2600.example.dts

DTC [C] Documentation/devicetree/bindings/clock/cirrus,cs2600.example.dtb

---

Changes in V6:
- Fixed clock naming in descriptions to match datasheet.
- Fixed clock-names property values to match datasheet names.

- Added cirrus,internal-oscillator boolean to flag that the internal oscillator is the clock source. Previously the driver inferred this if clock-names did not contain "ref_clk_in", but this made it difficult to enforce dts correctness because there was no way to tell whether ref_clk_in was intentionally or accidentally missing.

- Changed the cirrus,clock-mode enum property into two booleans, since effectively it was two separate features masquerading as an enum:
- cirrus,smart-mode present to enable smart mode.
- cirrus,smart-mode-clkin-only to enable a feature where the output will be suppressed until both input clocks are present.

- Changes to cirrus,aux-output-source property:
- Renamed to cirrus,aux1-output-source because it's for the AUX1 pin.
- Added more options.
- Renamed the "no_clkin" option to "clkin_missing".
- Reformatted the description as a list instead of one long sentence.

- Changed clock-names from an enum to an ordered list of const. This implicitly ensures ref_clk_in is always required.

- Added properties to invert bclk and fsync outputs.
- Added property cirrus,fsync-duty-cycles.
- Added #clock-cells and vdd-supply to the list of required properies.
- Rewritten description description section.
- Reordered the property list to put common properties before custom cirrus properties.
- Added more examples.
- Added header file to define the clock indexes for DT consumers of the CS2600 clocks.

Note:
V5 was Reviewed-by: Krzysztof Kozlowski <[email protected]> but I haven't carried this forward because the changes in V6 are large.

Changes in V5:
- Change dt-binding to dt-bindings in commit message
- Add minItems to clocks
- Remove unneeded comments for the cirrus,aux-output-source enums

Changes in V4:
- Change DT description to add more detail.
- Move reg property to keep consistant with the required list.
- Add enum to string DT inputs and change the input values to lower case.
- Change unevaluatedProperties to additionalProperties

Changes in V3:
- Changed clock-names in device tree to an enum since the both clocks are not always required.
- Changed cirrus,aux-output-source to a string input.
- Added cirrus,clock-mode as a string input.
- Fixed ordering in DT example.
- Removed dt-bindings .h file.

Changes in V2:
- Remove | from after "description" in DT binding.
- Remove description from "clocks" in DT binding.
- Change ref_clk to xti
- Clarify auxiliary clock pin
- Change DT example to not have "reg" for the bus
- Change DT example to change i2c@0 to i2c
- Remove "OUTPUT" from the clock ouputs in the dt binding include file.

 .../bindings/clock/cirrus,cs2600.yaml         | 262 ++++++++++++++++++
 MAINTAINERS                                   |   7 +
 .../dt-bindings/clock/cirrus,cs2600-clock.h   |  18 ++
 3 files changed, 287 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/cirrus,cs2600.yaml
 create mode 100644 include/dt-bindings/clock/cirrus,cs2600-clock.h
Documentation/devicetree/bindings/clock/cirrus,cs2600.yaml new file +262 -0
new file mode 100644
index 000000000000..5f7da2ba87a0
@@ -0,0 +1,262 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/cirrus,cs2600.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cirrus Logic Fractional-N Clock Synthesizer & Clock Multiplier
+
+maintainers:
+  - [email protected]
+
+description: |
+  The CS2600 is a system-clocking device that enables frequency synthesis
+  and clock generation from a stable timing reference clock. The device can
+  generate low-jitter clocks from a noisy clock reference at frequencies as
+  low as 50 Hz. The CS2600 has two clock inputs: REF_CLK_IN and CLK_IN.
+  REF_CLK_IN can be either a crystal or an external clock source.
+  REF_CLK_IN is always required to clock the PLL. Either REF_CLK_IN or
+  CLK_IN can be used as the reference frequency from which the output
+  frequency is derived.
+
+  The clock tree of the CS2600 is:
+
+                 +------------+
+  REF_CLK_IN ----| Hybrid PLL | PLL_OUT    +--------+
+                 |   + FLL    |-----+------|  GATE  |--------- CLK_OUT
+      CLK_IN ----|            |     |      +--------+
+                 +------------+     |
+                                    |      +---------+
+                                    +------| DIVIDER |-------- BCLK_OUT
+                                    |      +---------+
+                                    |
+                                    |      +---------+
+                                    +------| DIVIDER |-------- FSYNC_OUT
+                                           +---------+
+
+  Clock output names are defined in <dt-bindings/clock/cirrus,cs2600-clock.h>
+
+properties:
+  compatible:
+    enum:
+      - cirrus,cs2600
+
+  reg:
+    maxItems: 1
+
+  '#clock-cells':
+    const: 1
+
+  clocks:
+    minItems: 1
+    maxItems: 2
+
+  clock-names:
+    minItems: 1
+    items:
+      - const: ref_clk_in
+      - const: clk_in
+
+  vdd-supply:
+    description: Power Supply
+
+  clock-output-names:
+    maxItems: 4
+    description:
+      Names for the output clocks corresponding to PLL_OUT, CLK_OUT,
+      BCLK_OUT and FSYNC_OUT.
+
+  cirrus,aux1-output-source:
+    description: |
+      Selects the signal  routed to the AUX1_OUT pin:
+        - "ref_clk_in"    the clock on REF_CLK_IN
+        - "clk_in"        the clock on CLK_IN
+        - "phase_unlock"  indicate the input and output clocks are not in phase,
+        - "freq_unlock"   indicate the PLL is unlocked,
+        - "clkin_missing" indicate no clock present at clk_in pin.
+    $ref: /schemas/types.yaml#/definitions/string
+    enum:
+      - ref_clk_in
+      - clk_in
+      - phase_unlock
+      - freq_unlock
+      - clkin_missing
+    default: ref_clk_in
+
+  cirrus,internal-oscillator:
+    description:
+      Use the internal oscillator as the source of REF_CLK_IN. The clocks
+      and clock-names properties must provide a fixed-rate clock of exactly
+      12000000 Hz as the ref_clk_in clock.
+    type: boolean
+
+  cirrus,smart-mode:
+    description:
+      Use REF_CLK_IN as the frequency reference until CLK_IN is present,
+      with a glitchless switch to CLK_IN as the frequency reference when
+      CLK_IN is present.
+    type: boolean
+
+  cirrus,smart-mode-clkin-only:
+    description:
+      In Smart Mode keep PLL_OUT disabled until there is clock on CLK_IN.
+    type: boolean
+
+  cirrus,bclk-invert:
+    description:
+      Invert the BCLK output.
+    type: boolean
+
+  cirrus,fsync-invert:
+    description:
+      Invert the FSYNC output.
+    type: boolean
+
+  cirrus,fsync-duty-cycles:
+    description:
+      FSYNC output duty. The number of BCLK cycles that FSYNC is asserted.
+      If not present the duty is 50%.
+      This is determined by the type of audio port that is being clocked.
+      For example TDM formats typically define FSYNC is a pulse of 1 BCLK
+      at the start of a frame.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [ 1, 2, 4, 8, 16, 32 ]
+
+required:
+  - compatible
+  - reg
+  - '#clock-cells'
+  - clocks
+  - clock-names
+  - vdd-supply
+
+allOf:
+  - if:
+      required:
+        - cirrus,smart-mode-clkin-only
+    then:
+      required:
+        - cirrus,smart-mode
+
+  # Require clk_in for smart mode
+  - if:
+      required:
+        - cirrus,smart-mode
+    then:
+      properties:
+        clocks:
+          minItems: 2
+        clock-names:
+          minItems: 2
+
+additionalProperties: false
+
+examples:
+  - |
+    /* Smart mode */
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      clock-controller@2c {
+        compatible = "cirrus,cs2600";
+        reg = <0x2c>;
+        #clock-cells = <1>;
+        clocks = <&xtl_clk>, <&sync_clock>;
+        clock-names = "ref_clk_in", "clk_in";
+        vdd-supply = <&vreg>;
+        cirrus,smart-mode;
+      };
+    };
+
+  - |
+    /* Smart mode with PLL_OUT suppressed until CLK_IN is available */
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      clock-controller@2c {
+        compatible = "cirrus,cs2600";
+        reg = <0x2c>;
+        #clock-cells = <1>;
+        clocks = <&xtl_clk>, <&sync_clock>;
+        clock-names = "ref_clk_in", "clk_in";
+        vdd-supply = <&vreg>;
+        cirrus,smart-mode;
+        cirrus,smart-mode-clkin-only;
+      };
+    };
+
+  - |
+    /* Manual mode using only internal oscillator */
+    intosc_clk: intosc-clk {
+      compatible = "fixed-clock";
+      #clock-cells = <0>;
+      clock-frequency = <12000000>;
+    };
+
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      clock-controller@2c {
+        compatible = "cirrus,cs2600";
+        reg = <0x2c>;
+        #clock-cells = <1>;
+        clocks = <&intosc_clk>;
+        clock-names = "ref_clk_in";
+        vdd-supply = <&vreg>;
+        cirrus,internal-oscillator;
+      };
+    };
+
+  - |
+    /* Manual mode using internal oscillator and external clk_in */
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      clock-controller@2c {
+        compatible = "cirrus,cs2600";
+        reg = <0x2c>;
+        #clock-cells = <1>;
+        clocks = <&intosc_clk>, <&sync_clock>;
+        clock-names = "ref_clk_in", "clk_in";
+        vdd-supply = <&vreg>;
+        cirrus,internal-oscillator;
+      };
+    };
+
+  - |
+    /* Invert FSYNC output clock */
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      clock-controller@2c {
+        compatible = "cirrus,cs2600";
+        reg = <0x2c>;
+        #clock-cells = <1>;
+        clocks = <&intosc_clk>, <&sync_clock>;
+        clock-names = "ref_clk_in", "clk_in";
+        vdd-supply = <&vreg>;
+        cirrus,fsync-invert;
+      };
+    };
+
+  - |
+    /* Change FSYNC duty */
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      clock-controller@2c {
+        compatible = "cirrus,cs2600";
+        reg = <0x2c>;
+        #clock-cells = <1>;
+        clocks = <&intosc_clk>, <&sync_clock>;
+        clock-names = "ref_clk_in", "clk_in";
+        vdd-supply = <&vreg>;
+        cirrus,fsync-duty-cycles = <1>;
+      };
+    };
MAINTAINERS +7 -0
index 15011f5752a9..fc91c4389237 100644
@@ -6213,6 +6213,13 @@ F:	sound/hda/codecs/side-codecs/cs*
 F:	sound/hda/codecs/side-codecs/hda_component*
 F:	sound/soc/codecs/cs*
 
+CIRRUS LOGIC CLOCK DRIVERS
+M:	[email protected]
+L:	[email protected]
+S:	Supported
+F:	Documentation/devicetree/bindings/clock/cirrus,cs2600*
+F:	include/dt-bindings/clock/cirrus,cs2600*
+
 CIRRUS LOGIC HAPTIC DRIVERS
 M:	James Ogletree <[email protected]>
 M:	Fred Treven <[email protected]>
include/dt-bindings/clock/cirrus,cs2600-clock.h new file +18 -0
new file mode 100644
index 000000000000..bde505e17813
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause*/
+/*
+ * CS2600 clock driver
+ *
+ * Copyright (C) 2023-2025 Cirrus Logic, Inc. and
+ *                         Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_CS2600_H
+#define __DT_BINDINGS_CLOCK_CS2600_H
+
+/* Clock output indexes */
+#define CS2600_PLL_OUT			0
+#define CS2600_CLK_OUT			1
+#define CS2600_BCLK_OUT			2
+#define CS2600_FSYNC_OUT		3
+
+#endif /* __DT_BINDINGS_CLOCK_CS2600_H */
Signature · 1 line
2.47.3
#2 lore raw

Re: [PATCH v6 1/3] dt-bindings: clock: cs2600: Add support for the CS2600

Quoted 7 lines · Richard Fitzgerald

From: Paul Handrigan <[email protected]>

Add device tree schema for the Cirrus Logic CS2600 clock generator.

The majority of the schema is typical clock, power and I2C properties.

Quoted 8 lines

Passes dt_binding_check:
make dt_binding_check DT_SCHEMA_FILES=clock/cirrus,cs2600.yaml

  SCHEMA  Documentation/devicetree/bindings/processed-schema.json
  CHKDT   ./Documentation/devicetree/bindings
  LINT    ./Documentation/devicetree/bindings
  STYLE   ./Documentation/devicetree/bindings
  DTEX    Documentation/devicetree/bindings/clock/cirrus,cs2600.example.dts

DTC [C] Documentation/devicetree/bindings/clock/cirrus,cs2600.example.dtb

Why is this in the commit msg? What is its purpose? Do you see any of this in any commits?

Quoted 41 lines

---

Changes in V6:
- Fixed clock naming in descriptions to match datasheet.
- Fixed clock-names property values to match datasheet names.

- Added cirrus,internal-oscillator boolean to flag that the internal oscillator is the clock source. Previously the driver inferred this if clock-names did not contain "ref_clk_in", but this made it difficult to enforce dts correctness because there was no way to tell whether ref_clk_in was intentionally or accidentally missing.

- Changed the cirrus,clock-mode enum property into two booleans, since effectively it was two separate features masquerading as an enum:
- cirrus,smart-mode present to enable smart mode.
- cirrus,smart-mode-clkin-only to enable a feature where the output will be suppressed until both input clocks are present.

- Changes to cirrus,aux-output-source property:
- Renamed to cirrus,aux1-output-source because it's for the AUX1 pin.
- Added more options.
- Renamed the "no_clkin" option to "clkin_missing".
- Reformatted the description as a list instead of one long sentence.

- Changed clock-names from an enum to an ordered list of const. This implicitly ensures ref_clk_in is always required.

- Added properties to invert bclk and fsync outputs.
- Added property cirrus,fsync-duty-cycles.
- Added #clock-cells and vdd-supply to the list of required properies.
- Rewritten description description section.
- Reordered the property list to put common properties before custom cirrus properties.
- Added more examples.
- Added header file to define the clock indexes for DT consumers of the CS2600 clocks.

Quoted 3 lines

Note:
V5 was Reviewed-by: Krzysztof Kozlowski <[email protected]> but I haven't carried this forward because the changes in V6 are large.

Dropping my tag is right thing to do, but making significant changes to hardware at v6 is very odd. Bindings represent here the hardware, so how is that v5 had one view of hardware and the next revision rewrites it completely.

Probably answer - v5 was heavily incomplete - but we do ask to make it complete in the first place (see writing bindings), so all my previous review was waste of time.

This will wait for review.

Quoted 8 lines
Documentation/devicetree/bindings/clock/cirrus,cs2600.yaml new file +8 -0
+examples:
+  - |
+    /* Smart mode */
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      clock-controller@2c {

Documentation/devicetree/bindings/clock/cirrus,cs2600.yaml line 171

Quoted 10 lines
Documentation/devicetree/bindings/clock/cirrus,cs2600.yaml new file +10 -0
+        compatible = "cirrus,cs2600";
+        reg = <0x2c>;
+        #clock-cells = <1>;
+        clocks = <&xtl_clk>, <&sync_clock>;
+        clock-names = "ref_clk_in", "clk_in";
+        vdd-supply = <&vreg>;
+        cirrus,smart-mode;
+      };
+    };
+

Two examples max.

Best regards,
Krzysztof

#3 lore raw

Re: [PATCH v6 1/3] dt-bindings: clock: cs2600: Add support for the CS2600

Quoted 17 lines · Krzysztof Kozlowski
Quoted 7 lines · Richard Fitzgerald

From: Paul Handrigan <[email protected]>

Add device tree schema for the Cirrus Logic CS2600 clock generator.

The majority of the schema is typical clock, power and I2C properties.

Quoted 8 lines

Passes dt_binding_check:
make dt_binding_check DT_SCHEMA_FILES=clock/cirrus,cs2600.yaml

   SCHEMA  Documentation/devicetree/bindings/processed-schema.json
   CHKDT   ./Documentation/devicetree/bindings
   LINT    ./Documentation/devicetree/bindings
   STYLE   ./Documentation/devicetree/bindings
   DTEX    Documentation/devicetree/bindings/clock/cirrus,cs2600.example.dts

DTC [C] Documentation/devicetree/bindings/clock/cirrus,cs2600.example.dtb

Quoted 3 lines

Why is this in the commit msg? What is its purpose? Do you see any of this in any commits?

Purpose is to inform people that I ran the check and it passes. If you don't want that information I'm fine with that. I'll leave it out in V7.

Quoted 46 lines
Quoted 42 lines

---

Changes in V6:
- Fixed clock naming in descriptions to match datasheet.
- Fixed clock-names property values to match datasheet names.

- Added cirrus,internal-oscillator boolean to flag that the internal oscillator is the clock source. Previously the driver inferred this if clock-names did not contain "ref_clk_in", but this made it difficult to enforce dts correctness because there was no way to tell whether ref_clk_in was intentionally or accidentally missing.

- Changed the cirrus,clock-mode enum property into two booleans, since effectively it was two separate features masquerading as an enum:
- cirrus,smart-mode present to enable smart mode.
- cirrus,smart-mode-clkin-only to enable a feature where the output will be suppressed until both input clocks are present.

- Changes to cirrus,aux-output-source property:
- Renamed to cirrus,aux1-output-source because it's for the AUX1 pin.
- Added more options.
- Renamed the "no_clkin" option to "clkin_missing".
- Reformatted the description as a list instead of one long sentence.

- Changed clock-names from an enum to an ordered list of const. This implicitly ensures ref_clk_in is always required.

- Added properties to invert bclk and fsync outputs.
- Added property cirrus,fsync-duty-cycles.
- Added #clock-cells and vdd-supply to the list of required properies.
- Rewritten description description section.
- Reordered the property list to put common properties before custom cirrus properties.
- Added more examples.
- Added header file to define the clock indexes for DT consumers of the CS2600 clocks.

Quoted 3 lines

Note:
V5 was Reviewed-by: Krzysztof Kozlowski <[email protected]> but I haven't carried this forward because the changes in V6 are large.

Quoted 8 lines

Dropping my tag is right thing to do, but making significant changes to hardware at v6 is very odd. Bindings represent here the hardware, so how is that v5 had one view of hardware and the next revision rewrites it completely.

Probably answer - v5 was heavily incomplete - but we do ask to make it complete in the first place (see writing bindings), so all my previous review was waste of time.

Unfortunately I can't do anything about previous versions. I apologize on behalf of Cirrus that they could have been better. I've made what I think are improvements in V6.

V5 was sent in Dec 2024. That's over a year to (a) find issues with V5 and (b) incorporate learnings from non-Linux products about how the CS2600 will be used by manufacturers.

Quoted 15 lines

This will wait for review.

Quoted 12 lines
Documentation/devicetree/bindings/clock/cirrus,cs2600.yaml new file +12 -0
+examples:
+  - |
+    /* Smart mode */
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      clock-controller@2c {
+        compatible = "cirrus,cs2600";
+        reg = <0x2c>;
+        #clock-cells = <1>;
+        clocks = <&xtl_clk>, <&sync_clock>;
Quoted 9 lines

Documentation/devicetree/bindings/clock/cirrus,cs2600.yaml line 171

Quoted 6 lines
Documentation/devicetree/bindings/clock/cirrus,cs2600.yaml new file +6 -0
+        clock-names = "ref_clk_in", "clk_in";
+        vdd-supply = <&vreg>;
+        cirrus,smart-mode;
+      };
+    };
+

Two examples max.

That's an unfortunate rule. But if that's the rule I'll trim it to 2.

Quoted 3 lines

Best regards,
Krzysztof

#4 lore raw

PATCH v6 2/3 clk: cs2600: Add Fractional-N clock driver

In reply to Richard Fitzgerald

From: Paul Handrigan <[email protected]>

Add driver for the Cirrus Logic CS2600 fractional-N clock synthesizer and multiplier.

The CS2600 is a system-clocking device using a hybrid fractional-N analog PLL and a digital frequency-locked loop (FLL). The CS2600 enables frequency synthesis and clock generation from a stable timing reference clock. The device can generate low-jitter clocks between 6 MHz and 75 MHz from a noisy clock reference between 50 Hz and 30 MHz, or from a stable clock reference between 8 MHz and 75 MHz.

The timing reference clock (REF_CLK_IN) can be an external clock source or the internal oscillator. This clock drives the PLL and is always required to generate output clocks, even if it is not used as the frequency reference.

The frequency reference source can be either REF_CLK_IN or an external clock on CLK_IN.

The PLL_OUT clock frequency is a ratio of the chosen frequency reference. The ratio is a fixed-point number of either 12.20 or 20.12 precision. The lower-precision high-multiplication 20.12 ratios are only available when CLK_IN is the frequency reference.

In smart mode the CS2600 will synthesize the output based on REF_CLK_IN until a clock is present on CLK_IN; it then performs a glitchless switch to CLK_IN as the frequency reference. A variant of smart mode automatically enables the PLL output only when CLK_IN becomes available.

Three output pins are derived from PLL_OUT:

CLK_OUT: Gated output from PLL_OUT.
BCLK_OUT: Divided from PLL_OUT.
FSYNC_OUT: Divided from PLL_OUT.

The BCLK_OUT and FSYNC_OUT have different sets of available divide ratios intended to provide the typical frequency relation between bit clock and frame clock on I2S/TDM audio interfaces. They can also be inverted with respect to CLK_OUT to support various formats of serial audio data. FSYNC_OUT has the additional feature that it can be either a 50:50 duty cycle or a pulse of a programmable number of BCLK cycles.

This configuration is modeled in the driver as:

                 +------------+
  REF_CLK_IN ----| Hybrid PLL | PLL_OUT    +--------+
                 |   + FLL    |-----+------|  GATE  |--------- CLK_OUT
      CLK_IN ----|            |     |      +--------+
                 +------------+     |
                                    |      +---------+
                                    +------| DIVIDER |-------- BCLK_OUT
                                    |      +---------+
                                    |
                                    |      +---------+
                                    +------| DIVIDER |-------- FSYNC_OUT
                                           +---------+

OF properties define the hardware-level configuration to match the hardware that the CS2600 is driving.

The CS2600_ERR_xxx register bits are not used in the driver but are defined in the header file as a convenience for anyone debugging a system.

---

Changes in V6:

- Merged the functions to calculate synth mode and mult mode ratios. The math is the same, only the fixed-point precision is different. The new cs2600_calc_ratio() function calculates at 20:20 fixed point and then converts to 20:12 if the value is too large for 12:20.
This also simplifies the calling code because it doesn't need all the checks of which ratio function it should be calling.
- Re-worked the rounding of calculated rates so that they didn't drift away from the best rate when the clock core passed the rounded rate from determine_rate() back into set_rate().
- Changed various parts of the recalc_rate()/determine_rate()/set_rate() functions that relied on the state of the other clock registers to make decisions. There's no guarantee whether those other clocks have been configured yet so the decision making could be invalid.
- Removed the use of different ratio slots depending on the mode. The The CS2600 doesn't care which slots are used for synth and mult ratio. Using fixed slots simplifies the code.
- Split the large cs2600_pll_out_prepare() into helper functions.
- Request ref_clk when starting the PLL. It is the system clock of the CS2600 so it is always required to clock the chip even if it isn't the frequency reference parent. In previous versions ref_clk was only enabled by being the parent, which meant the CS2600 wasn't clocked when using mult mode with clk_in as the frequency reference parent.
- Fix cs2600_pll_out_set_parent() to properly reconfigure the PLL when changing parents. The parent selection isn't a simple mux, it changes PLL mode and selected ratio registers. Previously the mode bit was written but the mode ratio registers weren't updated to match.
- Factor out the BCLK and FSYNC divider lookup into a single function. The lookup code is the same apart from the divide value that each register field value represents. The divide values are provided as an array.
- Don't attempt to set a best_parent_rate when setting BCLK/FSYNC rate. The parent (CLK_OUT) rate is not arbitrary, it must match the requirements of the device being clocked, and only the consumer driver will know what is correct.
- Set CLK_SET_PARENT_GATE on the PLL. Its configuration cannot be updated atomically, so it must be stopped to reconfigure it.
- Split several functions into separate functions for calculating a value and get/set the register.
- Use FIELD_PREP() and FIELD_GET() instead of multiple custom macros.
- Fix inverted register unfreeze and freeze that meant the bits affected by FREEZE_EN were not actually updated.
- Use a DEFINE_GUARD to implement the register field unfreeze.
- Rename cs2600_ref_clk_bound_rate() to cs2600_ref_clk_set_divider() to indicate it's actually setting the rate, not just finding a rounded value.
- Remove inappropriate use of in_range() that required some confusing math to pass the correct value into in_range(). It's clearer to do normal min/max comparison.
- Use DIV_ROUND_UP_ULL() to convert PPM to a frequency delta instead of open-coding it.
- Initialize all the clocks using a single common loop.
- Use clk_init_data.parent_hws to set FSYNC, BCLK and CLK_OUT parent as PLL_OUT, instead of looking up PLL_OUT by name and setting FSYNC, BCLK and CLK_OUT parent to that name.
- Smart mode selection is now two boolean properties instead of an enum property. One property select smart mode, the other property enables suppression of PLL_OUT until there is clock on CLK_IN.
- Added properties to invert FSYNC and BCLK relative to CLK_OUT.
- Added property to set the duty cycle of FSYNC (required when clocking TDM audio formats).
- Use match_string() instead of a string of strcmp() to lookup cirrus,aux1-output-source value.
- Use of the internal oscillator is now explictly flagged by the presence of the cirrus,internal-oscillator property. Previously the driver inferred it from the absence of a ref_clk in clock-names.
- More error checking.

Changes before V6:
Sorry, the previously upstreamed versions don't have a changelist for me to copy into here.

 MAINTAINERS                     |    1 +
 drivers/clk/Kconfig             |    1 +
 drivers/clk/Makefile            |    1 +
 drivers/clk/cirrus/Kconfig      |   10 +
 drivers/clk/cirrus/Makefile     |    3 +
 drivers/clk/cirrus/clk-cs2600.c | 1274 +++++++++++++++++++++++++++++++
 drivers/clk/cirrus/clk-cs2600.h |  163 ++++
 7 files changed, 1453 insertions(+)
 create mode 100644 drivers/clk/cirrus/Kconfig
 create mode 100644 drivers/clk/cirrus/Makefile
 create mode 100644 drivers/clk/cirrus/clk-cs2600.c
 create mode 100644 drivers/clk/cirrus/clk-cs2600.h
MAINTAINERS +1 -0
index fc91c4389237..54407ed4b338 100644
@@ -6218,6 +6218,7 @@ M:	[email protected]
 L:	[email protected]
 S:	Supported
 F:	Documentation/devicetree/bindings/clock/cirrus,cs2600*
+F:	drivers/clk/cirrus/*
 F:	include/dt-bindings/clock/cirrus,cs2600*
 
 CIRRUS LOGIC HAPTIC DRIVERS
drivers/clk/Kconfig +1 -0
index 1717ce75a907..e576c6cd33f4 100644
@@ -509,6 +509,7 @@ source "drivers/clk/actions/Kconfig"
 source "drivers/clk/analogbits/Kconfig"
 source "drivers/clk/aspeed/Kconfig"
 source "drivers/clk/bcm/Kconfig"
+source "drivers/clk/cirrus/Kconfig"
 source "drivers/clk/eswin/Kconfig"
 source "drivers/clk/hisilicon/Kconfig"
 source "drivers/clk/imgtec/Kconfig"
drivers/clk/Makefile +1 -0
index cc108a75a900..b1e3bb794d7f 100644
@@ -119,6 +119,7 @@ obj-$(CONFIG_ARCH_ARTPEC)		+= axis/
 obj-$(CONFIG_ARC_PLAT_AXS10X)		+= axs10x/
 obj-y					+= bcm/
 obj-$(CONFIG_ARCH_BERLIN)		+= berlin/
+obj-y					+= cirrus/
 obj-$(CONFIG_ARCH_DAVINCI)		+= davinci/
 obj-$(CONFIG_COMMON_CLK_ESWIN)		+= eswin/
 obj-$(CONFIG_ARCH_HISI)			+= hisilicon/
drivers/clk/cirrus/Kconfig new file +10 -0
new file mode 100644
index 000000000000..5674d6554f96
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config COMMON_CLK_CS2600
+	tristate "Clock driver for CS2600 Fractional-N Clock Synthesizer & Clock Multiplier"
+	depends on I2C
+	depends on OF
+	select REGMAP_I2C
+	help
+	  If you say yes here you get support for the CS2600 clock synthesizer
+	  and multiplier.
drivers/clk/cirrus/Makefile new file +3 -0
new file mode 100644
index 000000000000..182612dbb96a
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_COMMON_CLK_CS2600)		+= clk-cs2600.o
drivers/clk/cirrus/clk-cs2600.c new file +1274 -0

1,274 lines not on this page · raw message

drivers/clk/cirrus/clk-cs2600.h new file +163 -0

163 lines not on this page · raw message

Signature · 1 line
2.47.3
#5 lore raw

Re: [PATCH v6 2/3] clk: cs2600: Add Fractional-N clock driver

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider: - [High] The `cs2600_unfreeze` guard uses inverted lock/unlock operations, leaving the device permanently…

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] The cs2600_unfreeze guard uses inverted lock/unlock operations, leaving the device permanently frozen.
- [High] Clock rates and dividers are updated without the necessary hardware freeze guard, risking clock glitches.
- [High] Data race on statically defined parent_names array in cs2600_clk_register.
- [High] The cs2600_pll_out_recalc_rate callback returns uninitialized software state instead of interrogating the hardware.
- [High] Refcount underflow in cs2600_pll_out_unprepare due to early return in cs2600_pll_out_prepare.
- [Medium] The determine_rate callbacks incorrectly return -EINVAL and emit log spam instead of rounding req->rate.
- [Medium] Missing clk_in validation in smart mode leads to 0 parents, breaking rate calculation.
--

commit ce7a3c3019cf88623a644142e879bd585ca8f100
Author: Paul Handrigan <[email protected]>

clk: cs2600: Add Fractional-N clock driver

Add driver for the Cirrus Logic CS2600 fractional-N clock synthesizer and multiplier.

Quoted 5 lines
drivers/clk/cirrus/clk-cs2600.c new file +0 -0
new file mode 100644
index 0000000000000..73937ac868fcd

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 125

Quoted 7 lines
drivers/clk/cirrus/clk-cs2600.c new file +7 -0
+static void cs2600_clear_freeze(struct cs2600 *cs2600)
+{
+	regmap_clear_bits(cs2600->regmap, CS2600_PLL_CFG2, CS2600_FREEZE_EN);
+}
+
+DEFINE_GUARD(cs2600_unfreeze, struct cs2600 *,
+	     cs2600_clear_freeze(_T), cs2600_set_freeze(_T))

[Severity: High]
Are the lock and unlock operations inverted here?

The macro DEFINE_GUARD takes the _lock (entry) operation first and _unlock (exit) operation second. The device defaults to an unfrozen state (FREEZE_EN=0). By passing cs2600_clear_freeze as the entry operation, it is initially a no-op, and passing cs2600_set_freeze on exit permanently freezes the hardware.

Could this cause subsequent writes to critical registers to be ignored or held indefinitely, breaking the device configuration?

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 252

Quoted 11 lines
drivers/clk/cirrus/clk-cs2600.c new file +11 -0
+static unsigned long cs2600_pll_out_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
+	struct cs2600 *cs2600 = clk_hw->priv;
+
+	if ((parent_rate == 0) || (cs2600->pll_target_rate == 0))
+		return 0;
+
+	if (parent_rate == cs2600->pll_parent_rate)
+		return cs2600->pll_target_rate;

[Severity: High]
Does this function need to interrogate the actual hardware state?

During boot, the Common Clock Framework relies on recalc_rate to determine the active hardware rate. Because cs2600->pll_target_rate initializes to 0, this will report 0 Hz even if the bootloader previously configured and enabled the PLL.

Will this break downstream consumers that depend on inheriting the correct operational clock rate from the hardware?

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 318

Quoted 11 lines
drivers/clk/cirrus/clk-cs2600.c new file +11 -0
+static int cs2600_pll_out_set_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long parent_rate)
+{
+	struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
+	struct regmap *regmap = clk_hw->priv->regmap;
+	struct cs2600 *cs2600 = clk_hw->priv;
+	unsigned int ratio, smart_s_ratio;
+	bool hi_res;
+	int ret;
+
+	ret = cs2600_calc_ratio(cs2600, rate, parent_rate, &ratio, &hi_res);

[Severity: High]
Should this function acquire the unfreeze guard before modifying the ratio registers?

This function performs multiple 16-bit regmap_write operations to update a 32-bit ratio. Without suspending the hardware state machine during these updates, the chip might briefly operate with a corrupted 32-bit ratio (half old, half new).

Could this missing guard cause clock glitches, frequency spikes, or a temporary loss of lock?

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 456

Quoted 13 lines
drivers/clk/cirrus/clk-cs2600.c new file +13 -0
+static int cs2600_pll_out_prepare(struct clk_hw *hw)
+{
+	struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
+	struct regmap *regmap = clk_hw->priv->regmap;
+	struct cs2600 *cs2600 = clk_hw->priv;
+	int ret;
+
+	ret = regmap_test_bits(regmap, CS2600_PLL_CFG1, CS2600_PLL_EN1);
+	if (ret < 0)
+		return ret;
+
+	if (ret)
+		return 0;

[Severity: High]
Will this early return cause a refcount underflow later?

If the PLL is already enabled (for example, by the bootloader), this function returns 0 immediately and skips calling clk_prepare_enable(cs2600->ref_clk).

However, cs2600_pll_out_unprepare() unconditionally calls clk_disable_unprepare(cs2600->ref_clk).

When CCF disables an unused clock or a consumer unprepares it, wouldn't the driver drop a software reference to ref_clk that was never actually acquired, potentially shutting down the shared reference clock for other hardware?

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 660

Quoted 11 lines
drivers/clk/cirrus/clk-cs2600.c new file +11 -0
+static int cs2600_bclk_determine_rate(struct clk_hw *hw,
+				      struct clk_rate_request *req)
+{
+	struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
+	struct cs2600 *cs2600 = clk_hw->priv;
+
+	if (req->rate > CS2600_BCLK_OUT_MAX ||
+	    req->rate < CS2600_BCLK_OUT_MIN) {
+		dev_err(cs2600->dev, "Invalid bclk rate %lu", req->rate);
+		return -EINVAL;
+	}

[Severity: Medium]
Should this callback adjust req->rate to the closest supported frequency instead of returning -EINVAL?

CCF determine_rate callbacks are expected to round the requested rate. Returning -EINVAL breaks standard rate negotiation like clk_round_rate(). This also applies to cs2600_fsync_determine_rate().

Also, because consumers routinely request unachievable bounds during initial rate negotiation, printing a dev_err() here may cause unnecessary kernel log spam.

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 968

Quoted 3 lines
drivers/clk/cirrus/clk-cs2600.c new file +3 -0
+static int cs2600_clk_register(struct cs2600 *cs2600)
+{
+	static const char *parent_names[2];

[Severity: High]
Is it safe to share this static array across all driver instances without synchronization?

If multiple CS2600 devices are probed simultaneously via asynchronous probing or on independent buses, their probe threads could concurrently overwrite this static array before calling devm_clk_hw_register().

Would this corrupt the clock topologies, causing one device to register with the parent names intended for another?

Quoted 3 lines
drivers/clk/cirrus/clk-cs2600.c new file +3 -0
+	const struct clk_hw *out_clocks_parent;
+	struct clk_init_data init[ARRAY_SIZE(cs2600->hw)] = { };
+	int ret, i, n, input_num = 0;

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 998

Quoted 9 lines
drivers/clk/cirrus/clk-cs2600.c new file +9 -0
+	/* ref_clk_in is only a parent option in manual mode */
+	if (!cs2600_is_smart_mode(cs2600))
+		parent_names[input_num++] = __clk_get_name(cs2600->ref_clk);
+
+	if (cs2600->clk_in)
+		parent_names[input_num++] = __clk_get_name(cs2600->clk_in);
+
+	init[CS2600_PLL_OUT].parent_names = parent_names;
+	init[CS2600_PLL_OUT].num_parents = input_num;

[Severity: Medium]
What happens if clk_in is NULL when the device operates in smart mode?

The driver fetches clk_in as optional using devm_clk_get_optional(). If cs2600_is_smart_mode() is true and clk_in is missing from the device tree, input_num will be 0.

Registering the PLL with num_parents = 0 leaves the PLL without a valid parent clock in the CCF. Will this cause downstream frequency calculations to fail or evaluate to 0, despite the hardware relying on an external clock?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=2
#6 lore raw

Re: [PATCH v6 2/3] clk: cs2600: Add Fractional-N clock driver

Quoted 10 lines · On 30/06/2026 5:11 pm, [email protected] wrote:

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] The cs2600_unfreeze guard uses inverted lock/unlock operations, leaving the device permanently frozen.
- [High] Clock rates and dividers are updated without the necessary hardware freeze guard, risking clock glitches.
- [High] Data race on statically defined parent_names array in cs2600_clk_register.
- [High] The cs2600_pll_out_recalc_rate callback returns uninitialized software state instead of interrogating the hardware.
- [High] Refcount underflow in cs2600_pll_out_unprepare due to early return in cs2600_pll_out_prepare.
- [Medium] The determine_rate callbacks incorrectly return -EINVAL and emit log spam instead of rounding req->rate.
- [Medium] Missing clk_in validation in smart mode leads to 0 parents, breaking rate calculation.
--

Gemini has found some real issues and some bogus stuff, which I've commented on below.
I'll hold off on doing a V7 to fix the real issues, to give people a chance to add other reviews.

Quoted 16 lines

commit ce7a3c3019cf88623a644142e879bd585ca8f100
Author: Paul Handrigan <[email protected]>

clk: cs2600: Add Fractional-N clock driver

Add driver for the Cirrus Logic CS2600 fractional-N clock synthesizer and multiplier.

Quoted 5 lines
drivers/clk/cirrus/clk-cs2600.c new file +0 -0
new file mode 100644
index 0000000000000..73937ac868fcd

[ ... ]

Quoted 11 lines

drivers/clk/cirrus/clk-cs2600.c line 125

Quoted 7 lines
drivers/clk/cirrus/clk-cs2600.c new file +7 -0
+static void cs2600_clear_freeze(struct cs2600 *cs2600)
+{
+	regmap_clear_bits(cs2600->regmap, CS2600_PLL_CFG2, CS2600_FREEZE_EN);
+}
+
+DEFINE_GUARD(cs2600_unfreeze, struct cs2600 *,
+	     cs2600_clear_freeze(_T), cs2600_set_freeze(_T))

[Severity: High]
Are the lock and unlock operations inverted here?

No, they are not. FREEZE_EN must be cleared to write protected bits and set after they have been written.

Quoted 16 lines

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 252

Quoted 11 lines
drivers/clk/cirrus/clk-cs2600.c new file +11 -0
+static unsigned long cs2600_pll_out_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
+	struct cs2600 *cs2600 = clk_hw->priv;
+
+	if ((parent_rate == 0) || (cs2600->pll_target_rate == 0))
+		return 0;
+
+	if (parent_rate == cs2600->pll_parent_rate)
+		return cs2600->pll_target_rate;

[Severity: High]
Does this function need to interrogate the actual hardware state?

If the parent rate hasn't changed, the output rate hasn't changed. This is to prevent multiple fractional->integer conversions causing the set frequency to drift away from the best frequency.

Quoted 8 lines

During boot, the Common Clock Framework relies on recalc_rate to determine the active hardware rate. Because cs2600->pll_target_rate initializes to 0, this will report 0 Hz even if the bootloader previously configured and enabled the PLL.

Will this break downstream consumers that depend on inheriting the correct operational clock rate from the hardware?

The driver resets the CS2600 during probe.

Quoted 18 lines

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 318

Quoted 11 lines
drivers/clk/cirrus/clk-cs2600.c new file +11 -0
+static int cs2600_pll_out_set_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long parent_rate)
+{
+	struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
+	struct regmap *regmap = clk_hw->priv->regmap;
+	struct cs2600 *cs2600 = clk_hw->priv;
+	unsigned int ratio, smart_s_ratio;
+	bool hi_res;
+	int ret;
+
+	ret = cs2600_calc_ratio(cs2600, rate, parent_rate, &ratio, &hi_res);

[Severity: High]
Should this function acquire the unfreeze guard before modifying the ratio registers?

No, FREEZE_EN doesn't affect the ratio registers.

Quoted 8 lines

This function performs multiple 16-bit regmap_write operations to update a 32-bit ratio. Without suspending the hardware state machine during these updates, the chip might briefly operate with a corrupted 32-bit ratio (half old, half new).

Could this missing guard cause clock glitches, frequency spikes, or a temporary loss of lock?

It's a PLL. The output frequency will drift to the new frequency, and this can be preferable to a hard switch.
However, I will double-check with hardware people whether we should enforce that the clock is stopped around a ratio change instead of

Quoted 19 lines

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 456

Quoted 13 lines
drivers/clk/cirrus/clk-cs2600.c new file +13 -0
+static int cs2600_pll_out_prepare(struct clk_hw *hw)
+{
+	struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
+	struct regmap *regmap = clk_hw->priv->regmap;
+	struct cs2600 *cs2600 = clk_hw->priv;
+	int ret;
+
+	ret = regmap_test_bits(regmap, CS2600_PLL_CFG1, CS2600_PLL_EN1);
+	if (ret < 0)
+		return ret;
+
+	if (ret)
+		return 0;

[Severity: High]
Will this early return cause a refcount underflow later?

This quick exit is something in V5 I hadn't noticed, and it's a bit strange. This test should always return false.

Quoted 18 lines

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 660

Quoted 11 lines
drivers/clk/cirrus/clk-cs2600.c new file +11 -0
+static int cs2600_bclk_determine_rate(struct clk_hw *hw,
+				      struct clk_rate_request *req)
+{
+	struct cs2600_clk_hw *clk_hw = hw_to_cs2600_clk(hw);
+	struct cs2600 *cs2600 = clk_hw->priv;
+
+	if (req->rate > CS2600_BCLK_OUT_MAX ||
+	    req->rate < CS2600_BCLK_OUT_MIN) {
+		dev_err(cs2600->dev, "Invalid bclk rate %lu", req->rate);
+		return -EINVAL;
+	}

[Severity: Medium]
Should this callback adjust req->rate to the closest supported frequency instead of returning -EINVAL?

Nope, there's no way that this driver can determine what an appropriate parent rate would be. The parent rate is likely clocking some hardware via CLK_OUT and we can't arbitrarily change that rate.

Quoted 9 lines

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 968

Quoted 3 lines
drivers/clk/cirrus/clk-cs2600.c new file +3 -0
+static int cs2600_clk_register(struct cs2600 *cs2600)
+{
+	static const char *parent_names[2];

[Severity: High]
Is it safe to share this static array across all driver instances without synchronization?

Valid. This static should not be here.
This is a bug inherited from V5 that had not been picked up by any automated or manual code reviews until Gemini took a look.

Quoted 14 lines

[ ... ]

drivers/clk/cirrus/clk-cs2600.c line 998

Quoted 9 lines
drivers/clk/cirrus/clk-cs2600.c new file +9 -0
+	/* ref_clk_in is only a parent option in manual mode */
+	if (!cs2600_is_smart_mode(cs2600))
+		parent_names[input_num++] = __clk_get_name(cs2600->ref_clk);
+
+	if (cs2600->clk_in)
+		parent_names[input_num++] = __clk_get_name(cs2600->clk_in);
+
+	init[CS2600_PLL_OUT].parent_names = parent_names;
+	init[CS2600_PLL_OUT].num_parents = input_num;

[Severity: Medium]
What happens if clk_in is NULL when the device operates in smart mode?

Yes, this is a bug.

#7 lore raw

Re: [PATCH v6 2/3] clk: cs2600: Add Fractional-N clock driver

In reply to Richard Fitzgerald

drivers/clk/cirrus/clk-cs2600.c line 1258

Quoted 4 lines · Richard Fitzgerald
drivers/clk/cirrus/clk-cs2600.c new file +4 -0
+static const struct i2c_device_id cs2600_id[] = {
+	{ "cs2600", },
+	{}
+};

Please make this:

	static const struct i2c_device_id cs2600_id[] = {
		{ .name = "cs2600" },
		{ }
	};

to match the common style.

Best regards
Uwe

#8 lore raw

Re: [PATCH v6 2/3] clk: cs2600: Add Fractional-N clock driver

In reply to Richard Fitzgerald

Hi Richard,

Thanks for the patch.

Quoted 154 lines · Richard Fitzgerald

From: Paul Handrigan <[email protected]>

Add driver for the Cirrus Logic CS2600 fractional-N clock synthesizer and multiplier.

The CS2600 is a system-clocking device using a hybrid fractional-N analog PLL and a digital frequency-locked loop (FLL). The CS2600 enables frequency synthesis and clock generation from a stable timing reference clock. The device can generate low-jitter clocks between 6 MHz and 75 MHz from a noisy clock reference between 50 Hz and 30 MHz, or from a stable clock reference between 8 MHz and 75 MHz.

The timing reference clock (REF_CLK_IN) can be an external clock source or the internal oscillator. This clock drives the PLL and is always required to generate output clocks, even if it is not used as the frequency reference.

The frequency reference source can be either REF_CLK_IN or an external clock on CLK_IN.

The PLL_OUT clock frequency is a ratio of the chosen frequency reference. The ratio is a fixed-point number of either 12.20 or 20.12 precision. The lower-precision high-multiplication 20.12 ratios are only available when CLK_IN is the frequency reference.

In smart mode the CS2600 will synthesize the output based on REF_CLK_IN until a clock is present on CLK_IN; it then performs a glitchless switch to CLK_IN as the frequency reference. A variant of smart mode automatically enables the PLL output only when CLK_IN becomes available.

Three output pins are derived from PLL_OUT:

CLK_OUT: Gated output from PLL_OUT.
BCLK_OUT: Divided from PLL_OUT.
FSYNC_OUT: Divided from PLL_OUT.

The BCLK_OUT and FSYNC_OUT have different sets of available divide ratios intended to provide the typical frequency relation between bit clock and frame clock on I2S/TDM audio interfaces. They can also be inverted with respect to CLK_OUT to support various formats of serial audio data. FSYNC_OUT has the additional feature that it can be either a 50:50 duty cycle or a pulse of a programmable number of BCLK cycles.

This configuration is modeled in the driver as:

                 +------------+
  REF_CLK_IN ----| Hybrid PLL | PLL_OUT    +--------+
                 |   + FLL    |-----+------|  GATE  |--------- CLK_OUT
      CLK_IN ----|            |     |      +--------+
                 +------------+     |
                                    |      +---------+
                                    +------| DIVIDER |-------- BCLK_OUT
                                    |      +---------+
                                    |
                                    |      +---------+
                                    +------| DIVIDER |-------- FSYNC_OUT
                                           +---------+

OF properties define the hardware-level configuration to match the hardware that the CS2600 is driving.

The CS2600_ERR_xxx register bits are not used in the driver but are defined in the header file as a convenience for anyone debugging a system.

---

Changes in V6:

- Merged the functions to calculate synth mode and mult mode ratios. The math is the same, only the fixed-point precision is different. The new cs2600_calc_ratio() function calculates at 20:20 fixed point and then converts to 20:12 if the value is too large for 12:20.
This also simplifies the calling code because it doesn't need all the checks of which ratio function it should be calling.
- Re-worked the rounding of calculated rates so that they didn't drift away from the best rate when the clock core passed the rounded rate from determine_rate() back into set_rate().
- Changed various parts of the recalc_rate()/determine_rate()/set_rate() functions that relied on the state of the other clock registers to make decisions. There's no guarantee whether those other clocks have been configured yet so the decision making could be invalid.
- Removed the use of different ratio slots depending on the mode. The The CS2600 doesn't care which slots are used for synth and mult ratio. Using fixed slots simplifies the code.
- Split the large cs2600_pll_out_prepare() into helper functions.
- Request ref_clk when starting the PLL. It is the system clock of the CS2600 so it is always required to clock the chip even if it isn't the frequency reference parent. In previous versions ref_clk was only enabled by being the parent, which meant the CS2600 wasn't clocked when using mult mode with clk_in as the frequency reference parent.
- Fix cs2600_pll_out_set_parent() to properly reconfigure the PLL when changing parents. The parent selection isn't a simple mux, it changes PLL mode and selected ratio registers. Previously the mode bit was written but the mode ratio registers weren't updated to match.
- Factor out the BCLK and FSYNC divider lookup into a single function. The lookup code is the same apart from the divide value that each register field value represents. The divide values are provided as an array.
- Don't attempt to set a best_parent_rate when setting BCLK/FSYNC rate. The parent (CLK_OUT) rate is not arbitrary, it must match the requirements of the device being clocked, and only the consumer driver will know what is correct.
- Set CLK_SET_PARENT_GATE on the PLL. Its configuration cannot be updated atomically, so it must be stopped to reconfigure it.
- Split several functions into separate functions for calculating a value and get/set the register.
- Use FIELD_PREP() and FIELD_GET() instead of multiple custom macros.
- Fix inverted register unfreeze and freeze that meant the bits affected by FREEZE_EN were not actually updated.
- Use a DEFINE_GUARD to implement the register field unfreeze.
- Rename cs2600_ref_clk_bound_rate() to cs2600_ref_clk_set_divider() to indicate it's actually setting the rate, not just finding a rounded value.
- Remove inappropriate use of in_range() that required some confusing math to pass the correct value into in_range(). It's clearer to do normal min/max comparison.
- Use DIV_ROUND_UP_ULL() to convert PPM to a frequency delta instead of open-coding it.
- Initialize all the clocks using a single common loop.
- Use clk_init_data.parent_hws to set FSYNC, BCLK and CLK_OUT parent as PLL_OUT, instead of looking up PLL_OUT by name and setting FSYNC, BCLK and CLK_OUT parent to that name.
- Smart mode selection is now two boolean properties instead of an enum property. One property select smart mode, the other property enables suppression of PLL_OUT until there is clock on CLK_IN.
- Added properties to invert FSYNC and BCLK relative to CLK_OUT.
- Added property to set the duty cycle of FSYNC (required when clocking TDM audio formats).
- Use match_string() instead of a string of strcmp() to lookup cirrus,aux1-output-source value.
- Use of the internal oscillator is now explictly flagged by the presence of the cirrus,internal-oscillator property. Previously the driver inferred it from the absence of a ref_clk in clock-names.
- More error checking.

Changes before V6:
Sorry, the previously upstreamed versions don't have a changelist for me to copy into here.

 MAINTAINERS                     |    1 +
 drivers/clk/Kconfig             |    1 +
 drivers/clk/Makefile            |    1 +
 drivers/clk/cirrus/Kconfig      |   10 +
 drivers/clk/cirrus/Makefile     |    3 +
 drivers/clk/cirrus/clk-cs2600.c | 1274 +++++++++++++++++++++++++++++++
 drivers/clk/cirrus/clk-cs2600.h |  163 ++++
 7 files changed, 1453 insertions(+)
 create mode 100644 drivers/clk/cirrus/Kconfig
 create mode 100644 drivers/clk/cirrus/Makefile
 create mode 100644 drivers/clk/cirrus/clk-cs2600.c
 create mode 100644 drivers/clk/cirrus/clk-cs2600.h

[snip]

Quoted 5 lines
drivers/clk/cirrus/clk-cs2600.c new file +0 -0
new file mode 100644
index 000000000000..73937ac868fc

[snip]

drivers/clk/cirrus/clk-cs2600.c line 125

Quoted 3 lines
drivers/clk/cirrus/clk-cs2600.c new file +3 -0
+
+DEFINE_GUARD(cs2600_unfreeze, struct cs2600 *,
+	     cs2600_clear_freeze(_T), cs2600_set_freeze(_T))

Can you go through the Sashiko output for this series?

https://sashiko.dev/#/message/20260630161121.020FB1F000E9%40smtp.kernel.org

The clear/set inverted looks questionable to me. If this is expected, then a comment clarifying why should be added.

[snip]

drivers/clk/cirrus/clk-cs2600.c line 968

Quoted 3 lines
drivers/clk/cirrus/clk-cs2600.c new file +3 -0
+static int cs2600_clk_register(struct cs2600 *cs2600)
+{
+	static const char *parent_names[2];

I wouldn't worry too about about Sashiko asking about multiple driver instances for this section, unless you think it will eventually be used in that way.

drivers/clk/cirrus/clk-cs2600.c line 972

Quoted 4 lines
drivers/clk/cirrus/clk-cs2600.c new file +4 -0
+	const struct clk_hw *out_clocks_parent;
+	struct clk_init_data init[ARRAY_SIZE(cs2600->hw)] = { };
+	int ret, i, n, input_num = 0;
+	const char *names[ARRAY_SIZE(cs2600_clks)];

Reverse Christmas tree order.

I can do a more thorough review once you go through the Sashiko results.

Brian

#9 lore raw

Re: [PATCH v6 2/3] clk: cs2600: Add Fractional-N clock driver

Quoted 10 lines · Brian Masney

Hi Richard,

Thanks for the patch.

Quoted 5 lines · Richard Fitzgerald

From: Paul Handrigan <[email protected]>

Add driver for the Cirrus Logic CS2600 fractional-N clock synthesizer and multiplier.

<SNIP>

Quoted 5 lines

drivers/clk/cirrus/clk-cs2600.c line 125

Quoted 3 lines
drivers/clk/cirrus/clk-cs2600.c new file +3 -0
+
+DEFINE_GUARD(cs2600_unfreeze, struct cs2600 *,
+	     cs2600_clear_freeze(_T), cs2600_set_freeze(_T))

Can you go through the Sashiko output for this series?

I sent an email responding to those, but it was reply-all to the Sashiko bot email and I didn't notice that Sashiko had stripped a lot of the original email recipients.

My responses to Sashiko are here:
https://lore.kernel.org/all/[email protected]/

Quoted 4 lines

https://sashiko.dev/#/message/20260630161121.020FB1F000E9%40smtp.kernel.org

The clear/set inverted looks questionable to me. If this is expected, then a comment clarifying why should be added.

I'll add a comment in the next version explaining the order.

Quoted 9 lines

[snip]

drivers/clk/cirrus/clk-cs2600.c line 968

Quoted 3 lines
drivers/clk/cirrus/clk-cs2600.c new file +3 -0
+static int cs2600_clk_register(struct cs2600 *cs2600)
+{
+	static const char *parent_names[2];

I wouldn't worry too about about Sashiko asking about multiple driver instances for this section, unless you think it will eventually be used in that way.

Trivial to fix though. That 'static' shouldn't be there.

Quoted 6 lines

drivers/clk/cirrus/clk-cs2600.c line 972

Quoted 4 lines
drivers/clk/cirrus/clk-cs2600.c new file +4 -0
+	const struct clk_hw *out_clocks_parent;
+	struct clk_init_data init[ARRAY_SIZE(cs2600->hw)] = { };
+	int ret, i, n, input_num = 0;
+	const char *names[ARRAY_SIZE(cs2600_clks)];

Reverse Christmas tree order.

Will do.

Quoted 4 lines

I can do a more thorough review once you go through the Sashiko results.

Brian

#10 lore raw

PATCH v6 3/3 clk: cs2600: Add KUnit test for CS2600 driver

In reply to Richard Fitzgerald

Add a KUnit test for the CS2600 clock driver.

This test uses a mock I2C host driver and mock regmap to partially emulate the hardware. The regmap is permanently in cache-only mode so that it will act as an emulation of a set of device registers. Some special cases are implemented to emulate behavior of certain registers:

- Some register bits can only be changed when the FREEZE_EN bit is 0.
- UNLOCK status bits are 1 if the PLL is disabled or not locked.

The bulk of the testing is to confirm that the frequency ratio and PLL configuration register fields are set correctly. The PLL configuration and ratio precision depend on several factors:

- The frequency reference source (REF_CLK_IN or CLK_IN).
- The magnitude of frequency difference between input and output clocks.
- Whether the CS2600 is operating in smart mode.

The test uses hardcoded tables of the expected ratio register settings for pairs of IN:OUT clock frequencies in various modes. The values in these tables were derived independently from the calculation code in the clk-cs2600.c driver. The purpose of this is to avoid false test passes caused by the test code having the same calculation bug as the code it is testing. Using an independent source of ratios reduces this risk. These tables do not attempt to test every possible ratio, but focus on (a) typical frequencies, and (b) edge cases where rounding is expected.

Devicetree overlay files are used to configure properties for a variety of possible hardware configurations. There are test cases to validate that the properties are applied correctly.

---
New in V6 series.

 drivers/clk/cirrus/Kconfig                    |   13 +
 drivers/clk/cirrus/Makefile                   |   24 +
 drivers/clk/cirrus/clk-cs2600-kunit.c         | 2495 +++++++++++++++++
 drivers/clk/cirrus/clk-cs2600-kunit.h         |   18 +
 drivers/clk/cirrus/kunit_clk_cs2600.dtsi      |   40 +
 .../clk/cirrus/kunit_clk_cs2600_assigned.dtso |   27 +
 .../kunit_clk_cs2600_aux1_out_clk_in.dtso     |   17 +
 ...it_clk_cs2600_aux1_out_clk_in_missing.dtso |   17 +
 ...kunit_clk_cs2600_aux1_out_freq_unlock.dtso |   17 +
 ...unit_clk_cs2600_aux1_out_phase_unlock.dtso |   17 +
 .../kunit_clk_cs2600_aux1_out_ref_clk_in.dtso |   17 +
 .../cirrus/kunit_clk_cs2600_bclk_invert.dtso  |   17 +
 .../cirrus/kunit_clk_cs2600_fsync_duty_1.dtso |   17 +
 .../cirrus/kunit_clk_cs2600_fsync_duty_2.dtso |   17 +
 .../kunit_clk_cs2600_fsync_duty_32.dtso       |   17 +
 .../cirrus/kunit_clk_cs2600_fsync_invert.dtso |   17 +
 .../kunit_clk_cs2600_manual_intosc_clkin.dtso |   17 +
 .../kunit_clk_cs2600_manual_intosc_only.dtso  |   17 +
 .../kunit_clk_cs2600_manual_refclk_clkin.dtso |   16 +
 .../kunit_clk_cs2600_manual_refclk_only.dtso  |   16 +
 .../kunit_clk_cs2600_name_output_clocks.dtso  |   23 +
 ...it_clk_cs2600_smart_clkin_only_intosc.dtso |   19 +
 ...it_clk_cs2600_smart_clkin_only_refclk.dtso |   18 +
 .../cirrus/kunit_clk_cs2600_smart_intosc.dtso |   18 +
 .../cirrus/kunit_clk_cs2600_smart_refclk.dtso |   17 +
 25 files changed, 2948 insertions(+)
 create mode 100644 drivers/clk/cirrus/clk-cs2600-kunit.c
 create mode 100644 drivers/clk/cirrus/clk-cs2600-kunit.h
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600.dtsi
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_assigned.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_clk_in.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_clk_in_missing.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_freq_unlock.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_phase_unlock.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_ref_clk_in.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_bclk_invert.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_fsync_duty_1.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_fsync_duty_2.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_fsync_duty_32.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_fsync_invert.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_manual_intosc_clkin.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_manual_intosc_only.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_manual_refclk_clkin.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_manual_refclk_only.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_name_output_clocks.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_smart_clkin_only_intosc.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_smart_clkin_only_refclk.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_smart_intosc.dtso
 create mode 100644 drivers/clk/cirrus/kunit_clk_cs2600_smart_refclk.dtso
drivers/clk/cirrus/Kconfig +13 -0
index 5674d6554f96..a6807cab4b94 100644
@@ -8,3 +8,16 @@ config COMMON_CLK_CS2600
 	help
 	  If you say yes here you get support for the CS2600 clock synthesizer
 	  and multiplier.
+
+config COMMON_CLK_CS2600_KUNIT_TEST
+	tristate "KUnit test for CS2600 clock driver" if !KUNIT_ALL_TESTS
+	depends on COMMON_CLK_CS2600
+	depends on KUNIT
+	depends on REGULATOR
+	depends on REGULATOR_FIXED_VOLTAGE
+	default KUNIT_ALL_TESTS
+	help
+		Build the KUnit tests for the Cirrus Logic CS2600
+		clock driver.
+
+		If unsure say N.
drivers/clk/cirrus/Makefile +24 -0
index 182612dbb96a..be59a601d936 100644
@@ -1,3 +1,27 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_COMMON_CLK_CS2600)		+= clk-cs2600.o
+
+clk-cs2600_kunit-y :=		clk-cs2600-kunit.o \
+				kunit_clk_cs2600_assigned.dtbo.o \
+				kunit_clk_cs2600_aux1_out_clk_in.dtbo.o \
+				kunit_clk_cs2600_aux1_out_clk_in_missing.dtbo.o \
+				kunit_clk_cs2600_aux1_out_freq_unlock.dtbo.o \
+				kunit_clk_cs2600_aux1_out_phase_unlock.dtbo.o \
+				kunit_clk_cs2600_aux1_out_ref_clk_in.dtbo.o \
+				kunit_clk_cs2600_bclk_invert.dtbo.o \
+				kunit_clk_cs2600_fsync_duty_1.dtbo.o \
+				kunit_clk_cs2600_fsync_duty_2.dtbo.o \
+				kunit_clk_cs2600_fsync_duty_32.dtbo.o \
+				kunit_clk_cs2600_fsync_invert.dtbo.o \
+				kunit_clk_cs2600_manual_intosc_clkin.dtbo.o \
+				kunit_clk_cs2600_manual_intosc_only.dtbo.o \
+				kunit_clk_cs2600_manual_refclk_clkin.dtbo.o \
+				kunit_clk_cs2600_manual_refclk_only.dtbo.o \
+				kunit_clk_cs2600_name_output_clocks.dtbo.o \
+				kunit_clk_cs2600_smart_clkin_only_intosc.dtbo.o \
+				kunit_clk_cs2600_smart_clkin_only_refclk.dtbo.o \
+				kunit_clk_cs2600_smart_intosc.dtbo.o \
+				kunit_clk_cs2600_smart_refclk.dtbo.o
+
+obj-$(CONFIG_COMMON_CLK_CS2600_KUNIT_TEST)	+= clk-cs2600_kunit.o
drivers/clk/cirrus/clk-cs2600-kunit.c new file +2495 -0

2,495 lines not on this page · raw message

drivers/clk/cirrus/clk-cs2600-kunit.h new file +18 -0
new file mode 100644
index 000000000000..2387610cc23b
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * KUnit test for the Cirrus Logic CS2600 clock driver.
+ *
+ * Copyright (C) 2026 Cirrus Logic, Inc. and
+ *                    Cirrus Logic International Semiconductor Ltd.
+ */
+
+#define CS2600_KUNIT_PLLOUT_RENAME	"renamed_pll"
+#define CS2600_KUNIT_CLKOUT_RENAME	"renamed_clk"
+#define CS2600_KUNIT_BCLK_RENAME	"renamed_bs"
+#define CS2600_KUNIT_FSYNC_RENAME	"renamed_fs"
+
+#define CS2600_KUNIT_CLKOUT_ASSIGNED	6144000
+#define CS2600_KUNIT_BCLK_ASSIGNED	1536000
+#define CS2600_KUNIT_FSYNC_ASSIGNED	96000
+#define CS2600_KUNIT_BCLK_DIV_INDEX	3
+#define CS2600_KUNIT_FSYNC_DIV_INDEX	2
drivers/clk/cirrus/kunit_clk_cs2600.dtsi new file +40 -0
new file mode 100644
index 000000000000..9fd11d322837
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/cirrus,cs2600-clock.h>
+
+&{/} {
+	dummy_vreg: VDD-DUMMY {
+		compatible = "regulator-fixed";
+		regulator-name = "vdd";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
+
+	cs2600_kunit_refclk: cs2600_kunit_refclk {
+		compatible = "cirrus,cs2600-kunit-refclk";
+		#clock-cells = <0>;
+	};
+
+	cs2600_kunit_clkin: cs2600_kunit_clkin {
+		compatible = "cirrus,cs2600-kunit-clkin";
+		#clock-cells = <0>;
+	};
+
+	cs2600_kunit_i2c: cs2600-kunit-i2c {
+		compatible = "cirrus,cs2600-kunit-i2c";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		clock-frequency = <400000>;
+	};
+
+	cs2600_test_consumer: cs2600-test-consumer {
+		compatible = "cirrus,cs2600-kunit-consumer";
+		clocks = <&cs2600 CS2600_PLL_OUT>, <&cs2600 CS2600_CLK_OUT>,
+			 <&cs2600 CS2600_BCLK_OUT>, <&cs2600 CS2600_FSYNC_OUT>;
+		clock-names = "pllout", "clkout", "bclk", "fsync";
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_assigned.dtso new file +27 -0
new file mode 100644
index 000000000000..fd2d7b3b5b10
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+#include "clk-cs2600-kunit.h"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		vdd-supply = <&dummy_vreg>;
+	};
+};
+
+&cs2600_test_consumer {
+	assigned-clocks = <&cs2600 CS2600_CLK_OUT>,
+			  <&cs2600 CS2600_BCLK_OUT>,
+			  <&cs2600 CS2600_FSYNC_OUT>;
+	assigned-clock-rates = <CS2600_KUNIT_CLKOUT_ASSIGNED
+				CS2600_KUNIT_BCLK_ASSIGNED
+				CS2600_KUNIT_FSYNC_ASSIGNED>;
+};
+
drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_clk_in.dtso new file +17 -0
new file mode 100644
index 000000000000..aeaa3740d6fd
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		vdd-supply = <&dummy_vreg>;
+		cirrus,aux1-output-source = "clk_in";
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_clk_in_missing.dtso new file +17 -0
new file mode 100644
index 000000000000..809e55f22d4d
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		vdd-supply = <&dummy_vreg>;
+		cirrus,aux1-output-source = "clkin_missing";
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_freq_unlock.dtso new file +17 -0
new file mode 100644
index 000000000000..2d295792ef68
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		vdd-supply = <&dummy_vreg>;
+		cirrus,aux1-output-source = "freq_unlock";
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_phase_unlock.dtso new file +17 -0
new file mode 100644
index 000000000000..5c242cd06707
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		vdd-supply = <&dummy_vreg>;
+		cirrus,aux1-output-source = "phase_unlock";
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_aux1_out_ref_clk_in.dtso new file +17 -0
new file mode 100644
index 000000000000..4e9205789ef4
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		vdd-supply = <&dummy_vreg>;
+		cirrus,aux1-output-source = "ref_clk_in";
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_bclk_invert.dtso new file +17 -0
new file mode 100644
index 000000000000..c3d89d7c8020
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		vdd-supply = <&dummy_vreg>;
+		cirrus,bclk-invert;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_fsync_duty_1.dtso new file +17 -0
new file mode 100644
index 000000000000..f3e175c93c65
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		vdd-supply = <&dummy_vreg>;
+		cirrus,fsync-duty-cycles = <1>;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_fsync_duty_2.dtso new file +17 -0
new file mode 100644
index 000000000000..11f308e49ff2
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		vdd-supply = <&dummy_vreg>;
+		cirrus,fsync-duty-cycles = <2>;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_fsync_duty_32.dtso new file +17 -0
new file mode 100644
index 000000000000..00656af862d7
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		vdd-supply = <&dummy_vreg>;
+		cirrus,fsync-duty-cycles = <32>;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_fsync_invert.dtso new file +17 -0
new file mode 100644
index 000000000000..d85b5d851266
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		vdd-supply = <&dummy_vreg>;
+		cirrus,fsync-invert;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_manual_intosc_clkin.dtso new file +17 -0
new file mode 100644
index 000000000000..859b28daf25c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>, <&cs2600_kunit_clkin>;
+		clock-names = "ref_clk_in", "clk_in";
+		cirrus,internal-oscillator;
+		vdd-supply = <&dummy_vreg>;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_manual_intosc_only.dtso new file +17 -0
new file mode 100644
index 000000000000..dba29f398774
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		cirrus,internal-oscillator;
+		vdd-supply = <&dummy_vreg>;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_manual_refclk_clkin.dtso new file +16 -0
new file mode 100644
index 000000000000..5af4eb45fee0
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>, <&cs2600_kunit_clkin>;
+		clock-names = "ref_clk_in", "clk_in";
+		vdd-supply = <&dummy_vreg>;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_manual_refclk_only.dtso new file +16 -0
new file mode 100644
index 000000000000..dc2bf6f4a90a
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>;
+		clock-names = "ref_clk_in";
+		vdd-supply = <&dummy_vreg>;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_name_output_clocks.dtso new file +23 -0
new file mode 100644
index 000000000000..d05a69512bc6
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "clk-cs2600-kunit.h"
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>, <&cs2600_kunit_clkin>;
+		clock-names = "ref_clk_in", "clk_in";
+		vdd-supply = <&dummy_vreg>;
+
+		clock-output-names =
+			CS2600_KUNIT_PLLOUT_RENAME,
+			CS2600_KUNIT_CLKOUT_RENAME,
+			CS2600_KUNIT_BCLK_RENAME,
+			CS2600_KUNIT_FSYNC_RENAME;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_smart_clkin_only_intosc.dtso new file +19 -0
new file mode 100644
index 000000000000..76fe2e5a01c9
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>, <&cs2600_kunit_clkin>;
+		clock-names = "ref_clk_in", "clk_in";
+		cirrus,internal-oscillator;
+		cirrus,smart-mode;
+		cirrus,smart-mode-clkin-only;
+		vdd-supply = <&dummy_vreg>;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_smart_clkin_only_refclk.dtso new file +18 -0
new file mode 100644
index 000000000000..83e1b57deeaa
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>, <&cs2600_kunit_clkin>;
+		clock-names = "ref_clk_in", "clk_in";
+		cirrus,smart-mode;
+		cirrus,smart-mode-clkin-only;
+		vdd-supply = <&dummy_vreg>;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_smart_intosc.dtso new file +18 -0
new file mode 100644
index 000000000000..893406b49f1c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>, <&cs2600_kunit_clkin>;
+		clock-names = "ref_clk_in", "clk_in";
+		cirrus,internal-oscillator;
+		cirrus,smart-mode;
+		vdd-supply = <&dummy_vreg>;
+	};
+};
drivers/clk/cirrus/kunit_clk_cs2600_smart_refclk.dtso new file +17 -0
new file mode 100644
index 000000000000..6f46834cd2de
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+/plugin/;
+
+#include "kunit_clk_cs2600.dtsi"
+
+&cs2600_kunit_i2c {
+	cs2600: cs2600@2c {
+		compatible = "cirrus,cs2600";
+		reg = <0x2c>;
+		#clock-cells = <1>;
+		clocks = <&cs2600_kunit_refclk>, <&cs2600_kunit_clkin>;
+		clock-names = "ref_clk_in", "clk_in";
+		cirrus,smart-mode;
+		vdd-supply = <&dummy_vreg>;
+	};
+};
Signature · 1 line
2.47.3
#11 lore raw

Re: [PATCH v6 3/3] clk: cs2600: Add KUnit test for CS2600 driver

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider: - [Medium] 32-bit integer overflow in the calculation of `fdelta` tightens the acceptable error bounds…

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [Medium] 32-bit integer overflow in the calculation of fdelta tightens the acceptable error bounds, potentially causing false test failures.
- [Medium] clk_prepare_enable_kunit() is repeatedly invoked inside loops, bypassing hardware enable paths after the first iteration and accumulating redundant cleanup actions.
- [Medium] The test verifies the ratio slot using a stale copy of the PLL configuration registers read prior to clk_set_rate().
- [Low] Evaluating a device tree node pointer value after dropping its reference with of_node_put().
- [Low] The Kconfig entry for COMMON_CLK_CS2600_KUNIT_TEST fails to select DTC, leading to potential build failures.
- [Low] The mock I2C transfer function cs2600_kunit_i2c_xfer unconditionally accesses message buffers without validating message count or length. --

commit da440c9cd81c522da5fe16912468ea26cbaf9db1

Author: Richard Fitzgerald <[email protected]>
Date:   Tue Jun 30 15:55:49 2026 +0000

clk: cs2600: Add KUnit test for CS2600 driver

This patch adds a KUnit test for the CS2600 clock driver using a mock I2C host driver and regmap to partially emulate the hardware. The test relies on hardcoded tables to validate the frequency ratio and PLL configuration settings, and uses devicetree overlays to validate different hardware properties.

Quoted 6 lines
drivers/clk/cirrus/Kconfig +0 -0
index 5674d6554f966..a6807cab4b945 100644
@@ -8,3 +8,16 @@ config COMMON_CLK_CS2600
 	help

drivers/clk/cirrus/Kconfig config COMMON_CLK_CS2600 · line 18

Quoted 10 lines
drivers/clk/cirrus/Kconfig +8 -0
 	  If you say yes here you get support for the CS2600 clock synthesizer
 	  and multiplier.
+
+config COMMON_CLK_CS2600_KUNIT_TEST
+	tristate "KUnit test for CS2600 clock driver" if !KUNIT_ALL_TESTS
+	depends on COMMON_CLK_CS2600
+	depends on KUNIT
+	depends on REGULATOR
+	depends on REGULATOR_FIXED_VOLTAGE
+	default KUNIT_ALL_TESTS

[Severity: Low]
Does this Kconfig entry need to select DTC? The test's Makefile builds Device Tree overlays (.dtbo files), which could cause build failures if CONFIG_DTC is disabled.

Quoted 5 lines
drivers/clk/cirrus/Kconfig +5 -0
+	help
+		Build the KUnit tests for the Cirrus Logic CS2600
+		clock driver.
+
+		If unsure say N.

[ ... ]

Quoted 5 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +0 -0
new file mode 100644
index 0000000000000..9631801ac553b

[ ... ]

Quoted 13 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +13 -0
+static void cs2600_kunit_synth_ratio(struct kunit *test)
+{
+	const struct cs2600_kunit_params *params = test->param_value;
+	const struct cs2600_kunit_ratio_config *ratios = params->s_ratios;
+	struct cs2600_kunit_priv *priv = test->priv;
+	unsigned int ratio_sel, pll_cfg2, ratio;
+	int i;
+
+	KUNIT_ASSERT_NOT_NULL(test, ratios);
+
+	for (i = 0; ratios[i].clk_out != 0; i++) {
+		/* 6 MHz is out-of-range for some input clocks */
+		if (ratios[i].r == 0) {

drivers/clk/cirrus/clk-cs2600-kunit.c line 968

Quoted 10 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +10 -0
+			KUNIT_ASSERT_EQ(test, ratios[i].clk_out, 6000000);
+			continue;
+		}
+
+		KUNIT_EXPECT_EQ_MSG(test, 0,
+				    clk_set_rate(priv->clkout, ratios[i].clk_out),
+				    "clk_out:%u s:%#x\n",
+				    ratios[i].clk_out, ratios[i].r);
+		KUNIT_EXPECT_EQ_MSG(test, 0,
+				    clk_prepare_enable_kunit(test, priv->clkout),

[Severity: Medium]
Is it intentional to call clk_prepare_enable_kunit() in every loop iteration without a matching disable? This will repeatedly increment the clock's reference count and accumulate deferred cleanup actions, which might bypass the actual hardware enable paths on subsequent loop iterations. This also appears to happen in cs2600_kunit_mult_ratio() and cs2600_kunit_smart_ratio().

Quoted 4 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +4 -0
+				    "clk_out:%u s:%#x\n",
+				    ratios[i].clk_out, ratios[i].r);
+
+		KUNIT_ASSERT_EQ(test, 0, regmap_read(priv->regmap, CS2600_PLL_CFG1, &ratio_sel));

[ ... ]

Quoted 8 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +8 -0
+static void cs2600_kunit_change_parent_same_clkout(struct kunit *test)
+{
+	const struct cs2600_kunit_params *params = test->param_value;
+	struct cs2600_kunit_priv *priv = test->priv;
+	struct clk *refclk = clk_hw_get_clk_kunit(test, priv->refclk_hw, NULL);
+	struct clk *clkin = clk_hw_get_clk_kunit(test, priv->clkin_hw, NULL);
+	unsigned int ratio_sel, pll_cfg1, pll_cfg2, pll_cfg3, ratio;
+	int i;

[ ... ]

drivers/clk/cirrus/clk-cs2600-kunit.c line 1272

Quoted 6 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +6 -0
+		/* clk_set_rate() should recalculate based on new parent frequency */
+		KUNIT_EXPECT_EQ_MSG(test, 0,
+				    clk_set_rate(priv->clkout, params->m_ratios[i].clk_out),
+				    "clk_out:%u\n", params->m_ratios[i].clk_out);
+
+		ratio_sel = FIELD_GET(CS2600_M_RATIO_SEL_MASK, pll_cfg2);

[Severity: Medium]
Does pll_cfg2 contain the updated register state here? The test appears to validate the ratio slot using a stale copy of the PLL configuration registers (pll_cfg2, and later pll_cfg1) read prior to the clk_set_rate() call, which might fail to verify the newly updated hardware state.

Quoted 21 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +21 -0
+		ratio = cs2600_kunit_get_ratio_val(test, ratio_sel);
+		KUNIT_EXPECT_EQ_MSG(test, params->m_ratios[i].r, ratio,
+				    "clk_out:%u\n", params->m_ratios[i].clk_out);
+
+		/*
+		 * Change back to default parent and it should be synth mode again
+		 */
+		KUNIT_EXPECT_EQ(test, 0, clk_set_parent(priv->pllout, refclk));
+
+		KUNIT_ASSERT_EQ(test, 0, regmap_read(priv->regmap, CS2600_PLL_CFG1, &pll_cfg1));
+		KUNIT_ASSERT_EQ(test, 0, regmap_read(priv->regmap, CS2600_PLL_CFG2, &pll_cfg2));
+		KUNIT_ASSERT_EQ(test, 0, regmap_read(priv->regmap, CS2600_PLL_CFG3, &pll_cfg3));
+
+		KUNIT_EXPECT_EQ(test, 0, FIELD_GET(CS2600_PLL_MODE_SEL, pll_cfg2));
+		KUNIT_EXPECT_EQ(test, FIELD_GET(CS2600_S_RATIO_SEL_MASK, pll_cfg1),
+				FIELD_GET(CS2600_M_RATIO_SEL_MASK, pll_cfg2));
+
+		/* Ratio should have changed if reference frequency has changed */
+		if (clk_get_rate(refclk) != clk_get_rate(clkin)) {
+			ratio_sel = FIELD_GET(CS2600_S_RATIO_SEL_MASK, pll_cfg1);
+			ratio = cs2600_kunit_get_ratio_val(test, ratio_sel);

drivers/clk/cirrus/clk-cs2600-kunit.c line 1303

Quoted 10 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +10 -0
+			KUNIT_EXPECT_NE_MSG(test, params->m_ratios[i].r, ratio,
+					    "clk_out:%u\n", params->s_ratios[i].clk_out);
+		}
+
+		/* clk_set_rate() should recalculate based on new parent frequency */
+		KUNIT_EXPECT_EQ_MSG(test, 0,
+				    clk_set_rate(priv->clkout, params->s_ratios[i].clk_out),
+				    "clk_out:%u\n", params->s_ratios[i].clk_out);
+
+		ratio_sel = FIELD_GET(CS2600_S_RATIO_SEL_MASK, pll_cfg1);

[Severity: Medium]
Similar to the issue above, is pll_cfg1 stale here because it was read before clk_set_rate() updated the hardware?

Quoted 5 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +5 -0
+		ratio = cs2600_kunit_get_ratio_val(test, ratio_sel);
+		KUNIT_EXPECT_EQ_MSG(test, params->s_ratios[i].r, ratio,
+				    "clk_out:%u\n", params->m_ratios[i].clk_out);
+	}
+}

[ ... ]

Quoted 7 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +7 -0
+static void cs2600_kunit_bclk_divider(struct kunit *test)
+{
+	struct cs2600_kunit_priv *priv = test->priv;
+	unsigned int out_cfg1, bclk_div, fdelta;
+	int i;
+
+	for (i = 0; cs2600_kunit_bclk_divs[i].pll_out != 0; i++) {

[ ... ]

drivers/clk/cirrus/clk-cs2600-kunit.c line 1431

Quoted 5 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +5 -0
+		/*
+		 * Actual reported frequency can be slightly out because of
+		 * PLL resolution, but should be within worst-case PPM.
+		 */
+		fdelta = (cs2600_kunit_bclk_divs[i].bf_out * CS2600_20_12_PPM) >> 20;

[Severity: Medium]
Can this multiplication overflow a 32-bit integer? The maximum bf_out value is 24,576,000, and CS2600_20_12_PPM is 224, making their product 5,505,024,000, which exceeds the 32-bit UINT_MAX. This could evaluate to an artificially tight tolerance range, potentially causing false test failures.

Quoted 5 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +5 -0
+		KUNIT_EXPECT_GE_MSG(test, clk_get_rate(priv->bclk),
+				    cs2600_kunit_bclk_divs[i].bf_out - fdelta,
+				    "clk_out:%u bclk:%u\n",
+				    cs2600_kunit_bclk_divs[i].pll_out,
+				    cs2600_kunit_bclk_divs[i].bf_out);

[ ... ]

drivers/clk/cirrus/clk-cs2600-kunit.c line 1647

Quoted 10 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +10 -0
+static int cs2600_kunit_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+	struct cs2600_kunit_priv *priv = i2c_get_adapdata(adap);
+	unsigned int addr, val;
+	__be16 *bebuf;
+	int ret;
+
+	/* First word of first message is always the register address */
+	bebuf = (__force __be16 *)msgs[0].buf;
+	addr = be16_to_cpu(bebuf[0]);

[Severity: Low]
Could this unconditionally dereference memory? The code appears to access msgs[0].buf without first verifying that num >= 1 and msgs[0].len >= sizeof(__be16).

Quoted 10 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +10 -0
+
+	/* A second word in the first message is a data write */
+	if (msgs[0].len >= 2 * sizeof(__be16)) {
+		val = be16_to_cpu(bebuf[1]);
+		ret = cs2600_kunit_i2c_write(priv, addr, val);
+		if (ret < 0)
+			return ret;
+
+		return 1;
+	}

[ ... ]

Quoted 5 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +5 -0
+static int cs2600_kunit_case_common_init(struct kunit *test)
+{
+	const struct cs2600_kunit_params *params = test->param_value;
+	struct cs2600_kunit_priv *priv;
+	struct device_node *np;

[ ... ]

drivers/clk/cirrus/clk-cs2600-kunit.c line 1885

Quoted 4 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +4 -0
+	/* Wait for probes if the test case init already loaded the overlay */
+	np = of_find_node_by_name(NULL, "cs2600-test-consumer");
+	of_node_put(np);
+	if (!IS_ERR_OR_NULL(np))

[Severity: Low]
Is it safe to evaluate np with IS_ERR_OR_NULL() after its reference has been dropped by of_node_put()? Although IS_ERR_OR_NULL() only performs address evaluation, relying on a conceptually dangling pointer value might lead to unintended behavior.

Quoted 4 lines
drivers/clk/cirrus/clk-cs2600-kunit.c new file +4 -0
+		cs2600_kunit_wait_for_probes(test);
+
+	return 0;
+}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
#12 lore raw

Re: [PATCH v6 3/3] clk: cs2600: Add KUnit test for CS2600 driver

In reply to Richard Fitzgerald

Hi Richard,

Quoted 8 lines · Richard Fitzgerald

+++ b/drivers/clk/cirrus/Kconfig

drivers/clk/cirrus/Kconfig +3 -0
@@ -8,3 +8,16 @@ config COMMON_CLK_CS2600
 	help
 	  If you say yes here you get support for the CS2600 clock synthesizer
 	  and multiplier.
+
+config COMMON_CLK_CS2600_KUNIT_TEST
+	tristate "KUnit test for CS2600 clock driver" if !KUNIT_ALL_TESTS

drivers/clk/cirrus/Kconfig config COMMON_CLK_CS2600 · line 23

Quoted 10 lines
drivers/clk/cirrus/Kconfig +10 -0
+	depends on COMMON_CLK_CS2600
+	depends on KUNIT
+	depends on REGULATOR
+	depends on REGULATOR_FIXED_VOLTAGE
+	default KUNIT_ALL_TESTS
+	help
+		Build the KUnit tests for the Cirrus Logic CS2600
+		clock driver.
+
+		If unsure say N.

Could you format your kconfig help text with a tab + 2 spaces, as the kconfig documentation shows in its example?

In fact, your previous patch in this series (adding COMMON_CLK_CS2600) formatted it correctly.

- Julian Braha