diff --git a/CREDITS b/CREDITS index 84793a967a0b..c8a67ab8efe5 100644 --- a/CREDITS +++ b/CREDITS @@ -4085,6 +4085,10 @@ S: 4226 Landgreen Street S: Rockville, Maryland 20853 S: USA +N: Michael Turquette +E: mturquette@baylibre.com +D: common clk maintenance + N: Stephen Tweedie E: sct@redhat.com P: 1024/E7A417AD E2 FE A4 20 34 EC ED FC 7D 7E 67 8D E0 31 D1 69 diff --git a/Documentation/devicetree/bindings/clock/eswin,eic7700-hspcrg.yaml b/Documentation/devicetree/bindings/clock/eswin,eic7700-hspcrg.yaml new file mode 100644 index 000000000000..43df689ae647 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/eswin,eic7700-hspcrg.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/eswin,eic7700-hspcrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ESWIN EIC7700 HSP Clock and Reset Generator + +maintainers: + - Xuyang Dong + +description: + Clock and reset generator for the ESWIN EIC7700 HSP (high-speed peripherals). + +properties: + compatible: + const: eswin,eic7700-hspcrg + + reg: + maxItems: 1 + + clocks: + items: + - description: HSP configuration top clock + - description: MMC top clock + - description: SATA top clock + + clock-names: + items: + - const: cfg + - const: mmc + - const: sata + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + - | + clock-controller@50440000 { + compatible = "eswin,eic7700-hspcrg"; + reg = <0x50440000 0x2000>; + clocks = <&clock 171>, <&clock 254>, <&clock 187>; + clock-names = "cfg", "mmc", "sata"; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/keystone-gate.txt b/Documentation/devicetree/bindings/clock/keystone-gate.txt deleted file mode 100644 index 43f6fb6c9392..000000000000 --- a/Documentation/devicetree/bindings/clock/keystone-gate.txt +++ /dev/null @@ -1,27 +0,0 @@ -Binding for Keystone gate control driver which uses PSC controller IP. - -This binding uses the common clock binding[1]. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt - -Required properties: -- compatible : shall be "ti,keystone,psc-clock". -- #clock-cells : from common clock binding; shall be set to 0. -- clocks : parent clock phandle -- reg : psc control and domain address address space -- reg-names : psc control and domain registers -- domain-id : psc domain id needed to check the transition state register - -Optional properties: -- clock-output-names : From common clock binding to override the - default output clock name -Example: - clkusb: clkusb { - #clock-cells = <0>; - compatible = "ti,keystone,psc-clock"; - clocks = <&chipclk16>; - clock-output-names = "usb"; - reg = <0x02350008 0xb00>, <0x02350000 0x400>; - reg-names = "control", "domain"; - domain-id = <0>; - }; diff --git a/Documentation/devicetree/bindings/clock/mediatek,mt8173-mfgtop.yaml b/Documentation/devicetree/bindings/clock/mediatek,mt8173-mfgtop.yaml new file mode 100644 index 000000000000..03db1ee9e594 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mediatek,mt8173-mfgtop.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/clock/mediatek,mt8173-mfgtop.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MediaTek MT8173 MFG TOP controller + +maintainers: + - AngeloGioacchino Del Regno + +description: + The MFG TOP glue layer controls various signals going to the MFG (GPU) + block on the MT8173. + +properties: + compatible: + const: mediatek,mt8173-mfgtop + + reg: + maxItems: 1 + + clocks: + maxItems: 4 + + clock-names: + items: + - const: sys + - const: mem + - const: core + - const: clk26m + + power-domains: + maxItems: 1 + + '#clock-cells': + const: 1 + + '#power-domain-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - power-domains + - '#clock-cells' + - '#power-domain-cells' + +additionalProperties: false + +examples: + - | + #include + #include + + clock-controller@13fff000 { + compatible = "mediatek,mt8173-mfgtop"; + reg = <0x13fff000 0x1000>; + clocks = <&topckgen CLK_TOP_AXI_MFG_IN_SEL>, + <&topckgen CLK_TOP_MEM_MFG_IN_SEL>, + <&topckgen CLK_TOP_MFG_SEL>, + <&clk26m>; + clock-names = "sys", "mem", "core", "clk26m"; + power-domains = <&spm MT8173_POWER_DOMAIN_MFG>; + #clock-cells = <1>; + #power-domain-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/clock/mediatek,pericfg.yaml b/Documentation/devicetree/bindings/clock/mediatek,pericfg.yaml index b98cf45efe2f..f0c8df4d10c8 100644 --- a/Documentation/devicetree/bindings/clock/mediatek,pericfg.yaml +++ b/Documentation/devicetree/bindings/clock/mediatek,pericfg.yaml @@ -65,7 +65,7 @@ examples: - | pericfg@10003000 { - compatible = "mediatek,mt7623-pericfg", "mediatek,mt2701-pericfg", "syscon"; + compatible = "mediatek,mt7623-pericfg", "mediatek,mt2701-pericfg", "syscon"; reg = <0x10003000 0x1000>; #clock-cells = <1>; #reset-cells = <1>; diff --git a/Documentation/devicetree/bindings/clock/silabs,si544.yaml b/Documentation/devicetree/bindings/clock/silabs,si544.yaml index f87e71867108..218f5abdef26 100644 --- a/Documentation/devicetree/bindings/clock/silabs,si544.yaml +++ b/Documentation/devicetree/bindings/clock/silabs,si544.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/clock/silabs,si544.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Silicon Labs SI514/SI544 clock generator +title: Silicon Labs SI514/SI544/SI549 clock generator maintainers: - Mike Looijmans @@ -13,8 +13,9 @@ description: > Silicon Labs 514/544 programmable I2C clock generator. Details about the device can be found in the datasheet: - https://www.silabs.com/Support%20Documents/TechnicalDocs/si514.pdf - https://www.silabs.com/documents/public/data-sheets/si544-datasheet.pdf + https://www.skyworksinc.com/-/media/Skyworks/SL/documents/public/data-sheets/Si514.pdf + https://www.skyworksinc.com/-/media/Skyworks/SL/documents/public/data-sheets/si544-datasheet.pdf + https://www.skyworksinc.com/-/media/Skyworks/SL/documents/public/data-sheets/si549-datasheet.pdf properties: compatible: @@ -23,6 +24,9 @@ properties: - silabs,si544a - silabs,si544b - silabs,si544c + - silabs,si549a + - silabs,si549b + - silabs,si549c reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/clock/st,stm32mp21-rcc.yaml b/Documentation/devicetree/bindings/clock/st,stm32mp21-rcc.yaml index 4368063c6709..e4d64d4b63c6 100644 --- a/Documentation/devicetree/bindings/clock/st,stm32mp21-rcc.yaml +++ b/Documentation/devicetree/bindings/clock/st,stm32mp21-rcc.yaml @@ -125,75 +125,75 @@ examples: reg = <0x44200000 0x10000>; #clock-cells = <1>; #reset-cells = <1>; - clocks = <&scmi_clk CK_SCMI_HSE>, - <&scmi_clk CK_SCMI_HSI>, - <&scmi_clk CK_SCMI_MSI>, - <&scmi_clk CK_SCMI_LSE>, - <&scmi_clk CK_SCMI_LSI>, - <&scmi_clk CK_SCMI_HSE_DIV2>, - <&scmi_clk CK_SCMI_ICN_HS_MCU>, - <&scmi_clk CK_SCMI_ICN_LS_MCU>, - <&scmi_clk CK_SCMI_ICN_SDMMC>, - <&scmi_clk CK_SCMI_ICN_DDR>, - <&scmi_clk CK_SCMI_ICN_DISPLAY>, - <&scmi_clk CK_SCMI_ICN_HSL>, - <&scmi_clk CK_SCMI_ICN_NIC>, - <&scmi_clk CK_SCMI_FLEXGEN_07>, - <&scmi_clk CK_SCMI_FLEXGEN_08>, - <&scmi_clk CK_SCMI_FLEXGEN_09>, - <&scmi_clk CK_SCMI_FLEXGEN_10>, - <&scmi_clk CK_SCMI_FLEXGEN_11>, - <&scmi_clk CK_SCMI_FLEXGEN_12>, - <&scmi_clk CK_SCMI_FLEXGEN_13>, - <&scmi_clk CK_SCMI_FLEXGEN_14>, - <&scmi_clk CK_SCMI_FLEXGEN_16>, - <&scmi_clk CK_SCMI_FLEXGEN_17>, - <&scmi_clk CK_SCMI_FLEXGEN_18>, - <&scmi_clk CK_SCMI_FLEXGEN_19>, - <&scmi_clk CK_SCMI_FLEXGEN_20>, - <&scmi_clk CK_SCMI_FLEXGEN_21>, - <&scmi_clk CK_SCMI_FLEXGEN_22>, - <&scmi_clk CK_SCMI_FLEXGEN_23>, - <&scmi_clk CK_SCMI_FLEXGEN_24>, - <&scmi_clk CK_SCMI_FLEXGEN_25>, - <&scmi_clk CK_SCMI_FLEXGEN_26>, - <&scmi_clk CK_SCMI_FLEXGEN_27>, - <&scmi_clk CK_SCMI_FLEXGEN_29>, - <&scmi_clk CK_SCMI_FLEXGEN_30>, - <&scmi_clk CK_SCMI_FLEXGEN_31>, - <&scmi_clk CK_SCMI_FLEXGEN_33>, - <&scmi_clk CK_SCMI_FLEXGEN_36>, - <&scmi_clk CK_SCMI_FLEXGEN_37>, - <&scmi_clk CK_SCMI_FLEXGEN_38>, - <&scmi_clk CK_SCMI_FLEXGEN_39>, - <&scmi_clk CK_SCMI_FLEXGEN_40>, - <&scmi_clk CK_SCMI_FLEXGEN_41>, - <&scmi_clk CK_SCMI_FLEXGEN_42>, - <&scmi_clk CK_SCMI_FLEXGEN_43>, - <&scmi_clk CK_SCMI_FLEXGEN_44>, - <&scmi_clk CK_SCMI_FLEXGEN_45>, - <&scmi_clk CK_SCMI_FLEXGEN_46>, - <&scmi_clk CK_SCMI_FLEXGEN_47>, - <&scmi_clk CK_SCMI_FLEXGEN_48>, - <&scmi_clk CK_SCMI_FLEXGEN_50>, - <&scmi_clk CK_SCMI_FLEXGEN_51>, - <&scmi_clk CK_SCMI_FLEXGEN_52>, - <&scmi_clk CK_SCMI_FLEXGEN_53>, - <&scmi_clk CK_SCMI_FLEXGEN_54>, - <&scmi_clk CK_SCMI_FLEXGEN_55>, - <&scmi_clk CK_SCMI_FLEXGEN_56>, - <&scmi_clk CK_SCMI_FLEXGEN_57>, - <&scmi_clk CK_SCMI_FLEXGEN_58>, - <&scmi_clk CK_SCMI_FLEXGEN_61>, - <&scmi_clk CK_SCMI_FLEXGEN_62>, - <&scmi_clk CK_SCMI_FLEXGEN_63>, - <&scmi_clk CK_SCMI_ICN_APB1>, - <&scmi_clk CK_SCMI_ICN_APB2>, - <&scmi_clk CK_SCMI_ICN_APB3>, - <&scmi_clk CK_SCMI_ICN_APB4>, - <&scmi_clk CK_SCMI_ICN_APB5>, - <&scmi_clk CK_SCMI_ICN_APBDBG>, - <&scmi_clk CK_SCMI_TIMG1>, - <&scmi_clk CK_SCMI_TIMG2>; + clocks = <&scmi_clk CK_SCMI_HSE>, + <&scmi_clk CK_SCMI_HSI>, + <&scmi_clk CK_SCMI_MSI>, + <&scmi_clk CK_SCMI_LSE>, + <&scmi_clk CK_SCMI_LSI>, + <&scmi_clk CK_SCMI_HSE_DIV2>, + <&scmi_clk CK_SCMI_ICN_HS_MCU>, + <&scmi_clk CK_SCMI_ICN_LS_MCU>, + <&scmi_clk CK_SCMI_ICN_SDMMC>, + <&scmi_clk CK_SCMI_ICN_DDR>, + <&scmi_clk CK_SCMI_ICN_DISPLAY>, + <&scmi_clk CK_SCMI_ICN_HSL>, + <&scmi_clk CK_SCMI_ICN_NIC>, + <&scmi_clk CK_SCMI_FLEXGEN_07>, + <&scmi_clk CK_SCMI_FLEXGEN_08>, + <&scmi_clk CK_SCMI_FLEXGEN_09>, + <&scmi_clk CK_SCMI_FLEXGEN_10>, + <&scmi_clk CK_SCMI_FLEXGEN_11>, + <&scmi_clk CK_SCMI_FLEXGEN_12>, + <&scmi_clk CK_SCMI_FLEXGEN_13>, + <&scmi_clk CK_SCMI_FLEXGEN_14>, + <&scmi_clk CK_SCMI_FLEXGEN_16>, + <&scmi_clk CK_SCMI_FLEXGEN_17>, + <&scmi_clk CK_SCMI_FLEXGEN_18>, + <&scmi_clk CK_SCMI_FLEXGEN_19>, + <&scmi_clk CK_SCMI_FLEXGEN_20>, + <&scmi_clk CK_SCMI_FLEXGEN_21>, + <&scmi_clk CK_SCMI_FLEXGEN_22>, + <&scmi_clk CK_SCMI_FLEXGEN_23>, + <&scmi_clk CK_SCMI_FLEXGEN_24>, + <&scmi_clk CK_SCMI_FLEXGEN_25>, + <&scmi_clk CK_SCMI_FLEXGEN_26>, + <&scmi_clk CK_SCMI_FLEXGEN_27>, + <&scmi_clk CK_SCMI_FLEXGEN_29>, + <&scmi_clk CK_SCMI_FLEXGEN_30>, + <&scmi_clk CK_SCMI_FLEXGEN_31>, + <&scmi_clk CK_SCMI_FLEXGEN_33>, + <&scmi_clk CK_SCMI_FLEXGEN_36>, + <&scmi_clk CK_SCMI_FLEXGEN_37>, + <&scmi_clk CK_SCMI_FLEXGEN_38>, + <&scmi_clk CK_SCMI_FLEXGEN_39>, + <&scmi_clk CK_SCMI_FLEXGEN_40>, + <&scmi_clk CK_SCMI_FLEXGEN_41>, + <&scmi_clk CK_SCMI_FLEXGEN_42>, + <&scmi_clk CK_SCMI_FLEXGEN_43>, + <&scmi_clk CK_SCMI_FLEXGEN_44>, + <&scmi_clk CK_SCMI_FLEXGEN_45>, + <&scmi_clk CK_SCMI_FLEXGEN_46>, + <&scmi_clk CK_SCMI_FLEXGEN_47>, + <&scmi_clk CK_SCMI_FLEXGEN_48>, + <&scmi_clk CK_SCMI_FLEXGEN_50>, + <&scmi_clk CK_SCMI_FLEXGEN_51>, + <&scmi_clk CK_SCMI_FLEXGEN_52>, + <&scmi_clk CK_SCMI_FLEXGEN_53>, + <&scmi_clk CK_SCMI_FLEXGEN_54>, + <&scmi_clk CK_SCMI_FLEXGEN_55>, + <&scmi_clk CK_SCMI_FLEXGEN_56>, + <&scmi_clk CK_SCMI_FLEXGEN_57>, + <&scmi_clk CK_SCMI_FLEXGEN_58>, + <&scmi_clk CK_SCMI_FLEXGEN_61>, + <&scmi_clk CK_SCMI_FLEXGEN_62>, + <&scmi_clk CK_SCMI_FLEXGEN_63>, + <&scmi_clk CK_SCMI_ICN_APB1>, + <&scmi_clk CK_SCMI_ICN_APB2>, + <&scmi_clk CK_SCMI_ICN_APB3>, + <&scmi_clk CK_SCMI_ICN_APB4>, + <&scmi_clk CK_SCMI_ICN_APB5>, + <&scmi_clk CK_SCMI_ICN_APBDBG>, + <&scmi_clk CK_SCMI_TIMG1>, + <&scmi_clk CK_SCMI_TIMG2>; }; ... diff --git a/Documentation/devicetree/bindings/clock/st,stm32mp25-rcc.yaml b/Documentation/devicetree/bindings/clock/st,stm32mp25-rcc.yaml index 1e3b5d218bb0..3b7925d2c175 100644 --- a/Documentation/devicetree/bindings/clock/st,stm32mp25-rcc.yaml +++ b/Documentation/devicetree/bindings/clock/st,stm32mp25-rcc.yaml @@ -135,85 +135,85 @@ examples: reg = <0x44200000 0x10000>; #clock-cells = <1>; #reset-cells = <1>; - clocks = <&scmi_clk CK_SCMI_HSE>, - <&scmi_clk CK_SCMI_HSI>, - <&scmi_clk CK_SCMI_MSI>, - <&scmi_clk CK_SCMI_LSE>, - <&scmi_clk CK_SCMI_LSI>, - <&scmi_clk CK_SCMI_HSE_DIV2>, - <&scmi_clk CK_SCMI_ICN_HS_MCU>, - <&scmi_clk CK_SCMI_ICN_LS_MCU>, - <&scmi_clk CK_SCMI_ICN_SDMMC>, - <&scmi_clk CK_SCMI_ICN_DDR>, - <&scmi_clk CK_SCMI_ICN_DISPLAY>, - <&scmi_clk CK_SCMI_ICN_HSL>, - <&scmi_clk CK_SCMI_ICN_NIC>, - <&scmi_clk CK_SCMI_ICN_VID>, - <&scmi_clk CK_SCMI_FLEXGEN_07>, - <&scmi_clk CK_SCMI_FLEXGEN_08>, - <&scmi_clk CK_SCMI_FLEXGEN_09>, - <&scmi_clk CK_SCMI_FLEXGEN_10>, - <&scmi_clk CK_SCMI_FLEXGEN_11>, - <&scmi_clk CK_SCMI_FLEXGEN_12>, - <&scmi_clk CK_SCMI_FLEXGEN_13>, - <&scmi_clk CK_SCMI_FLEXGEN_14>, - <&scmi_clk CK_SCMI_FLEXGEN_15>, - <&scmi_clk CK_SCMI_FLEXGEN_16>, - <&scmi_clk CK_SCMI_FLEXGEN_17>, - <&scmi_clk CK_SCMI_FLEXGEN_18>, - <&scmi_clk CK_SCMI_FLEXGEN_19>, - <&scmi_clk CK_SCMI_FLEXGEN_20>, - <&scmi_clk CK_SCMI_FLEXGEN_21>, - <&scmi_clk CK_SCMI_FLEXGEN_22>, - <&scmi_clk CK_SCMI_FLEXGEN_23>, - <&scmi_clk CK_SCMI_FLEXGEN_24>, - <&scmi_clk CK_SCMI_FLEXGEN_25>, - <&scmi_clk CK_SCMI_FLEXGEN_26>, - <&scmi_clk CK_SCMI_FLEXGEN_27>, - <&scmi_clk CK_SCMI_FLEXGEN_28>, - <&scmi_clk CK_SCMI_FLEXGEN_29>, - <&scmi_clk CK_SCMI_FLEXGEN_30>, - <&scmi_clk CK_SCMI_FLEXGEN_31>, - <&scmi_clk CK_SCMI_FLEXGEN_32>, - <&scmi_clk CK_SCMI_FLEXGEN_33>, - <&scmi_clk CK_SCMI_FLEXGEN_34>, - <&scmi_clk CK_SCMI_FLEXGEN_35>, - <&scmi_clk CK_SCMI_FLEXGEN_36>, - <&scmi_clk CK_SCMI_FLEXGEN_37>, - <&scmi_clk CK_SCMI_FLEXGEN_38>, - <&scmi_clk CK_SCMI_FLEXGEN_39>, - <&scmi_clk CK_SCMI_FLEXGEN_40>, - <&scmi_clk CK_SCMI_FLEXGEN_41>, - <&scmi_clk CK_SCMI_FLEXGEN_42>, - <&scmi_clk CK_SCMI_FLEXGEN_43>, - <&scmi_clk CK_SCMI_FLEXGEN_44>, - <&scmi_clk CK_SCMI_FLEXGEN_45>, - <&scmi_clk CK_SCMI_FLEXGEN_46>, - <&scmi_clk CK_SCMI_FLEXGEN_47>, - <&scmi_clk CK_SCMI_FLEXGEN_48>, - <&scmi_clk CK_SCMI_FLEXGEN_49>, - <&scmi_clk CK_SCMI_FLEXGEN_50>, - <&scmi_clk CK_SCMI_FLEXGEN_51>, - <&scmi_clk CK_SCMI_FLEXGEN_52>, - <&scmi_clk CK_SCMI_FLEXGEN_53>, - <&scmi_clk CK_SCMI_FLEXGEN_54>, - <&scmi_clk CK_SCMI_FLEXGEN_55>, - <&scmi_clk CK_SCMI_FLEXGEN_56>, - <&scmi_clk CK_SCMI_FLEXGEN_57>, - <&scmi_clk CK_SCMI_FLEXGEN_58>, - <&scmi_clk CK_SCMI_FLEXGEN_59>, - <&scmi_clk CK_SCMI_FLEXGEN_60>, - <&scmi_clk CK_SCMI_FLEXGEN_61>, - <&scmi_clk CK_SCMI_FLEXGEN_62>, - <&scmi_clk CK_SCMI_FLEXGEN_63>, - <&scmi_clk CK_SCMI_ICN_APB1>, - <&scmi_clk CK_SCMI_ICN_APB2>, - <&scmi_clk CK_SCMI_ICN_APB3>, - <&scmi_clk CK_SCMI_ICN_APB4>, - <&scmi_clk CK_SCMI_ICN_APBDBG>, - <&scmi_clk CK_SCMI_TIMG1>, - <&scmi_clk CK_SCMI_TIMG2>, - <&scmi_clk CK_SCMI_PLL3>, - <&clk_dsi_txbyte>; + clocks = <&scmi_clk CK_SCMI_HSE>, + <&scmi_clk CK_SCMI_HSI>, + <&scmi_clk CK_SCMI_MSI>, + <&scmi_clk CK_SCMI_LSE>, + <&scmi_clk CK_SCMI_LSI>, + <&scmi_clk CK_SCMI_HSE_DIV2>, + <&scmi_clk CK_SCMI_ICN_HS_MCU>, + <&scmi_clk CK_SCMI_ICN_LS_MCU>, + <&scmi_clk CK_SCMI_ICN_SDMMC>, + <&scmi_clk CK_SCMI_ICN_DDR>, + <&scmi_clk CK_SCMI_ICN_DISPLAY>, + <&scmi_clk CK_SCMI_ICN_HSL>, + <&scmi_clk CK_SCMI_ICN_NIC>, + <&scmi_clk CK_SCMI_ICN_VID>, + <&scmi_clk CK_SCMI_FLEXGEN_07>, + <&scmi_clk CK_SCMI_FLEXGEN_08>, + <&scmi_clk CK_SCMI_FLEXGEN_09>, + <&scmi_clk CK_SCMI_FLEXGEN_10>, + <&scmi_clk CK_SCMI_FLEXGEN_11>, + <&scmi_clk CK_SCMI_FLEXGEN_12>, + <&scmi_clk CK_SCMI_FLEXGEN_13>, + <&scmi_clk CK_SCMI_FLEXGEN_14>, + <&scmi_clk CK_SCMI_FLEXGEN_15>, + <&scmi_clk CK_SCMI_FLEXGEN_16>, + <&scmi_clk CK_SCMI_FLEXGEN_17>, + <&scmi_clk CK_SCMI_FLEXGEN_18>, + <&scmi_clk CK_SCMI_FLEXGEN_19>, + <&scmi_clk CK_SCMI_FLEXGEN_20>, + <&scmi_clk CK_SCMI_FLEXGEN_21>, + <&scmi_clk CK_SCMI_FLEXGEN_22>, + <&scmi_clk CK_SCMI_FLEXGEN_23>, + <&scmi_clk CK_SCMI_FLEXGEN_24>, + <&scmi_clk CK_SCMI_FLEXGEN_25>, + <&scmi_clk CK_SCMI_FLEXGEN_26>, + <&scmi_clk CK_SCMI_FLEXGEN_27>, + <&scmi_clk CK_SCMI_FLEXGEN_28>, + <&scmi_clk CK_SCMI_FLEXGEN_29>, + <&scmi_clk CK_SCMI_FLEXGEN_30>, + <&scmi_clk CK_SCMI_FLEXGEN_31>, + <&scmi_clk CK_SCMI_FLEXGEN_32>, + <&scmi_clk CK_SCMI_FLEXGEN_33>, + <&scmi_clk CK_SCMI_FLEXGEN_34>, + <&scmi_clk CK_SCMI_FLEXGEN_35>, + <&scmi_clk CK_SCMI_FLEXGEN_36>, + <&scmi_clk CK_SCMI_FLEXGEN_37>, + <&scmi_clk CK_SCMI_FLEXGEN_38>, + <&scmi_clk CK_SCMI_FLEXGEN_39>, + <&scmi_clk CK_SCMI_FLEXGEN_40>, + <&scmi_clk CK_SCMI_FLEXGEN_41>, + <&scmi_clk CK_SCMI_FLEXGEN_42>, + <&scmi_clk CK_SCMI_FLEXGEN_43>, + <&scmi_clk CK_SCMI_FLEXGEN_44>, + <&scmi_clk CK_SCMI_FLEXGEN_45>, + <&scmi_clk CK_SCMI_FLEXGEN_46>, + <&scmi_clk CK_SCMI_FLEXGEN_47>, + <&scmi_clk CK_SCMI_FLEXGEN_48>, + <&scmi_clk CK_SCMI_FLEXGEN_49>, + <&scmi_clk CK_SCMI_FLEXGEN_50>, + <&scmi_clk CK_SCMI_FLEXGEN_51>, + <&scmi_clk CK_SCMI_FLEXGEN_52>, + <&scmi_clk CK_SCMI_FLEXGEN_53>, + <&scmi_clk CK_SCMI_FLEXGEN_54>, + <&scmi_clk CK_SCMI_FLEXGEN_55>, + <&scmi_clk CK_SCMI_FLEXGEN_56>, + <&scmi_clk CK_SCMI_FLEXGEN_57>, + <&scmi_clk CK_SCMI_FLEXGEN_58>, + <&scmi_clk CK_SCMI_FLEXGEN_59>, + <&scmi_clk CK_SCMI_FLEXGEN_60>, + <&scmi_clk CK_SCMI_FLEXGEN_61>, + <&scmi_clk CK_SCMI_FLEXGEN_62>, + <&scmi_clk CK_SCMI_FLEXGEN_63>, + <&scmi_clk CK_SCMI_ICN_APB1>, + <&scmi_clk CK_SCMI_ICN_APB2>, + <&scmi_clk CK_SCMI_ICN_APB3>, + <&scmi_clk CK_SCMI_ICN_APB4>, + <&scmi_clk CK_SCMI_ICN_APBDBG>, + <&scmi_clk CK_SCMI_TIMG1>, + <&scmi_clk CK_SCMI_TIMG2>, + <&scmi_clk CK_SCMI_PLL3>, + <&clk_dsi_txbyte>; }; ... diff --git a/Documentation/devicetree/bindings/clock/ti,keystone,psc-clock.yaml b/Documentation/devicetree/bindings/clock/ti,keystone,psc-clock.yaml new file mode 100644 index 000000000000..ed42adb27832 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ti,keystone,psc-clock.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/ti,keystone,psc-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI Keystone PSC clock + +maintainers: + - Santosh Shilimkar + - Murali Karicheri + +properties: + compatible: + const: ti,keystone,psc-clock + + reg: + items: + - description: PSC control + - description: Domain + + reg-names: + items: + - const: control + - const: domain + + domain-id: + $ref: /schemas/types.yaml#/definitions/uint32 + description: PSC domain id needed to check the transition state register + + clocks: + maxItems: 1 + + clock-output-names: + maxItems: 1 + + "#clock-cells": + const: 0 + +required: + - compatible + - reg + - reg-names + - domain-id + - clocks + - "#clock-cells" + +additionalProperties: false + +examples: + - | + clkusb@2350008 { + compatible = "ti,keystone,psc-clock"; + reg = <0x02350008 0xb00>, <0x02350000 0x400>; + reg-names = "control", "domain"; + domain-id = <0>; + clocks = <&chipclk16>; + clock-output-names = "usb"; + #clock-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/clock/ti/apll.txt b/Documentation/devicetree/bindings/clock/ti/apll.txt deleted file mode 100644 index bbd505c1199d..000000000000 --- a/Documentation/devicetree/bindings/clock/ti/apll.txt +++ /dev/null @@ -1,43 +0,0 @@ -Binding for Texas Instruments APLL clock. - -This binding uses the common clock binding[1]. It assumes a -register-mapped APLL with usually two selectable input clocks -(reference clock and bypass clock), with analog phase locked -loop logic for multiplying the input clock to a desired output -clock. This clock also typically supports different operation -modes (locked, low power stop etc.) APLL mostly behaves like -a subtype of a DPLL [2], although a simplified one at that. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt -[2] Documentation/devicetree/bindings/clock/ti/dpll.txt - -Required properties: -- compatible : shall be "ti,dra7-apll-clock" or "ti,omap2-apll-clock" -- #clock-cells : from common clock binding; shall be set to 0. -- clocks : link phandles of parent clocks (clk-ref and clk-bypass) -- reg : address and length of the register set for controlling the APLL. - It contains the information of registers in the following order: - "control" - contains the control register offset - "idlest" - contains the idlest register offset - "autoidle" - contains the autoidle register offset (OMAP2 only) -- ti,clock-frequency : static clock frequency for the clock (OMAP2 only) -- ti,idlest-shift : bit-shift for the idlest field (OMAP2 only) -- ti,bit-shift : bit-shift for enable and autoidle fields (OMAP2 only) - -Examples: - apll_pcie_ck: apll_pcie_ck { - #clock-cells = <0>; - clocks = <&apll_pcie_in_clk_mux>, <&dpll_pcie_ref_ck>; - reg = <0x021c>, <0x0220>; - compatible = "ti,dra7-apll-clock"; - }; - - apll96_ck: apll96_ck { - #clock-cells = <0>; - compatible = "ti,omap2-apll-clock"; - clocks = <&sys_ck>; - ti,bit-shift = <2>; - ti,idlest-shift = <8>; - ti,clock-frequency = <96000000>; - reg = <0x0500>, <0x0530>, <0x0520>; - }; diff --git a/Documentation/devicetree/bindings/clock/ti/clockdomain.txt b/Documentation/devicetree/bindings/clock/ti/clockdomain.txt deleted file mode 100644 index edf0b5d42768..000000000000 --- a/Documentation/devicetree/bindings/clock/ti/clockdomain.txt +++ /dev/null @@ -1,25 +0,0 @@ -Binding for Texas Instruments clockdomain. - -This binding uses the common clock binding[1] in consumer role. -Every clock on TI SoC belongs to one clockdomain, but software -only needs this information for specific clocks which require -their parent clockdomain to be controlled when the clock is -enabled/disabled. This binding doesn't define a new clock -binding type, it is used to group existing clock nodes under -hardware hierarchy. - -[1] Documentation/devicetree/bindings/clock/clock-bindings.txt - -Required properties: -- compatible : shall be "ti,clockdomain" -- #clock-cells : from common clock binding; shall be set to 0. -- clocks : link phandles of clocks within this domain - -Optional properties: -- clock-output-names : from common clock binding. - -Examples: - dss_clkdm: dss_clkdm { - compatible = "ti,clockdomain"; - clocks = <&dss1_alwon_fck_3430es2>, <&dss_ick_3430es2>; - }; diff --git a/Documentation/devicetree/bindings/clock/ti/ti,apll-clock.yaml b/Documentation/devicetree/bindings/clock/ti/ti,apll-clock.yaml new file mode 100644 index 000000000000..e14a76254ce7 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ti/ti,apll-clock.yaml @@ -0,0 +1,89 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/ti/ti,apll-clock.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments APLL clock + +maintainers: + - Eduard Bostina + +allOf: + - $ref: /schemas/clock/clock.yaml# + +description: + This binding assumes a register-mapped APLL with usually two selectable + input clocks (reference clock and bypass clock), with analog phase locked + loop logic for multiplying the input clock to a desired output clock. + This clock also typically supports different operation modes (locked, low + power stop etc.) APLL mostly behaves like a subtype of a DPLL, although + a simplified one at that. + +properties: + compatible: + enum: + - ti,dra7-apll-clock + - ti,omap2-apll-clock + + "#clock-cells": + const: 0 + + clocks: + minItems: 1 + maxItems: 2 + description: Link phandles of parent clocks (clk-ref and clk-bypass) + + reg: + minItems: 2 + maxItems: 3 + description: | + Address and length of the register set for controlling the APLL. + It contains the information of registers in the following order: + "control" - contains the control register offset + "idlest" - contains the idlest register offset + "autoidle" - contains the autoidle register offset (OMAP2 only) + + ti,clock-frequency: + $ref: /schemas/types.yaml#/definitions/uint32 + description: static clock frequency for the clock (OMAP2 only) + + ti,idlest-shift: + $ref: /schemas/types.yaml#/definitions/uint32 + description: bit-shift for the idlest field (OMAP2 only) + + ti,bit-shift: + $ref: /schemas/types.yaml#/definitions/uint32 + description: bit-shift for enable and autoidle fields (OMAP2 only) + +required: + - compatible + - "#clock-cells" + - clocks + - reg + +unevaluatedProperties: false + +examples: + - | + bus { + #address-cells = <1>; + #size-cells = <0>; + + clock@21c { + #clock-cells = <0>; + compatible = "ti,dra7-apll-clock"; + clocks = <&apll_pcie_in_clk_mux>, <&dpll_pcie_ref_ck>; + reg = <0x021c>, <0x0220>; + }; + + clock@500 { + #clock-cells = <0>; + compatible = "ti,omap2-apll-clock"; + clocks = <&sys_ck>; + ti,bit-shift = <2>; + ti,idlest-shift = <8>; + ti,clock-frequency = <96000000>; + reg = <0x0500>, <0x0530>, <0x0520>; + }; + }; diff --git a/Documentation/devicetree/bindings/clock/ti/ti,clockdomain.yaml b/Documentation/devicetree/bindings/clock/ti/ti,clockdomain.yaml new file mode 100644 index 000000000000..95746bfdced4 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ti/ti,clockdomain.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/ti/ti,clockdomain.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments clockdomain + +maintainers: + - Tero Kristo + +description: + This binding uses the common clock binding in consumer role. Every clock on TI + SoC belongs to one clockdomain, but software only needs this information for + specific clocks which require their parent clockdomain to be controlled when + the clock is enabled/disabled. This binding doesn't define a new clock binding + type, it is used to group existing clock nodes under hardware hierarchy. + +properties: + compatible: + const: ti,clockdomain + + "#clock-cells": + const: 0 + + clocks: + minItems: 1 + maxItems: 64 + + clock-output-names: + maxItems: 1 + +required: + - compatible + - clocks + +additionalProperties: false + +examples: + - | + dss_clkdm { + compatible = "ti,clockdomain"; + clocks = <&dss1_alwon_fck_3430es2>, <&dss_ick_3430es2>; + }; diff --git a/Documentation/devicetree/bindings/clock/ti/ti,gate-clock.yaml b/Documentation/devicetree/bindings/clock/ti/ti,gate-clock.yaml index eaa727ab0d7f..438e190d1067 100644 --- a/Documentation/devicetree/bindings/clock/ti/ti,gate-clock.yaml +++ b/Documentation/devicetree/bindings/clock/ti/ti,gate-clock.yaml @@ -19,7 +19,7 @@ description: | that is used. [1] Documentation/devicetree/bindings/clock/gpio-gate-clock.yaml - [2] Documentation/devicetree/bindings/clock/ti/clockdomain.txt + [2] Documentation/devicetree/bindings/clock/ti/ti,clockdomain.yaml properties: compatible: diff --git a/Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml b/Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml new file mode 100644 index 000000000000..ede565ec440c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/ultrarisc,dp1000-clk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: UltraRISC DP1000 Clock Controller + +maintainers: + - Jia Wang + +description: | + The UltraRISC DP1000 clock controller is driven from a single external + oscillator input. It provides a system PLL with fractional multiplier + and post-divider stages, several fixed-ratio derived clocks for + the on-chip subsystem, Clock Configuration Register (CCR) divider + outputs for GMAC and the UART, I2C, and SPI root clocks, and + per-instance gate clocks for UART0-3, I2C0-3, and SPI0-1. + + All available clocks are defined as preprocessor macros in + include/dt-bindings/clock/ultrarisc,dp1000-clk.h + +properties: + compatible: + const: ultrarisc,dp1000-clk + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + description: + External oscillator input clock used as the parent of the PLLs. + + "#clock-cells": + const: 1 + +required: + - compatible + - reg + - clocks + - "#clock-cells" + +additionalProperties: false + +examples: + - | + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + clock-controller@11080000 { + compatible = "ultrarisc,dp1000-clk"; + reg = <0x0 0x11080000 0x0 0x1000>; + clocks = <&osc>; + #clock-cells = <1>; + }; + }; diff --git a/Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml b/Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml index b497c28e8094..3f311f90294e 100644 --- a/Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml +++ b/Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml @@ -70,7 +70,7 @@ additionalProperties: false examples: - | - clock-controller@b0000000 { + clock-controller@b0000000 { compatible = "xlnx,clocking-wizard"; reg = <0xb0000000 0x10000>; #clock-cells = <1>; diff --git a/Documentation/devicetree/bindings/remoteproc/ti,keystone-rproc.txt b/Documentation/devicetree/bindings/remoteproc/ti,keystone-rproc.txt index 91f0a3b0c0b2..847552133633 100644 --- a/Documentation/devicetree/bindings/remoteproc/ti,keystone-rproc.txt +++ b/Documentation/devicetree/bindings/remoteproc/ti,keystone-rproc.txt @@ -75,7 +75,7 @@ SoCs only: - clocks: Should contain the device's input clock, and should be defined as per the bindings in, - Documentation/devicetree/bindings/clock/keystone-gate.txt + Documentation/devicetree/bindings/clock/ti,keystone,psc-clock.yaml The following are mandatory properties for Keystone 2 66AK2G SoCs only: diff --git a/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml b/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml new file mode 100644 index 000000000000..50dd0593e1d9 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/cix/cix,sky1-audss-cru.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cix Sky1 audio subsystem clock and reset unit + +maintainers: + - Joakim Zhang + +description: | + The Cix Sky1 Audio Subsystem (AUDSS) Clock and Reset Unit (CRU) groups + audio-related clock muxing, gating and block-level software reset control + in a single register block. + + A single device node exposes both the clock controller and software reset + lines. The clock driver registers as a platform driver; the reset controller + is registered by an auxiliary driver bound from the clock driver. + + Four SoC-level reference clocks listed in clocks/clock-names feed the AUDSS + clock tree. Internal AUDSS clocks are exposed via #clock-cells; indices are + defined in include/dt-bindings/clock/cix,sky1-audss-cru.h. + + Block-level software reset indices are exposed via #reset-cells; indices + are defined in include/dt-bindings/reset/cix,sky1-audss-cru.h. + + The SoC syscon NoC (or bus) reset is described via resets. The audio + subsystem power domain is described via power-domains. + +properties: + compatible: + const: cix,sky1-audss-cru + + reg: + maxItems: 1 + + '#clock-cells': + const: 1 + description: + Clock indices are defined in include/dt-bindings/clock/cix,sky1-audss-cru.h. + + '#reset-cells': + const: 1 + description: + Reset indices are defined in include/dt-bindings/reset/cix,sky1-audss-cru.h. + + clocks: + items: + - description: I2S parent clock for sampling rates multiple of 8kHz. + - description: I2S parent clock for sampling rates multiple of 11.025kHz. + - description: Clock feeding most devices in AUDSS (NOC, DSP, SRAM, HDA, DMAC, I2S, and mailbox). + - description: Clock feeding HDA, timer and watchdog, which is a dedicated 48 MHz clock. + + clock-names: + items: + - const: x8k + - const: x11k + - const: sys + - const: 48m + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - '#clock-cells' + - '#reset-cells' + - clocks + - clock-names + - power-domains + - resets + +additionalProperties: false + +examples: + - | + audss_cru: clock-controller@7110000 { + compatible = "cix,sky1-audss-cru"; + reg = <0x7110000 0x10000>; + #clock-cells = <1>; + #reset-cells = <1>; + clocks = <&scmi_clk 76>, <&scmi_clk 78>, + <&scmi_clk 70>, <&scmi_clk 71>; + clock-names = "x8k", "x11k", "sys", "48m"; + power-domains = <&smc_devpd 0>; + resets = <&s5_syscon 31>; + }; diff --git a/Documentation/driver-api/clk.rst b/Documentation/driver-api/clk.rst index c6aca8186a78..294ead519bbf 100644 --- a/Documentation/driver-api/clk.rst +++ b/Documentation/driver-api/clk.rst @@ -42,21 +42,8 @@ clock interface. Common data structures and api ============================== -Below is the common struct clk_core definition from -drivers/clk/clk.c, modified for brevity:: - - struct clk_core { - const char *name; - const struct clk_ops *ops; - struct clk_hw *hw; - struct module *owner; - struct clk_core *parent; - const char **parent_names; - struct clk_core **parents; - u8 num_parents; - u8 new_parent_index; - ... - }; +.. kernel-doc:: drivers/clk/clk.c + :identifiers: struct clk_core The members above make up the core of the clk tree topology. The clk api itself defines several driver-facing functions which operate on @@ -64,38 +51,17 @@ struct clk. That api is documented in include/linux/clk.h. Platforms and devices utilizing the common struct clk_core use the struct clk_ops pointer in struct clk_core to perform the hardware-specific parts of -the operations defined in clk-provider.h:: +the operations defined in clk-provider.h, and can set one or more +framework-level flags documented below. - struct clk_ops { - int (*prepare)(struct clk_hw *hw); - void (*unprepare)(struct clk_hw *hw); - int (*is_prepared)(struct clk_hw *hw); - void (*unprepare_unused)(struct clk_hw *hw); - int (*enable)(struct clk_hw *hw); - void (*disable)(struct clk_hw *hw); - int (*is_enabled)(struct clk_hw *hw); - void (*disable_unused)(struct clk_hw *hw); - unsigned long (*recalc_rate)(struct clk_hw *hw, - unsigned long parent_rate); - int (*determine_rate)(struct clk_hw *hw, - struct clk_rate_request *req); - int (*set_parent)(struct clk_hw *hw, u8 index); - u8 (*get_parent)(struct clk_hw *hw); - int (*set_rate)(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate); - int (*set_rate_and_parent)(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate, - u8 index); - unsigned long (*recalc_accuracy)(struct clk_hw *hw, - unsigned long parent_accuracy); - int (*get_phase)(struct clk_hw *hw); - int (*set_phase)(struct clk_hw *hw, int degrees); - void (*init)(struct clk_hw *hw); - void (*debug_init)(struct clk_hw *hw, - struct dentry *dentry); - }; +.. kernel-doc:: include/linux/clk-provider.h + :identifiers: struct clk_ops + +Core flags +========== + +.. kernel-doc:: include/linux/clk-provider.h + :doc: clk framework flags Hardware clk implementations ============================ diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 017fb155a5bc..5067500ded5c 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -248,6 +248,7 @@ CLOCK devm_clk_put() devm_clk_bulk_get() devm_clk_bulk_get_all() + devm_clk_bulk_get_enable() devm_clk_bulk_get_optional() devm_get_clk_from_child() devm_clk_hw_register() diff --git a/MAINTAINERS b/MAINTAINERS index dcf9bde53b3c..2d104b631e7a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6447,9 +6447,9 @@ F: include/linux/comedi/ F: include/uapi/linux/comedi.h COMMON CLK FRAMEWORK -M: Michael Turquette M: Stephen Boyd -R: Brian Masney +M: Brian Masney +M: Jerome Brunet L: linux-clk@vger.kernel.org S: Maintained Q: http://patchwork.kernel.org/project/linux-clk/list/ @@ -9660,9 +9660,10 @@ ESWIN EIC7700 CLOCK DRIVER M: Yifeng Huang M: Xuyang Dong S: Maintained -F: Documentation/devicetree/bindings/clock/eswin,eic7700-clock.yaml +F: Documentation/devicetree/bindings/clock/eswin,eic7700* F: drivers/clk/eswin/ -F: include/dt-bindings/clock/eswin,eic7700-clock.h +F: include/dt-bindings/clock/eswin,eic7700* +F: include/dt-bindings/reset/eswin,eic7700-hspcrg.h ET131X NETWORK DRIVER M: Mark Einon @@ -27672,6 +27673,14 @@ S: Maintained F: drivers/usb/common/ulpi.c F: include/linux/ulpi/ +ULTRARISC DP1000 CLOCK DRIVER +M: Jia Wang +L: linux-clk@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml +F: drivers/clk/ultrarisc/* +F: include/dt-bindings/clock/ultrarisc,dp1000-clk.h + ULTRARISC DP1000 PINCTRL DRIVER M: Jia Wang L: linux-gpio@vger.kernel.org diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi index a77c52296ebd..9b2d969f6bfe 100644 --- a/arch/arm64/boot/dts/cix/sky1.dtsi +++ b/arch/arm64/boot/dts/cix/sky1.dtsi @@ -6,6 +6,10 @@ #include #include +#include +#include +#include +#include #include "sky1-power.h" / { @@ -558,6 +562,20 @@ cix,mbox-dir = "rx"; }; + audss_cru: clock-controller@7110000 { + compatible = "cix,sky1-audss-cru"; + reg = <0x0 0x07110000 0x0 0x10000>; + #clock-cells = <1>; + #reset-cells = <1>; + clocks = <&scmi_clk CLK_TREE_AUDIO_CLK0>, + <&scmi_clk CLK_TREE_AUDIO_CLK2>, + <&scmi_clk CLK_TREE_AUDIO_CLK4>, + <&scmi_clk CLK_TREE_AUDIO_CLK5>; + clock-names = "x8k", "x11k", "sys", "48m"; + power-domains = <&smc_devpd SKY1_PD_AUDIO>; + resets = <&s5_syscon SKY1_AUDIO_HIFI5_NOC_RESET_N>; + }; + mbox_sfh2ap: mailbox@8090000 { compatible = "cix,sky1-mbox"; reg = <0x0 0x08090000 0x0 0x10000>; diff --git a/drivers/clk/.kunitconfig b/drivers/clk/.kunitconfig index 8a0ea41934a2..ea05b9a28c80 100644 --- a/drivers/clk/.kunitconfig +++ b/drivers/clk/.kunitconfig @@ -4,6 +4,7 @@ CONFIG_OF=y CONFIG_OF_OVERLAY=y CONFIG_COMMON_CLK=y CONFIG_CLK_KUNIT_TEST=y +CONFIG_CLK_DIVIDER_KUNIT_TEST=y CONFIG_CLK_FIXED_RATE_KUNIT_TEST=y CONFIG_CLK_GATE_KUNIT_TEST=y CONFIG_CLK_FD_KUNIT_TEST=y diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 1717ce75a907..f9592fd9ec2b 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -155,12 +155,12 @@ config COMMON_CLK_SI514 generator. config COMMON_CLK_SI544 - tristate "Clock driver for SiLabs 544 devices" + tristate "Clock driver for SiLabs 544 and compatible devices" depends on I2C select REGMAP_I2C help - This driver supports the Silicon Labs 544 programmable clock - generator. + This driver supports the Silicon Labs 544/549 programmable clock + generators. config COMMON_CLK_SI570 tristate "Clock driver for SiLabs 570 and compatible devices" @@ -278,7 +278,7 @@ config COMMON_CLK_LAN966X tristate "Generic Clock Controller driver for LAN966X SoC" depends on HAS_IOMEM depends on OF - depends on SOC_LAN966 || ARCH_LAN969X || COMPILE_TEST + depends on SOC_LAN966 || ARCH_LAN969X || MCHP_LAN966X_PCI || COMPILE_TEST help This driver provides support for Generic Clock Controller(GCK) on LAN966X SoC. GCK generates and supplies clock to various peripherals @@ -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/cix/Kconfig" source "drivers/clk/eswin/Kconfig" source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/imgtec/Kconfig" @@ -541,6 +542,7 @@ source "drivers/clk/tenstorrent/Kconfig" source "drivers/clk/thead/Kconfig" source "drivers/clk/stm32/Kconfig" source "drivers/clk/ti/Kconfig" +source "drivers/clk/ultrarisc/Kconfig" source "drivers/clk/uniphier/Kconfig" source "drivers/clk/visconti/Kconfig" source "drivers/clk/x86/Kconfig" @@ -579,4 +581,12 @@ config CLK_FD_KUNIT_TEST help Kunit test for the clk-fractional-divider type. +config CLK_DIVIDER_KUNIT_TEST + tristate "KUnit tests for clk divider bestdiv" if !KUNIT_ALL_TESTS + depends on KUNIT + depends on !S390 + default KUNIT_ALL_TESTS + help + Kunit test for the clk-divider type. + endif diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 6899e32e14aa..b18af485d7f0 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -18,9 +18,20 @@ clk-test-y := clk_test.o \ kunit_clk_assigned_rates_without_consumer.dtbo.o \ kunit_clk_assigned_rates_zero.dtbo.o \ kunit_clk_assigned_rates_zero_consumer.dtbo.o \ + kunit_clk_assigned_sscs_one.dtbo.o \ + kunit_clk_assigned_sscs_one_consumer.dtbo.o \ + kunit_clk_assigned_sscs_multiple.dtbo.o \ + kunit_clk_assigned_sscs_multiple_consumer.dtbo.o \ + kunit_clk_assigned_sscs_null.dtbo.o \ + kunit_clk_assigned_sscs_null_consumer.dtbo.o \ + kunit_clk_assigned_sscs_without.dtbo.o \ + kunit_clk_assigned_sscs_without_consumer.dtbo.o \ + kunit_clk_assigned_sscs_zero.dtbo.o \ + kunit_clk_assigned_sscs_zero_consumer.dtbo.o \ kunit_clk_hw_get_dev_of_node.dtbo.o \ kunit_clk_parent_data_test.dtbo.o obj-$(CONFIG_COMMON_CLK) += clk-divider.o +obj-$(CONFIG_CLK_DIVIDER_KUNIT_TEST) += clk-divider_test.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o obj-$(CONFIG_CLK_FIXED_RATE_KUNIT_TEST) += clk-fixed-rate-test.o @@ -88,7 +99,8 @@ obj-$(CONFIG_COMMON_CLK_RP1) += clk-rp1.o obj-$(CONFIG_COMMON_CLK_RPMI) += clk-rpmi.o obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o -obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o +scmi-clk-y := clk-scmi.o clk-scmi-oem.o +obj-$(CONFIG_COMMON_CLK_SCMI) += scmi-clk.o obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o obj-$(CONFIG_COMMON_CLK_SI5341) += clk-si5341.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o @@ -119,9 +131,10 @@ obj-$(CONFIG_ARCH_ARTPEC) += axis/ obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/ obj-y += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ +obj-y += cix/ obj-$(CONFIG_ARCH_DAVINCI) += davinci/ obj-$(CONFIG_COMMON_CLK_ESWIN) += eswin/ -obj-$(CONFIG_ARCH_HISI) += hisilicon/ +obj-$(CONFIG_COMMON_CLK_HISI) += hisilicon/ obj-y += imgtec/ obj-y += imx/ obj-y += ingenic/ @@ -151,7 +164,7 @@ obj-y += spacemit/ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-y += sprd/ obj-$(CONFIG_ARCH_STI) += st/ -obj-$(CONFIG_ARCH_STM32) += stm32/ +obj-$(CONFIG_COMMON_CLK_STM32MP) += stm32/ obj-y += starfive/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-y += sunxi-ng/ @@ -159,6 +172,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-y += tenstorrent/ obj-y += thead/ obj-y += ti/ +obj-y += ultrarisc/ obj-$(CONFIG_CLK_UNIPHIER) += uniphier/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-y += versatile/ diff --git a/drivers/clk/aspeed/clk-ast2700.c b/drivers/clk/aspeed/clk-ast2700.c index aa4dd7f24608..536600fcd024 100644 --- a/drivers/clk/aspeed/clk-ast2700.c +++ b/drivers/clk/aspeed/clk-ast2700.c @@ -272,6 +272,13 @@ static const unsigned int sdclk_parent_ids[] = { static const struct clk_hw *sdclk_parent_hws[ARRAY_SIZE(sdclk_parent_ids)]; +static const unsigned int peciclk_parent_ids[] = { + SCU1_CLKIN, + SCU1_CLK_HPLL_DIV4 +}; + +static const struct clk_hw *peciclk_parent_hws[ARRAY_SIZE(peciclk_parent_ids)]; + #define FIXED_CLK(_id, _name, _rate) \ { \ .id = _id, \ @@ -457,6 +464,7 @@ static const struct ast2700_clk_info ast2700_scu1_clk_info[] __initconst = { PLL_CLK(SCU1_CLK_HPLL, CLK_PLL, "soc1-hpll", SCU1_CLKIN, SCU1_HPLL_PARAM), PLL_CLK(SCU1_CLK_APLL, CLK_PLL, "soc1-apll", SCU1_CLKIN, SCU1_APLL_PARAM), PLL_CLK(SCU1_CLK_DPLL, CLK_PLL, "soc1-dpll", SCU1_CLKIN, SCU1_DPLL_PARAM), + FIXED_FACTOR_CLK(SCU1_CLK_HPLL_DIV4, "soc1-hpll_div4", SCU1_CLK_HPLL, 1, 4), FIXED_FACTOR_CLK(SCU1_CLK_APLL_DIV2, "soc1-apll_div2", SCU1_CLK_APLL, 1, 2), FIXED_FACTOR_CLK(SCU1_CLK_APLL_DIV4, "soc1-apll_div4", SCU1_CLK_APLL, 1, 4), FIXED_FACTOR_CLK(SCU1_CLK_CAN, "canclk", SCU1_CLK_APLL, 1, 10), @@ -480,6 +488,8 @@ static const struct ast2700_clk_info ast2700_scu1_clk_info[] __initconst = { uxclk_parent_hws, SCU1_CLK_SEL2, 0, 2), MUX_CLK(SCU1_CLK_HUXCLK, "huxclk", uxclk_parent_ids, ARRAY_SIZE(uxclk_parent_ids), uxclk_parent_hws, SCU1_CLK_SEL2, 3, 2), + MUX_CLK(SCU1_CLK_PECI, "peciclk", peciclk_parent_ids, ARRAY_SIZE(peciclk_parent_ids), + peciclk_parent_hws, SCU1_CLK_SEL2, 16, 1), DIVIDER_CLK(SCU1_CLK_SDCLK, "sdclk", SCU1_CLK_SDMUX, SCU1_CLK_SEL1, 14, 3, ast2700_clk_div_table), PLL_CLK(SCU1_CLK_UARTX, CLK_UART_PLL, "uartxclk", SCU1_CLK_UXCLK, SCU1_UXCLK_CTRL), diff --git a/drivers/clk/at91/dt-compat.c b/drivers/clk/at91/dt-compat.c index 5d543e807843..dad26dd5e71d 100644 --- a/drivers/clk/at91/dt-compat.c +++ b/drivers/clk/at91/dt-compat.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -217,7 +218,7 @@ CLK_OF_DECLARE(of_sama5d4_clk_h32mx_setup, "atmel,sama5d4-clk-h32mx", static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np) { struct regmap *regmap_sfr; - u8 bus_id; + u64 bus_id; const char *parent_names[2]; struct device_node *i2s_mux_np; struct clk_hw *hw; @@ -228,7 +229,7 @@ static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np) return; for_each_child_of_node(np, i2s_mux_np) { - if (of_property_read_u8(i2s_mux_np, "reg", &bus_id)) + if (of_property_read_reg(i2s_mux_np, 0, &bus_id, NULL)) continue; if (bus_id > I2S_BUS_NR) diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index 0171e6b2bfca..d6f5872d7d6a 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -1098,8 +1098,7 @@ static u8 kona_peri_clk_get_parent(struct clk_hw *hw) index = selector_read_index(bcm_clk->ccu, &data->sel); - /* Not all callers would handle an out-of-range value gracefully */ - return index == BAD_CLK_INDEX ? 0 : index; + return index; } static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate, diff --git a/drivers/clk/cix/Kconfig b/drivers/clk/cix/Kconfig new file mode 100644 index 000000000000..b057ef85fab6 --- /dev/null +++ b/drivers/clk/cix/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +# Audio subsystem clock support for Cixtech SoC family +menu "Cixtech Audio Subsystem Clock Driver" + +config CLK_SKY1_AUDSS + tristate "Cixtech Sky1 Audio Subsystem Clock Driver" + depends on ARCH_CIX || COMPILE_TEST + select AUXILIARY_BUS + select REGMAP_MMIO + select RESET_CONTROLLER + help + Support for the Audio Subsystem clock controller present on + Cixtech Sky1 SoC. This driver provides mux, divider and gate + clocks for DSP, I2S, HDA and related blocks in the audio + subsystem. Say M or Y here if you want to build this driver. +endmenu diff --git a/drivers/clk/cix/Makefile b/drivers/clk/cix/Makefile new file mode 100644 index 000000000000..bc612f1d08b2 --- /dev/null +++ b/drivers/clk/cix/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_CLK_SKY1_AUDSS) += clk-sky1-audss.o diff --git a/drivers/clk/cix/clk-sky1-audss.c b/drivers/clk/cix/clk-sky1-audss.c new file mode 100644 index 000000000000..e096b8121df3 --- /dev/null +++ b/drivers/clk/cix/clk-sky1-audss.c @@ -0,0 +1,1206 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2026 Cix Technology Group Co., Ltd. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define INFO_HIFI0 0x00 +#define INFO_CLK_GATE 0x10 +#define INFO_CLK_DIV 0x14 +#define INFO_CLK_MUX 0x18 +#define INFO_MCLK 0x70 + +#define SKY1_AUDSS_CLK_PARENTS_CNT 4 +#define SKY1_AUDSS_NUM_CLKS (CLK_MCLK4 + 1) + +static u32 sky1_reg_save[][2] = { + { INFO_HIFI0, 0 }, + { INFO_CLK_GATE, 0 }, + { INFO_CLK_DIV, 0 }, + { INFO_CLK_MUX, 0 }, + { INFO_MCLK, 0 }, +}; + +static const char * const sky1_audss_clk_names[SKY1_AUDSS_CLK_PARENTS_CNT] = { + "x8k", "x11k", "sys", "48m", +}; + +static const u32 sky1_clk_rate_default[SKY1_AUDSS_CLK_PARENTS_CNT] = { + 294912000, + 270950400, + 800000000, + 48000000, +}; + +static const char * const dsp_clk_parent[] = { + "audio_clk4" +}; + +static const char * const dsp_bclk_parent[] = { + "audio_clk4_div2" +}; + +static const char * const dsp_pbclk_parent[] = { + "audio_clk4_div4" +}; + +static const char * const sram_axi_parent[] = { + "audio_clk4_div2" +}; + +static const char * const hda_sys_parent[] = { + "audio_clk4_div2" +}; + +static const char * const hda_hda_parent[] = { + "audio_clk5" +}; + +static const char * const dmac_axi_parent[] = { + "audio_clk4_div2" +}; + +static const char * const wdg_apb_parent[] = { + "audio_clk5_div2" +}; + +static const char * const wdg_wdg_parent[] = { + "audio_clk5_div2" +}; + +static const char * const timer_apb_parent[] = { + "audio_clk4_div4" +}; + +static const char * const timer_timer_parent[] = { + "audio_clk5_div2" +}; + +static const char * const mailbox_apb_parent[] = { + "audio_clk4_div4" +}; + +static const char * const i2s_apb_parent[] = { + "audio_clk4_div4" +}; + +static const char * const i2s0_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s1_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s2_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s3_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s4_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s5_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s6_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s7_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s8_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const i2s9_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const char * const mclk_parents[] = { + "audio_clk0", "audio_clk2" +}; + +static const u32 i2s3_mux_table[] = { 0, 2 }; +static const u32 i2s4_mux_table[] = { 0, 2 }; + +/* + * audss composite clock definition + */ +struct muxdiv_cfg { + int offset; + u8 shift; + u8 width; + u8 flags; +}; + +struct gate_cfg { + int offset; + u8 shift; + u8 flags; +}; + +struct composite_clk_cfg { + u32 id; + const char * const name; + const char * const *parent_names; + int num_parents; + const u32 *mux_table; + struct muxdiv_cfg *mux_cfg; + struct muxdiv_cfg *div_cfg; + struct gate_cfg *gate_cfg; + unsigned long flags; +}; + +#define CFG(_id,\ + _name,\ + _parent_names,\ + _mux_table,\ + _mux_offset, _mux_shift, _mux_width, _mux_flags,\ + _div_offset, _div_shift, _div_width, _div_flags,\ + _gate_offset, _gate_shift, _gate_flags,\ + _flags)\ +{\ + .id = _id,\ + .name = _name,\ + .parent_names = _parent_names,\ + .num_parents = ARRAY_SIZE(_parent_names),\ + .mux_table = _mux_table,\ + .mux_cfg = &(struct muxdiv_cfg) { _mux_offset, _mux_shift, _mux_width, _mux_flags },\ + .div_cfg = &(struct muxdiv_cfg) { _div_offset, _div_shift, _div_width, _div_flags },\ + .gate_cfg = &(struct gate_cfg) { _gate_offset, _gate_shift, _gate_flags },\ + .flags = _flags,\ +} + +static const struct composite_clk_cfg sky1_audss_clks[] = { + /* dsp */ + CFG(CLK_DSP_CLK, + "audss_dsp_clk", + dsp_clk_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_HIFI0, 0, 0, + 0), + CFG(CLK_DSP_BCLK, + "audss_dsp_bclk", + dsp_bclk_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + -1, 0, 0, + 0), + CFG(CLK_DSP_PBCLK, + "audss_dsp_pbclk", + dsp_pbclk_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + -1, 0, 0, + 0), + /* sram */ + CFG(CLK_SRAM_AXI, + "audss_sram_axi", + sram_axi_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 16, 0, + 0), + /* hda */ + CFG(CLK_HDA_SYS, + "audss_hda_sys", + hda_sys_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 14, 0, + 0), + CFG(CLK_HDA_HDA, + "audss_hda_hda", + hda_hda_parent, + NULL, + -1, 0, 0, 0, + -1, 0, 0, 0, + INFO_CLK_GATE, 14, 0, + 0), + /* dmac */ + CFG(CLK_DMAC_AXI, + "audss_dmac_axi", + dmac_axi_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 15, 0, + 0), + /* wdg */ + CFG(CLK_WDG_APB, + "audss_wdg_apb", + wdg_apb_parent, + NULL, + -1, 0, 0, 0, + -1, 0, 0, 0, + INFO_CLK_GATE, 10, 0, + 0), + CFG(CLK_WDG_WDG, + "audss_wdg_wdg", + wdg_wdg_parent, + NULL, + -1, 0, 0, 0, + -1, 0, 0, 0, + INFO_CLK_GATE, 10, 0, + 0), + /* timer */ + CFG(CLK_TIMER_APB, + "audss_timer_apb", + timer_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 11, 0, + 0), + CFG(CLK_TIMER_TIMER, + "audss_timer_timer", + timer_timer_parent, + NULL, + -1, 0, 0, 0, + -1, 0, 0, 0, + INFO_CLK_GATE, 11, 0, + 0), + /* mailbox: mb0(ap->dsp), mb1(dsp->ap) */ + CFG(CLK_MB_0_APB, + "audss_mb_0_apb", + mailbox_apb_parent, + NULL, + -1, 0, 0, 0, + -1, 0, 0, 0, + INFO_CLK_GATE, 12, 0, + 0), + CFG(CLK_MB_1_APB, + "audss_mb_1_apb", + mailbox_apb_parent, + NULL, + -1, 0, 0, 0, + -1, 0, 0, 0, + INFO_CLK_GATE, 13, 0, + 0), + /* i2s */ + CFG(CLK_I2S0_APB, + "audss_i2s0_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 0, 0, + 0), + CFG(CLK_I2S1_APB, + "audss_i2s1_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 1, 0, + 0), + CFG(CLK_I2S2_APB, + "audss_i2s2_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 2, 0, + 0), + CFG(CLK_I2S3_APB, + "audss_i2s3_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 3, 0, + 0), + CFG(CLK_I2S4_APB, + "audss_i2s4_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 4, 0, + 0), + CFG(CLK_I2S5_APB, + "audss_i2s5_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 5, 0, + 0), + CFG(CLK_I2S6_APB, + "audss_i2s6_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 6, 0, + 0), + CFG(CLK_I2S7_APB, + "audss_i2s7_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 7, 0, + 0), + CFG(CLK_I2S8_APB, + "audss_i2s8_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 8, 0, + 0), + CFG(CLK_I2S9_APB, + "audss_i2s9_apb", + i2s_apb_parent, + NULL, + -1, 0, 0, 0, + INFO_CLK_DIV, 0, 2, 0, + INFO_CLK_GATE, 9, 0, + 0), + CFG(CLK_I2S0, + "audss_i2s0", + i2s0_parents, + NULL, + INFO_CLK_MUX, 0, 2, 0, + INFO_CLK_DIV, 2, 2, 0, + INFO_CLK_GATE, 0, 0, + 0), + CFG(CLK_I2S1, + "audss_i2s1", + i2s1_parents, + NULL, + INFO_CLK_MUX, 2, 2, 0, + INFO_CLK_DIV, 4, 2, 0, + INFO_CLK_GATE, 1, 0, + 0), + CFG(CLK_I2S2, + "audss_i2s2", + i2s2_parents, + NULL, + INFO_CLK_MUX, 4, 2, 0, + INFO_CLK_DIV, 6, 2, 0, + INFO_CLK_GATE, 2, 0, + 0), + CFG(CLK_I2S3, + "audss_i2s3", + i2s3_parents, + i2s3_mux_table, + INFO_CLK_MUX, 6, 2, 0, + INFO_CLK_DIV, 8, 2, 0, + INFO_CLK_GATE, 3, 0, + 0), + CFG(CLK_I2S4, + "audss_i2s4", + i2s4_parents, + i2s4_mux_table, + INFO_CLK_MUX, 8, 2, 0, + INFO_CLK_DIV, 10, 2, 0, + INFO_CLK_GATE, 4, 0, + 0), + CFG(CLK_I2S5, + "audss_i2s5", + i2s5_parents, + NULL, + INFO_CLK_MUX, 10, 2, 0, + INFO_CLK_DIV, 12, 2, 0, + INFO_CLK_GATE, 5, 0, + 0), + CFG(CLK_I2S6, + "audss_i2s6", + i2s6_parents, + NULL, + INFO_CLK_MUX, 12, 2, 0, + INFO_CLK_DIV, 14, 2, 0, + INFO_CLK_GATE, 6, 0, + 0), + CFG(CLK_I2S7, + "audss_i2s7", + i2s7_parents, + NULL, + INFO_CLK_MUX, 14, 2, 0, + INFO_CLK_DIV, 16, 2, 0, + INFO_CLK_GATE, 7, 0, + 0), + CFG(CLK_I2S8, + "audss_i2s8", + i2s8_parents, + NULL, + INFO_CLK_MUX, 16, 2, 0, + INFO_CLK_DIV, 18, 2, 0, + INFO_CLK_GATE, 8, 0, + 0), + CFG(CLK_I2S9, + "audss_i2s9", + i2s9_parents, + NULL, + INFO_CLK_MUX, 18, 2, 0, + INFO_CLK_DIV, 20, 2, 0, + INFO_CLK_GATE, 9, 0, + 0), + /* mclk */ + CFG(CLK_MCLK0, + "audss_mclk0", + mclk_parents, + NULL, + INFO_MCLK, 5, 1, 0, + -1, 0, 0, 0, + INFO_MCLK, 0, 0, + 0), + CFG(CLK_MCLK1, + "audss_mclk1", + mclk_parents, + NULL, + INFO_MCLK, 6, 1, 0, + -1, 0, 0, 0, + INFO_MCLK, 1, 0, + 0), + CFG(CLK_MCLK2, + "audss_mclk2", + mclk_parents, + NULL, + INFO_MCLK, 7, 1, 0, + -1, 0, 0, 0, + INFO_MCLK, 2, 0, + 0), + CFG(CLK_MCLK3, + "audss_mclk3", + mclk_parents, + NULL, + INFO_MCLK, 8, 1, 0, + -1, 0, 0, 0, + INFO_MCLK, 3, 0, + 0), + CFG(CLK_MCLK4, + "audss_mclk4", + mclk_parents, + NULL, + INFO_MCLK, 9, 1, 0, + -1, 0, 0, 0, + INFO_MCLK, 4, 0, + 0), +}; + +struct sky1_audss_clks_devtype_data { + u32 (*reg_save)[2]; + size_t reg_save_size; + const char * const *clk_names; + size_t clk_num; + const u32 *clk_rate_default; + const struct composite_clk_cfg *clk_cfg; + size_t clk_cfg_size; +}; + +static const struct regmap_config sky1_audss_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +struct sky1_audss_clks_priv { + struct device *dev; + struct regmap *regmap_cru; + struct reset_control *rst_noc; + struct clk *clks[SKY1_AUDSS_CLK_PARENTS_CNT]; + const struct sky1_audss_clks_devtype_data *devtype_data; + spinlock_t lock; + struct clk_hw_onecell_data *clk_data; +}; + +#if IS_ENABLED(CONFIG_RESET_SKY1_AUDSS) + +static int sky1_audss_reset_controller_register(struct device *dev) +{ + struct auxiliary_device *adev; + + if (!of_property_present(dev->of_node, "#reset-cells")) + return 0; + + adev = devm_auxiliary_device_create(dev, "reset", NULL); + if (!adev) + return -ENODEV; + + return 0; +} + +#else + +static int sky1_audss_reset_controller_register(struct device *dev) +{ + return 0; +} + +#endif + +/* + * clk_ops for audss clock mux/divider/gate + */ +struct sky1_clk_divider { + struct clk_divider div; + struct regmap *regmap; + int offset; +}; + +struct sky1_clk_gate { + struct clk_gate gate; + struct regmap *regmap; + int offset; +}; + +struct sky1_clk_mux { + struct clk_mux mux; + struct regmap *regmap; + int offset; +}; + +static inline struct sky1_clk_mux *to_sky1_clk_mux(struct clk_mux *mux) +{ + return container_of(mux, struct sky1_clk_mux, mux); +} + +static u8 sky1_audss_clk_mux_get_parent(struct clk_hw *hw) +{ + struct clk_mux *mux = to_clk_mux(hw); + struct sky1_clk_mux *sky1_mux = to_sky1_clk_mux(mux); + u32 val; + + regmap_read(sky1_mux->regmap, sky1_mux->offset, &val); + val = val >> mux->shift; + val &= mux->mask; + + return clk_mux_val_to_index(hw, mux->table, mux->flags, val); +} + +static int sky1_audss_clk_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_mux *mux = to_clk_mux(hw); + u32 val = clk_mux_index_to_val(mux->table, mux->flags, index); + struct sky1_clk_mux *sky1_mux = to_sky1_clk_mux(mux); + unsigned long flags = 0; + u32 reg; + + if (mux->lock) + spin_lock_irqsave(mux->lock, flags); + else + __acquire(mux->lock); + + if (mux->flags & CLK_MUX_HIWORD_MASK) { + reg = mux->mask << (mux->shift + 16); + } else { + regmap_read(sky1_mux->regmap, sky1_mux->offset, ®); + reg &= ~(mux->mask << mux->shift); + } + val = val << mux->shift; + reg |= val; + regmap_write(sky1_mux->regmap, sky1_mux->offset, reg); + + if (mux->lock) + spin_unlock_irqrestore(mux->lock, flags); + else + __release(mux->lock); + + return 0; +} + +static int sky1_audss_clk_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_mux *mux = to_clk_mux(hw); + + return clk_mux_determine_rate_flags(hw, req, mux->flags); +} + +static const struct clk_ops sky1_audss_clk_mux_ops = { + .get_parent = sky1_audss_clk_mux_get_parent, + .set_parent = sky1_audss_clk_mux_set_parent, + .determine_rate = sky1_audss_clk_mux_determine_rate, +}; + +static inline struct sky1_clk_divider *to_sky1_clk_divider(struct clk_divider *div) +{ + return container_of(div, struct sky1_clk_divider, div); +} + +static unsigned long sky1_audss_clk_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_divider *divider = to_clk_divider(hw); + struct sky1_clk_divider *sky1_div = to_sky1_clk_divider(divider); + unsigned int val; + + regmap_read(sky1_div->regmap, sky1_div->offset, &val); + val = val >> divider->shift; + val &= clk_div_mask(divider->width); + + return divider_recalc_rate(hw, parent_rate, val, divider->table, + divider->flags, divider->width); +} + +static int sky1_audss_clk_divider_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_divider *divider = to_clk_divider(hw); + struct sky1_clk_divider *sky1_div = to_sky1_clk_divider(divider); + + /* if read only, just return current value */ + if (divider->flags & CLK_DIVIDER_READ_ONLY) { + u32 val; + + regmap_read(sky1_div->regmap, sky1_div->offset, &val); + val = val >> divider->shift; + val &= clk_div_mask(divider->width); + + return divider_ro_determine_rate(hw, req, divider->table, + divider->width, + divider->flags, val); + } + + return divider_determine_rate(hw, req, divider->table, divider->width, + divider->flags); +} + +static int sky1_audss_clk_divider_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct clk_divider *divider = to_clk_divider(hw); + struct sky1_clk_divider *sky1_div = to_sky1_clk_divider(divider); + int value; + unsigned long flags = 0; + u32 val; + + value = divider_get_val(rate, parent_rate, divider->table, + divider->width, divider->flags); + if (value < 0) + return value; + + if (divider->lock) + spin_lock_irqsave(divider->lock, flags); + else + __acquire(divider->lock); + + if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { + val = clk_div_mask(divider->width) << (divider->shift + 16); + } else { + regmap_read(sky1_div->regmap, sky1_div->offset, &val); + val &= ~(clk_div_mask(divider->width) << divider->shift); + } + val |= (u32)value << divider->shift; + regmap_write(sky1_div->regmap, sky1_div->offset, val); + + if (divider->lock) + spin_unlock_irqrestore(divider->lock, flags); + else + __release(divider->lock); + + return 0; +} + +static const struct clk_ops sky1_audss_clk_divider_ops = { + .recalc_rate = sky1_audss_clk_divider_recalc_rate, + .determine_rate = sky1_audss_clk_divider_determine_rate, + .set_rate = sky1_audss_clk_divider_set_rate, +}; + +static inline struct sky1_clk_gate *to_sky1_clk_gate(struct clk_gate *gate) +{ + return container_of(gate, struct sky1_clk_gate, gate); +} + +static void sky1_audss_clk_gate_endisable(struct clk_hw *hw, int enable) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct sky1_clk_gate *sky1_gate = to_sky1_clk_gate(gate); + int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; + unsigned long flags = 0; + u32 reg; + + set ^= enable; + + if (gate->lock) + spin_lock_irqsave(gate->lock, flags); + else + __acquire(gate->lock); + + if (gate->flags & CLK_GATE_HIWORD_MASK) { + reg = BIT(gate->bit_idx + 16); + if (set) + reg |= BIT(gate->bit_idx); + } else { + regmap_read(sky1_gate->regmap, sky1_gate->offset, ®); + + if (set) + reg |= BIT(gate->bit_idx); + else + reg &= ~BIT(gate->bit_idx); + } + + regmap_write(sky1_gate->regmap, sky1_gate->offset, reg); + + if (gate->lock) + spin_unlock_irqrestore(gate->lock, flags); + else + __release(gate->lock); +} + +static int sky1_audss_clk_gate_enable(struct clk_hw *hw) +{ + sky1_audss_clk_gate_endisable(hw, 1); + + return 0; +} + +static void sky1_audss_clk_gate_disable(struct clk_hw *hw) +{ + sky1_audss_clk_gate_endisable(hw, 0); +} + +static int sky1_audss_clk_gate_is_enabled(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct sky1_clk_gate *sky1_gate = to_sky1_clk_gate(gate); + u32 reg; + + regmap_read(sky1_gate->regmap, sky1_gate->offset, ®); + + /* if a set bit disables this clk, flip it before masking */ + if (gate->flags & CLK_GATE_SET_TO_DISABLE) + reg ^= BIT(gate->bit_idx); + + reg &= BIT(gate->bit_idx); + + return !!reg; +} + +static const struct clk_ops sky1_audss_clk_gate_ops = { + .enable = sky1_audss_clk_gate_enable, + .disable = sky1_audss_clk_gate_disable, + .is_enabled = sky1_audss_clk_gate_is_enabled, +}; + +static struct clk_hw *sky1_audss_clk_register(struct device *dev, + const char *name, + const char * const *parent_names, + int num_parents, + struct regmap *regmap, + const u32 *mux_table, + struct muxdiv_cfg *mux_cfg, + struct muxdiv_cfg *div_cfg, + struct gate_cfg *gate_cfg, + unsigned long flags, + spinlock_t *lock) +{ + const struct clk_ops *sky1_gate_ops = NULL; + const struct clk_ops *sky1_mux_ops = NULL; + const struct clk_ops *sky1_div_ops = NULL; + struct sky1_clk_divider *sky1_div = NULL; + struct sky1_clk_gate *sky1_gate = NULL; + struct sky1_clk_mux *sky1_mux = NULL; + struct clk_hw *hw = ERR_PTR(-ENOMEM); + struct clk_parent_data *parent_data; + int i; + + parent_data = devm_kcalloc(dev, num_parents, sizeof(*parent_data), GFP_KERNEL); + if (!parent_data) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < num_parents; i++) + parent_data[i].name = parent_names[i]; + + if (mux_cfg->offset >= 0) { + sky1_mux = devm_kzalloc(dev, sizeof(*sky1_mux), GFP_KERNEL); + if (!sky1_mux) + return ERR_PTR(-ENOMEM); + + sky1_mux->mux.reg = NULL; + sky1_mux->mux.shift = mux_cfg->shift; + sky1_mux->mux.mask = BIT(mux_cfg->width) - 1; + sky1_mux->mux.flags = mux_cfg->flags; + sky1_mux->mux.table = mux_table; + sky1_mux->mux.lock = lock; + sky1_mux_ops = &sky1_audss_clk_mux_ops; + sky1_mux->regmap = regmap; + sky1_mux->offset = mux_cfg->offset; + } + + if (div_cfg->offset >= 0) { + sky1_div = devm_kzalloc(dev, sizeof(*sky1_div), GFP_KERNEL); + if (!sky1_div) + return ERR_PTR(-ENOMEM); + + sky1_div->div.reg = NULL; + sky1_div->div.shift = div_cfg->shift; + sky1_div->div.width = div_cfg->width; + sky1_div->div.flags = div_cfg->flags | CLK_DIVIDER_POWER_OF_TWO; + sky1_div->div.lock = lock; + sky1_div_ops = &sky1_audss_clk_divider_ops; + sky1_div->regmap = regmap; + sky1_div->offset = div_cfg->offset; + } + + if (gate_cfg->offset >= 0) { + sky1_gate = devm_kzalloc(dev, sizeof(*sky1_gate), GFP_KERNEL); + if (!sky1_gate) + return ERR_PTR(-ENOMEM); + + sky1_gate->gate.reg = NULL; + sky1_gate->gate.bit_idx = gate_cfg->shift; + sky1_gate->gate.flags = gate_cfg->flags; + sky1_gate->gate.lock = lock; + sky1_gate_ops = &sky1_audss_clk_gate_ops; + sky1_gate->regmap = regmap; + sky1_gate->offset = gate_cfg->offset; + } + + hw = devm_clk_hw_register_composite_pdata(dev, name, parent_data, num_parents, + sky1_mux ? &sky1_mux->mux.hw : NULL, sky1_mux_ops, + sky1_div ? &sky1_div->div.hw : NULL, sky1_div_ops, + sky1_gate ? &sky1_gate->gate.hw : NULL, sky1_gate_ops, + flags); + if (IS_ERR(hw)) { + dev_err(dev, "register %s clock failed with err = %ld\n", + name, PTR_ERR(hw)); + return hw; + } + + return hw; +} + +static int sky1_audss_clks_get(struct sky1_audss_clks_priv *priv) +{ + const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data; + int i; + + for (i = 0; i < devtype_data->clk_num; i++) { + priv->clks[i] = devm_clk_get(priv->dev, devtype_data->clk_names[i]); + if (IS_ERR(priv->clks[i])) + return dev_err_probe(priv->dev, PTR_ERR(priv->clks[i]), + "failed to get clock %s", devtype_data->clk_names[i]); + } + + return 0; +} + +static int sky1_audss_clks_enable(struct sky1_audss_clks_priv *priv) +{ + const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data; + int i, err; + + for (i = 0; i < devtype_data->clk_num; i++) { + err = clk_prepare_enable(priv->clks[i]); + if (err) { + dev_err(priv->dev, "failed to enable clock %s\n", + devtype_data->clk_names[i]); + goto err_clks; + } + } + + return 0; + +err_clks: + while (--i >= 0) + clk_disable_unprepare(priv->clks[i]); + + return err; +} + +static void sky1_audss_clks_disable(struct sky1_audss_clks_priv *priv) +{ + const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data; + int i; + + for (i = 0; i < devtype_data->clk_num; i++) + clk_disable_unprepare(priv->clks[i]); +} + +static int sky1_audss_clks_set_rate(struct sky1_audss_clks_priv *priv) +{ + const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data; + int i, err; + + for (i = 0; i < devtype_data->clk_num; i++) { + err = clk_set_rate(priv->clks[i], devtype_data->clk_rate_default[i]); + if (err) { + dev_err(priv->dev, "failed to set clock rate %s\n", + devtype_data->clk_names[i]); + return err; + } + } + + return 0; +} + +static void sky1_audss_clk_rpm_cleanup(void *data) +{ + pm_runtime_force_suspend(data); +} + +/* register sky1 audio subsystem clocks */ +static int sky1_audss_clk_probe(struct platform_device *pdev) +{ + const struct sky1_audss_clks_devtype_data *devtype_data; + struct sky1_audss_clks_priv *priv; + struct device *dev = &pdev->dev; + struct clk_hw **clk_table; + void __iomem *base; + int i, ret; + + devtype_data = device_get_match_data(dev); + if (!devtype_data) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->lock); + + priv->clk_data = devm_kzalloc(dev, + struct_size(priv->clk_data, hws, SKY1_AUDSS_NUM_CLKS), + GFP_KERNEL); + if (!priv->clk_data) + return -ENOMEM; + + priv->clk_data->num = SKY1_AUDSS_NUM_CLKS; + clk_table = priv->clk_data->hws; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->regmap_cru = devm_regmap_init_mmio(dev, base, &sky1_audss_regmap_config); + if (IS_ERR(priv->regmap_cru)) + return dev_err_probe(dev, PTR_ERR(priv->regmap_cru), + "failed to initialize regmap\n"); + + priv->dev = dev; + priv->devtype_data = devtype_data; + + priv->rst_noc = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(priv->rst_noc)) + return dev_err_probe(dev, PTR_ERR(priv->rst_noc), + "failed to get audss noc reset"); + + platform_set_drvdata(pdev, priv); + + reset_control_assert(priv->rst_noc); + + ret = sky1_audss_clks_get(priv); + if (ret) + return ret; + + ret = sky1_audss_clks_enable(priv); + if (ret) + return ret; + + /* release noc reset after clock on */ + ret = reset_control_deassert(priv->rst_noc); + if (ret) { + sky1_audss_clks_disable(priv); + return ret; + } + + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + /* + * Register cleanup action before clocks/provider (released last under LIFO). + */ + ret = devm_add_action_or_reset(dev, sky1_audss_clk_rpm_cleanup, dev); + if (ret) + return ret; + + ret = sky1_audss_clks_set_rate(priv); + if (ret) + goto fail_pm; + + /* audio_clk4 clock fixed divider */ + clk_table[CLK_AUD_CLK4_DIV2] = + devm_clk_hw_register_fixed_factor(dev, + "audio_clk4_div2", + "audio_clk4", + 0, + 1, 2); + if (IS_ERR(clk_table[CLK_AUD_CLK4_DIV2])) { + ret = PTR_ERR(clk_table[CLK_AUD_CLK4_DIV2]); + dev_err(dev, "failed to register clock %d, ret:%d\n", CLK_AUD_CLK4_DIV2, ret); + goto fail_pm; + } + + clk_table[CLK_AUD_CLK4_DIV4] = + devm_clk_hw_register_fixed_factor(dev, + "audio_clk4_div4", + "audio_clk4", + 0, + 1, 4); + if (IS_ERR(clk_table[CLK_AUD_CLK4_DIV4])) { + ret = PTR_ERR(clk_table[CLK_AUD_CLK4_DIV4]); + dev_err(dev, "failed to register clock %d, ret:%d\n", CLK_AUD_CLK4_DIV4, ret); + goto fail_pm; + } + + /* audio_clk5 clock fixed divider */ + clk_table[CLK_AUD_CLK5_DIV2] = + devm_clk_hw_register_fixed_factor(dev, + "audio_clk5_div2", + "audio_clk5", + 0, + 1, 2); + if (IS_ERR(clk_table[CLK_AUD_CLK5_DIV2])) { + ret = PTR_ERR(clk_table[CLK_AUD_CLK5_DIV2]); + dev_err(dev, "failed to register clock %d, ret:%d\n", CLK_AUD_CLK5_DIV2, ret); + goto fail_pm; + } + + for (i = 0; i < devtype_data->clk_cfg_size; i++) { + clk_table[devtype_data->clk_cfg[i].id] = + sky1_audss_clk_register(dev, + devtype_data->clk_cfg[i].name, + devtype_data->clk_cfg[i].parent_names, + devtype_data->clk_cfg[i].num_parents, + priv->regmap_cru, + devtype_data->clk_cfg[i].mux_table, + devtype_data->clk_cfg[i].mux_cfg, + devtype_data->clk_cfg[i].div_cfg, + devtype_data->clk_cfg[i].gate_cfg, + devtype_data->clk_cfg[i].flags, + &priv->lock); + if (IS_ERR(clk_table[devtype_data->clk_cfg[i].id])) { + ret = PTR_ERR(clk_table[devtype_data->clk_cfg[i].id]); + dev_err(dev, "failed to register clock %d, ret:%d\n", + devtype_data->clk_cfg[i].id, ret); + goto fail_pm; + } + } + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, priv->clk_data); + if (ret) { + dev_err(dev, "failed to add clock provider: %d\n", ret); + goto fail_pm; + } + + ret = sky1_audss_reset_controller_register(dev); + if (ret) { + dev_err(dev, "failed to register reset controller: %d\n", ret); + goto fail_pm; + } + + pm_runtime_put_sync(dev); + + return 0; + +fail_pm: + pm_runtime_put_sync(dev); + return ret; +} + +static int __maybe_unused sky1_audss_clk_runtime_suspend(struct device *dev) +{ + struct sky1_audss_clks_priv *priv = dev_get_drvdata(dev); + const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data; + unsigned long flags; + int i; + + spin_lock_irqsave(&priv->lock, flags); + for (i = 0; i < devtype_data->reg_save_size; i++) + regmap_read(priv->regmap_cru, + devtype_data->reg_save[i][0], &devtype_data->reg_save[i][1]); + spin_unlock_irqrestore(&priv->lock, flags); + + reset_control_assert(priv->rst_noc); + + sky1_audss_clks_disable(priv); + + return 0; +} + +static int __maybe_unused sky1_audss_clk_runtime_resume(struct device *dev) +{ + struct sky1_audss_clks_priv *priv = dev_get_drvdata(dev); + const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data; + unsigned long flags; + int i, ret; + + ret = sky1_audss_clks_enable(priv); + if (ret) { + dev_err(dev, "failed to enable clocks\n"); + return ret; + } + + ret = reset_control_deassert(priv->rst_noc); + if (ret) { + sky1_audss_clks_disable(priv); + return ret; + } + + spin_lock_irqsave(&priv->lock, flags); + for (i = 0; i < devtype_data->reg_save_size; i++) + regmap_write(priv->regmap_cru, + devtype_data->reg_save[i][0], devtype_data->reg_save[i][1]); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static const struct dev_pm_ops sky1_audss_clk_pm_ops = { + SET_RUNTIME_PM_OPS(sky1_audss_clk_runtime_suspend, + sky1_audss_clk_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static const struct sky1_audss_clks_devtype_data sky1_devtype_data = { + .reg_save = sky1_reg_save, + .reg_save_size = ARRAY_SIZE(sky1_reg_save), + .clk_names = sky1_audss_clk_names, + .clk_num = ARRAY_SIZE(sky1_audss_clk_names), + .clk_rate_default = sky1_clk_rate_default, + .clk_cfg = sky1_audss_clks, + .clk_cfg_size = ARRAY_SIZE(sky1_audss_clks), +}; + +static const struct of_device_id sky1_audss_clk_of_match[] = { + { .compatible = "cix,sky1-audss-cru", .data = &sky1_devtype_data, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, sky1_audss_clk_of_match); + +static struct platform_driver sky1_audss_clk_driver = { + .probe = sky1_audss_clk_probe, + .driver = { + .name = "sky1-audss-clk", + .suppress_bind_attrs = true, + .of_match_table = sky1_audss_clk_of_match, + .pm = pm_ptr(&sky1_audss_clk_pm_ops), + }, +}; +module_platform_driver(sky1_audss_clk_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joakim Zhang "); +MODULE_DESCRIPTION("Cixtech Sky1 Audio Subsystem Clock Controller Driver"); diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c index 1cae974e6d1d..8ddb50c67757 100644 --- a/drivers/clk/clk-bd718x7.c +++ b/drivers/clk/clk-bd718x7.c @@ -147,12 +147,12 @@ static int bd71837_clk_probe(struct platform_device *pdev) } static const struct platform_device_id bd718x7_clk_id[] = { - { "bd71837-clk", ROHM_CHIP_TYPE_BD71837 }, - { "bd71847-clk", ROHM_CHIP_TYPE_BD71847 }, - { "bd71828-clk", ROHM_CHIP_TYPE_BD71828 }, - { "bd71815-clk", ROHM_CHIP_TYPE_BD71815 }, - { "bd72720-clk", ROHM_CHIP_TYPE_BD72720 }, - { }, + { .name = "bd71837-clk", .driver_data = ROHM_CHIP_TYPE_BD71837 }, + { .name = "bd71847-clk", .driver_data = ROHM_CHIP_TYPE_BD71847 }, + { .name = "bd71828-clk", .driver_data = ROHM_CHIP_TYPE_BD71828 }, + { .name = "bd71815-clk", .driver_data = ROHM_CHIP_TYPE_BD71815 }, + { .name = "bd72720-clk", .driver_data = ROHM_CHIP_TYPE_BD72720 }, + { } }; MODULE_DEVICE_TABLE(platform, bd718x7_clk_id); diff --git a/drivers/clk/clk-cdce706.c b/drivers/clk/clk-cdce706.c index b7063cf5c5c1..828125a8610b 100644 --- a/drivers/clk/clk-cdce706.c +++ b/drivers/clk/clk-cdce706.c @@ -679,7 +679,7 @@ MODULE_DEVICE_TABLE(of, cdce706_dt_match); #endif static const struct i2c_device_id cdce706_id[] = { - { "cdce706" }, + { .name = "cdce706" }, { } }; MODULE_DEVICE_TABLE(i2c, cdce706_id); diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c index 0b2ad21e6e4d..30d0493c7718 100644 --- a/drivers/clk/clk-cdce925.c +++ b/drivers/clk/clk-cdce925.c @@ -826,10 +826,10 @@ static const struct clk_cdce925_chip_info clk_cdce949_info = { }; static const struct i2c_device_id cdce925_id[] = { - { "cdce913", (kernel_ulong_t)&clk_cdce913_info }, - { "cdce925", (kernel_ulong_t)&clk_cdce925_info }, - { "cdce937", (kernel_ulong_t)&clk_cdce937_info }, - { "cdce949", (kernel_ulong_t)&clk_cdce949_info }, + { .name = "cdce913", .driver_data = (kernel_ulong_t)&clk_cdce913_info }, + { .name = "cdce925", .driver_data = (kernel_ulong_t)&clk_cdce925_info }, + { .name = "cdce937", .driver_data = (kernel_ulong_t)&clk_cdce937_info }, + { .name = "cdce949", .driver_data = (kernel_ulong_t)&clk_cdce949_info }, { } }; MODULE_DEVICE_TABLE(i2c, cdce925_id); diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 835b1e4e5869..11842bce5918 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -462,3 +462,4 @@ struct clk_hw *devm_clk_hw_register_composite_pdata(struct device *dev, rate_hw, rate_ops, gate_hw, gate_ops, flags); } +EXPORT_SYMBOL_GPL(devm_clk_hw_register_composite_pdata); diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c index 303a0bb26e54..550b8ae375a2 100644 --- a/drivers/clk/clk-conf.c +++ b/drivers/clk/clk-conf.c @@ -155,6 +155,78 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) return 0; } +static int __set_clk_spread_spectrum(struct device_node *node, bool clk_supplier) +{ + u32 elem_size = sizeof(struct clk_spread_spectrum); + struct clk_spread_spectrum *sscs; + struct of_phandle_args clkspec; + int rc, count, index; + struct clk *clk; + + /* modfreq, spreadPercent, modmethod */ + count = of_property_count_elems_of_size(node, "assigned-clock-sscs", elem_size); + if (count <= 0) + return 0; + + sscs = kcalloc(count, elem_size, GFP_KERNEL); + if (!sscs) + return -ENOMEM; + + rc = of_property_read_u32_array(node, "assigned-clock-sscs", (u32 *)sscs, + count * 3); + if (rc) + goto free_sscs; + + for (index = 0; index < count; index++) { + struct clk_spread_spectrum *conf = &sscs[index]; + struct clk_hw *hw; + + if (!conf->modfreq_hz && !conf->spread_bp && !conf->method) + continue; + + rc = of_parse_phandle_with_args(node, "assigned-clocks", "#clock-cells", + index, &clkspec); + if (rc < 0) { + /* skip empty (null) phandles */ + if (rc == -ENOENT) { + rc = 0; + continue; + } else + goto free_sscs; + } + + if (clkspec.np == node && !clk_supplier) { + of_node_put(clkspec.np); + goto free_sscs; + } + + clk = of_clk_get_from_provider(&clkspec); + of_node_put(clkspec.np); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + pr_warn("clk: couldn't get clock %d for %pOF\n", + index, node); + rc = PTR_ERR(clk); + goto free_sscs; + } + + hw = __clk_get_hw(clk); + rc = clk_hw_set_spread_spectrum(hw, conf); + if (rc < 0) { + pr_err("clk: couldn't set %s clk spread spectrum %u %u %u: %d\n", + __clk_get_name(clk), conf->modfreq_hz, conf->spread_bp, + conf->method, rc); + /* Do not fail */ + rc = 0; + } + clk_put(clk); + } + +free_sscs: + kfree(sscs); + return rc; +} + /** * of_clk_set_defaults() - parse and set assigned clocks configuration * @node: device node to apply clock settings for @@ -174,6 +246,10 @@ int of_clk_set_defaults(struct device_node *node, bool clk_supplier) if (!node) return 0; + rc = __set_clk_spread_spectrum(node, clk_supplier); + if (rc < 0) + return rc; + rc = __set_clk_parents(node, clk_supplier); if (rc < 0) return rc; diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c index 8800472ba63f..e415bdff4d97 100644 --- a/drivers/clk/clk-cs2000-cp.c +++ b/drivers/clk/clk-cs2000-cp.c @@ -122,8 +122,8 @@ static const struct of_device_id cs2000_of_match[] = { MODULE_DEVICE_TABLE(of, cs2000_of_match); static const struct i2c_device_id cs2000_id[] = { - { "cs2000-cp", }, - {} + { .name = "cs2000-cp" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs2000_id); @@ -404,11 +404,7 @@ static int cs2000_enable(struct clk_hw *hw) if (ret < 0) return ret; - ret = cs2000_wait_pll_lock(priv); - if (ret < 0) - return ret; - - return ret; + return cs2000_wait_pll_lock(priv); } static void cs2000_disable(struct clk_hw *hw) diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c index 994d5bc5168b..4cc3863bd7da 100644 --- a/drivers/clk/clk-devres.c +++ b/drivers/clk/clk-devres.c @@ -99,6 +99,11 @@ struct clk *devm_clk_get_optional_enabled(struct device *dev, const char *id) } EXPORT_SYMBOL_GPL(devm_clk_get_optional_enabled); +static void devm_clk_disable_unprepare(void *data) +{ + clk_disable_unprepare(data); +} + struct clk *devm_clk_get_optional_enabled_with_rate(struct device *dev, const char *id, unsigned long rate) @@ -106,8 +111,7 @@ struct clk *devm_clk_get_optional_enabled_with_rate(struct device *dev, struct clk *clk; int ret; - clk = __devm_clk_get(dev, id, clk_get_optional, NULL, - clk_disable_unprepare); + clk = devm_clk_get_optional(dev, id); if (IS_ERR(clk)) return ERR_CAST(clk); @@ -119,6 +123,10 @@ struct clk *devm_clk_get_optional_enabled_with_rate(struct device *dev, if (ret) goto out_put_clk; + ret = devm_add_action_or_reset(dev, devm_clk_disable_unprepare, clk); + if (ret) + goto out_put_clk; + return clk; out_put_clk: @@ -222,6 +230,13 @@ err_clk_get: return ret; } +int __must_check devm_clk_bulk_get_enable(struct device *dev, int num_clks, + struct clk_bulk_data *clks) +{ + return __devm_clk_bulk_get_enable(dev, num_clks, clks, false); +} +EXPORT_SYMBOL_GPL(devm_clk_bulk_get_enable); + int __must_check devm_clk_bulk_get_optional_enable(struct device *dev, int num_clks, struct clk_bulk_data *clks) { diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index b3b485d23ea8..f68216c65d3d 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -15,6 +15,7 @@ #include #include #include +#include /* * DOC: basic adjustable divider clock that cannot gate @@ -301,6 +302,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent, int i, bestdiv = 0; unsigned long parent_rate, best = 0, now, maxdiv; unsigned long parent_rate_saved = *best_parent_rate; + unsigned long target_parent_rate; if (!rate) rate = 1; @@ -315,15 +317,11 @@ static int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent, return bestdiv; } - /* - * The maximum divider we can use without overflowing - * unsigned long in rate * i below - */ - maxdiv = min(ULONG_MAX / rate, maxdiv); - for (i = _next_div(table, 0, flags); i <= maxdiv; i = _next_div(table, i, flags)) { - if (rate * i == parent_rate_saved) { + bool overflow = check_mul_overflow(rate, (unsigned long)i, &target_parent_rate); + + if (!overflow && target_parent_rate == parent_rate_saved) { /* * It's the most ideal case if the requested rate can be * divided from parent clock without needing to change @@ -332,13 +330,24 @@ static int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent, *best_parent_rate = parent_rate_saved; return i; } - parent_rate = clk_hw_round_rate(parent, rate * i); + /* + * Clamp target_parent_rate to ULONG_MAX on overflow. The true + * required parent rate exceeds what can be represented, so ask + * the parent for the highest rate it can produce. There is no + * point continuing the loop past this since larger dividers + * only move further from the requested rate. + */ + if (overflow) + target_parent_rate = ULONG_MAX; + parent_rate = clk_hw_round_rate(parent, target_parent_rate); now = DIV_ROUND_UP_ULL((u64)parent_rate, i); if (_is_best_div(rate, now, best, flags)) { bestdiv = i; best = now; *best_parent_rate = parent_rate; } + if (overflow) + break; } if (!bestdiv) { diff --git a/drivers/clk/clk-divider_test.c b/drivers/clk/clk-divider_test.c new file mode 100644 index 000000000000..8ee1614128d0 --- /dev/null +++ b/drivers/clk/clk-divider_test.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for clk_divider_bestdiv() + */ +#include +#include +#include +#include +#include + +#define PARENT_RATE_1GHZ GIGA +#define PARENT_RATE_2GHZ (2 * GIGA) +#define PARENT_RATE_4GHZ (4 * GIGA) + +KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_fixed_rate_wrapper, + clk_hw_unregister_fixed_rate, struct clk_hw *); +KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_divider_wrapper, + clk_hw_unregister_divider, struct clk_hw *); +KUNIT_DEFINE_ACTION_WRAPPER(clk_hw_unregister_mux_wrapper, + clk_hw_unregister_mux, struct clk_hw *); + +static const struct clk_div_table bestdiv_table[] = { + { .val = 0, .div = 2 }, + { .val = 1, .div = 4 }, + { .val = 2, .div = 8 }, + { /* sentinel */ } +}; + +/* + * Test that clk_round_rate(clk, ULONG_MAX) returns the maximum achievable + * rate for a divider clock. + */ +static void clk_divider_bestdiv_ulong_max_returns_max_rate(struct kunit *test) +{ + struct clk_hw *parent_hw, *div_hw; + unsigned long rate; + u32 *fake_reg; + + fake_reg = kunit_kzalloc(test, sizeof(*fake_reg), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg); + + parent_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-parent", + NULL, 0, PARENT_RATE_1GHZ); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper, + parent_hw)); + + div_hw = clk_hw_register_divider_table(NULL, "bestdiv-div", + "bestdiv-parent", + CLK_SET_RATE_PARENT, + (void __iomem __force *)fake_reg, + 0, 2, 0, bestdiv_table, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_divider_wrapper, + div_hw)); + + /* + * ULONG_MAX is the canonical way to probe the maximum rate a clock + * can produce. + */ + rate = clk_hw_round_rate(div_hw, ULONG_MAX); + KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_1GHZ / 2); +} + +/* + * Test that clk_round_rate(clk, ULONG_MAX) returns the correct maximum rate + * when a mux clock sits between a divider and its parent candidates. + * + * Topology: + * + * [fixed 4 GHz] --\ + * +--> [mux CLK_SET_RATE_PARENT] --> [div {2,4,8} CLK_SET_RATE_PARENT] + * [fixed 2 GHz] --/ + * + */ +static void clk_divider_bestdiv_mux_ulong_max_returns_max_rate(struct kunit *test) +{ + static const char * const mux_parents[] = { + "bestdiv-mux-parent-a", + "bestdiv-mux-parent-b", + }; + struct clk_hw *parent_a_hw, *parent_b_hw, *mux_hw, *div_hw; + u32 *fake_reg_mux, *fake_reg_div; + unsigned long rate; + + fake_reg_mux = kunit_kzalloc(test, sizeof(*fake_reg_mux), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg_mux); + + fake_reg_div = kunit_kzalloc(test, sizeof(*fake_reg_div), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_reg_div); + + /* Higher-rate parent: the mux should select this for ULONG_MAX. */ + parent_a_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-mux-parent-a", + NULL, 0, PARENT_RATE_4GHZ); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_a_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper, + parent_a_hw)); + + /* Lower-rate parent: should not be selected. */ + parent_b_hw = clk_hw_register_fixed_rate(NULL, "bestdiv-mux-parent-b", + NULL, 0, PARENT_RATE_2GHZ); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent_b_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_fixed_rate_wrapper, + parent_b_hw)); + + /* + * 1-bit mux register selects between the two parents. + * CLK_SET_RATE_PARENT allows the divider's rate request to + * propagate into clk_mux_determine_rate(). + */ + mux_hw = clk_hw_register_mux(NULL, "bestdiv-mux", + mux_parents, ARRAY_SIZE(mux_parents), + CLK_SET_RATE_PARENT, + (void __iomem __force *)fake_reg_mux, + 0, 1, 0, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mux_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_mux_wrapper, + mux_hw)); + + div_hw = clk_hw_register_divider_table(NULL, "bestdiv-mux-div", + "bestdiv-mux", + CLK_SET_RATE_PARENT, + (void __iomem __force *)fake_reg_div, + 0, 2, 0, bestdiv_table, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, div_hw); + KUNIT_ASSERT_EQ(test, 0, + kunit_add_action_or_reset(test, clk_hw_unregister_divider_wrapper, + div_hw)); + + rate = clk_hw_round_rate(div_hw, ULONG_MAX); + KUNIT_EXPECT_EQ(test, rate, PARENT_RATE_4GHZ / 2); +} + +static struct kunit_case clk_divider_bestdiv_test_cases[] = { + KUNIT_CASE(clk_divider_bestdiv_ulong_max_returns_max_rate), + KUNIT_CASE(clk_divider_bestdiv_mux_ulong_max_returns_max_rate), + {} +}; + +static struct kunit_suite clk_divider_bestdiv_test_suite = { + .name = "clk_divider_bestdiv", + .test_cases = clk_divider_bestdiv_test_cases, +}; + +kunit_test_suite(clk_divider_bestdiv_test_suite); + +MODULE_DESCRIPTION("KUnit tests for clk divider"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c index 1ab0e2eca5d3..5c45aacb6409 100644 --- a/drivers/clk/clk-en7523.c +++ b/drivers/clk/clk-en7523.c @@ -57,6 +57,7 @@ #define REG_RST_CTRL2 0x830 #define REG_RST_CTRL1 0x834 +#define REG_PCIE_HB_RST BIT(29) #define EN751221_REG_RST_DMT 0x84 #define EN751221_REG_RST_USB 0xec @@ -338,6 +339,7 @@ static const struct en_clk_desc en7581_base_clks[] = { static const u16 en7581_rst_ofs[] = { REG_RST_CTRL2, REG_RST_CTRL1, + REG_NP_SCU_PCIC, }; static const u16 en751221_rst_ofs[] = { @@ -450,6 +452,11 @@ static const u16 en7581_rst_map[] = { [EN7581_CPU_TIMER_RST] = RST_NR_PER_BANK + 28, [EN7581_PCIE_HB_RST] = RST_NR_PER_BANK + 29, [EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31, + + /* RST_PCIC */ + [EN7581_PCIC_PERSTOUT0_RST] = 2 * RST_NR_PER_BANK + 29, + [EN7581_PCIC_PERSTOUT1_RST] = 2 * RST_NR_PER_BANK + 26, + [EN7581_PCIC_PERSTOUT2_RST] = 2 * RST_NR_PER_BANK + 16, }; static const u16 en751221_rst_map[] = { @@ -635,9 +642,7 @@ static int en7581_pci_enable(struct clk_hw *hw) void __iomem *np_base = cg->base; u32 val, mask; - mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 | - REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 | - REG_PCI_CONTROL_PERSTOUT; + mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1; val = readl(np_base + REG_PCI_CONTROL); writel(val | mask, np_base + REG_PCI_CONTROL); @@ -650,9 +655,7 @@ static void en7581_pci_disable(struct clk_hw *hw) void __iomem *np_base = cg->base; u32 val, mask; - mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 | - REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 | - REG_PCI_CONTROL_PERSTOUT; + mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1; val = readl(np_base + REG_PCI_CONTROL); writel(val & ~mask, np_base + REG_PCI_CONTROL); usleep_range(1000, 2000); @@ -754,11 +757,17 @@ static int en7523_reset_update(struct reset_controller_dev *rcdev, unsigned long id, bool assert) { struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev); - void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK]; + u32 offset = rst_data->bank_ofs[id / RST_NR_PER_BANK]; + void __iomem *addr = rst_data->base + offset; + bool inverted = false; u32 val; + /* For PCIC reset logic is inverted, 0:assert 1:deassert */ + if (offset == REG_NP_SCU_PCIC) + inverted = true; + val = readl(addr); - if (assert) + if (assert ^ inverted) val |= BIT(id % RST_NR_PER_BANK); else val &= ~BIT(id % RST_NR_PER_BANK); @@ -783,9 +792,17 @@ static int en7523_reset_status(struct reset_controller_dev *rcdev, unsigned long id) { struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev); - void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK]; + u32 offset = rst_data->bank_ofs[id / RST_NR_PER_BANK]; + void __iomem *addr = rst_data->base + offset; + bool inverted = false; + u32 val; - return !!(readl(addr) & BIT(id % RST_NR_PER_BANK)); + /* For PCIC reset logic is inverted, 0:assert 1:deassert */ + if (offset == REG_NP_SCU_PCIC) + inverted = true; + + val = readl(addr) & BIT(id % RST_NR_PER_BANK); + return inverted ? !val : !!val; } static int en7523_reset_xlate(struct reset_controller_dev *rcdev, @@ -853,6 +870,12 @@ static int en7581_clk_hw_init(struct platform_device *pdev, val = readl(base + REG_NP_SCU_PCIC); writel(val | 3, base + REG_NP_SCU_PCIC); + val = readl(base + REG_RST_CTRL1); + val |= REG_PCIE_HB_RST; + writel(val, base + REG_RST_CTRL1); + val &= ~REG_PCIE_HB_RST; + writel(val, base + REG_RST_CTRL1); + return en7581_reset_register(&pdev->dev, base, en7581_rst_map, ARRAY_SIZE(en7581_rst_map), en7581_rst_ofs); diff --git a/drivers/clk/clk-k230.c b/drivers/clk/clk-k230.c index cfc437038e4e..f34a3e6d3bca 100644 --- a/drivers/clk/clk-k230.c +++ b/drivers/clk/clk-k230.c @@ -2227,6 +2227,7 @@ static int k230_clk_set_rate_mul(struct clk_hw *hw, unsigned long rate, guard(spinlock)(rate_self->lock); mul_reg = readl(rate_self->reg + clk->mul_reg_off); + mul_reg &= ~(rate_self->mul_mask << rate_self->mul_shift); mul_reg |= ((mul - 1) & rate_self->mul_mask) << (rate_self->mul_shift); mul_reg |= BIT(rate_self->write_enable_bit); writel(mul_reg, rate_self->reg + clk->mul_reg_off); @@ -2257,6 +2258,7 @@ static int k230_clk_set_rate_div(struct clk_hw *hw, unsigned long rate, guard(spinlock)(rate_self->lock); div_reg = readl(rate_self->reg + clk->div_reg_off); + div_reg &= ~(rate_self->div_mask << rate_self->div_shift); div_reg |= ((div - 1) & rate_self->div_mask) << (rate_self->div_shift); div_reg |= BIT(rate_self->write_enable_bit); writel(div_reg, rate_self->reg + clk->div_reg_off); @@ -2287,11 +2289,13 @@ static int k230_clk_set_rate_mul_div(struct clk_hw *hw, unsigned long rate, guard(spinlock)(rate_self->lock); div_reg = readl(rate_self->reg + clk->div_reg_off); + div_reg &= ~(rate_self->div_mask << rate_self->div_shift); div_reg |= ((div - 1) & rate_self->div_mask) << (rate_self->div_shift); div_reg |= BIT(rate_self->write_enable_bit); writel(div_reg, rate_self->reg + clk->div_reg_off); mul_reg = readl(rate_self->reg + clk->mul_reg_off); + mul_reg &= ~(rate_self->mul_mask << rate_self->mul_shift); mul_reg |= ((mul - 1) & rate_self->mul_mask) << (rate_self->mul_shift); mul_reg |= BIT(rate_self->write_enable_bit); writel(mul_reg, rate_self->reg + clk->mul_reg_off); diff --git a/drivers/clk/clk-lmk04832.c b/drivers/clk/clk-lmk04832.c index 9bf86caad829..f9a0684d2628 100644 --- a/drivers/clk/clk-lmk04832.c +++ b/drivers/clk/clk-lmk04832.c @@ -175,10 +175,6 @@ #define LMK04832_REG_RB_HOLDOVER 0x188 #define LMK04832_REG_SPI_LOCK 0x555 -enum lmk04832_device_types { - LMK04832, -}; - /** * struct lmk04832_device_info - Holds static device information that is * specific to the chip revision @@ -197,14 +193,12 @@ struct lmk04832_device_info { unsigned int vco1_range[2]; }; -static const struct lmk04832_device_info lmk04832_device_info[] = { - [LMK04832] = { - .pid = 0x63d1, /* WARNING PROD_ID is inverted in the datasheet */ - .maskrev = 0x70, - .num_channels = 14, - .vco0_range = { 2440, 2580 }, - .vco1_range = { 2945, 3255 }, - }, +static const struct lmk04832_device_info lmk04832_device_info = { + .pid = 0x63d1, /* WARNING PROD_ID is inverted in the datasheet */ + .maskrev = 0x70, + .num_channels = 14, + .vco0_range = { 2440, 2580 }, + .vco1_range = { 2945, 3255 }, }; enum lmk04832_rdbk_type { @@ -422,11 +416,10 @@ static unsigned long lmk04832_vco_recalc_rate(struct clk_hw *hw, */ static int lmk04832_check_vco_ranges(struct lmk04832 *lmk, unsigned long rate) { - struct spi_device *spi = to_spi_device(lmk->dev); const struct lmk04832_device_info *info; unsigned long mhz = rate / 1000000; - info = &lmk04832_device_info[spi_get_device_id(spi)->driver_data]; + info = &lmk04832_device_info; if (mhz >= info->vco0_range[0] && mhz <= info->vco0_range[1]) return LMK04832_VAL_VCO_MUX_VCO0; @@ -822,13 +815,6 @@ static int lmk04832_sclk_sync_sequence(struct lmk04832 *lmk) if (ret) return ret; - ret = regmap_update_bits(lmk->regmap, LMK04832_REG_SYNC, - LMK04832_BIT_SYNC_MODE, - FIELD_PREP(LMK04832_BIT_SYNC_MODE, - lmk->sync_mode)); - if (ret) - return ret; - /* * 9. (optional) if SCLKx_y_DIS_MODE was used to mute SYSREF outputs * during the SYNC event, restore SCLKx_y_DIS_MODE=0 for active state, @@ -843,7 +829,10 @@ static int lmk04832_sclk_sync_sequence(struct lmk04832 *lmk) * SYNC pulse to delay the output by some number of VCO counts). */ - return ret; + return regmap_update_bits(lmk->regmap, LMK04832_REG_SYNC, + LMK04832_BIT_SYNC_MODE, + FIELD_PREP(LMK04832_BIT_SYNC_MODE, + lmk->sync_mode)); } static int lmk04832_sclk_is_enabled(struct clk_hw *hw) @@ -1405,7 +1394,7 @@ static int lmk04832_probe(struct spi_device *spi) int ret; int i; - info = &lmk04832_device_info[spi_get_device_id(spi)->driver_data]; + info = &lmk04832_device_info; lmk = devm_kzalloc(&spi->dev, sizeof(struct lmk04832), GFP_KERNEL); if (!lmk) @@ -1553,8 +1542,8 @@ static int lmk04832_probe(struct spi_device *spi) } static const struct spi_device_id lmk04832_id[] = { - { "lmk04832", LMK04832 }, - {} + { .name = "lmk04832" }, + { } }; MODULE_DEVICE_TABLE(spi, lmk04832_id); diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c index 9149ce4f702d..e6fde914c5ef 100644 --- a/drivers/clk/clk-max77686.c +++ b/drivers/clk/clk-max77686.c @@ -264,10 +264,10 @@ static int max77686_clk_probe(struct platform_device *pdev) } static const struct platform_device_id max77686_clk_id[] = { - { "max77686-clk", .driver_data = CHIP_MAX77686, }, - { "max77802-clk", .driver_data = CHIP_MAX77802, }, - { "max77620-clock", .driver_data = CHIP_MAX77620, }, - {}, + { .name = "max77686-clk", .driver_data = CHIP_MAX77686 }, + { .name = "max77802-clk", .driver_data = CHIP_MAX77802 }, + { .name = "max77620-clock", .driver_data = CHIP_MAX77620 }, + { } }; MODULE_DEVICE_TABLE(platform, max77686_clk_id); diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c index 3786a0153ad1..40663ef3ef0a 100644 --- a/drivers/clk/clk-moxart.c +++ b/drivers/clk/clk-moxart.c @@ -17,7 +17,6 @@ static void __init moxart_of_pll_clk_init(struct device_node *node) { void __iomem *base; struct clk_hw *hw; - struct clk *ref_clk; unsigned int mul; const char *name = node->name; const char *parent_name; @@ -34,12 +33,6 @@ static void __init moxart_of_pll_clk_init(struct device_node *node) mul = readl(base + 0x30) >> 3 & 0x3f; iounmap(base); - ref_clk = of_clk_get(node, 0); - if (IS_ERR(ref_clk)) { - pr_err("%pOF: of_clk_get failed\n", node); - return; - } - hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, mul, 1); if (IS_ERR(hw)) { pr_err("%pOF: failed to register clock\n", node); @@ -56,7 +49,6 @@ static void __init moxart_of_apb_clk_init(struct device_node *node) { void __iomem *base; struct clk_hw *hw; - struct clk *pll_clk; unsigned int div, val; unsigned int div_idx[] = { 2, 3, 4, 6, 8}; const char *name = node->name; @@ -78,12 +70,6 @@ static void __init moxart_of_apb_clk_init(struct device_node *node) val = 0; div = div_idx[val] * 2; - pll_clk = of_clk_get(node, 0); - if (IS_ERR(pll_clk)) { - pr_err("%pOF: of_clk_get failed\n", node); - return; - } - hw = clk_hw_register_fixed_factor(NULL, name, parent_name, 0, 1, div); if (IS_ERR(hw)) { pr_err("%pOF: failed to register clock\n", node); diff --git a/drivers/clk/clk-palmas.c b/drivers/clk/clk-palmas.c index 39049f62dbbb..fdbf8445ebc2 100644 --- a/drivers/clk/clk-palmas.c +++ b/drivers/clk/clk-palmas.c @@ -194,6 +194,13 @@ static void palmas_clks_get_clk_data(struct platform_device *pdev, cinfo->ext_control_pin = prop; } +static void palmas_clks_unprepare_ext_control(void *data) +{ + struct palmas_clock_info *cinfo = data; + + clk_unprepare(cinfo->hw.clk); +} + static int palmas_clks_init_configure(struct palmas_clock_info *cinfo) { int ret; @@ -214,13 +221,18 @@ static int palmas_clks_init_configure(struct palmas_clock_info *cinfo) return ret; } + ret = devm_add_action_or_reset(cinfo->dev, + palmas_clks_unprepare_ext_control, + cinfo); + if (ret) + return ret; + ret = palmas_ext_control_req_config(cinfo->palmas, cinfo->clk_desc->sleep_reqstr_id, cinfo->ext_control_pin, true); if (ret < 0) { dev_err(cinfo->dev, "Ext config for %s failed, %d\n", cinfo->clk_desc->clk_name, ret); - clk_unprepare(cinfo->hw.clk); return ret; } } @@ -230,7 +242,6 @@ static int palmas_clks_init_configure(struct palmas_clock_info *cinfo) static int palmas_clks_probe(struct platform_device *pdev) { struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); - struct device_node *node = pdev->dev.of_node; const struct palmas_clks_of_match_data *match_data; struct palmas_clock_info *cinfo; int ret; @@ -264,24 +275,19 @@ static int palmas_clks_probe(struct platform_device *pdev) return ret; } - ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &cinfo->hw); + ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get, + &cinfo->hw); if (ret < 0) dev_err(&pdev->dev, "Fail to add clock driver, %d\n", ret); return ret; } -static void palmas_clks_remove(struct platform_device *pdev) -{ - of_clk_del_provider(pdev->dev.of_node); -} - static struct platform_driver palmas_clks_driver = { .driver = { .name = "palmas-clk", .of_match_table = palmas_clks_of_match, }, .probe = palmas_clks_probe, - .remove = palmas_clks_remove, }; module_platform_driver(palmas_clks_driver); diff --git a/drivers/clk/clk-renesas-pcie.c b/drivers/clk/clk-renesas-pcie.c index 2f6d80ee77cc..564550c80389 100644 --- a/drivers/clk/clk-renesas-pcie.c +++ b/drivers/clk/clk-renesas-pcie.c @@ -394,9 +394,9 @@ static const struct rs9_chip_info renesas_9fgv0841_info = { }; static const struct i2c_device_id rs9_id[] = { - { "9fgv0241", .driver_data = (kernel_ulong_t)&renesas_9fgv0241_info }, - { "9fgv0441", .driver_data = (kernel_ulong_t)&renesas_9fgv0441_info }, - { "9fgv0841", .driver_data = (kernel_ulong_t)&renesas_9fgv0841_info }, + { .name = "9fgv0241", .driver_data = (kernel_ulong_t)&renesas_9fgv0241_info }, + { .name = "9fgv0441", .driver_data = (kernel_ulong_t)&renesas_9fgv0441_info }, + { .name = "9fgv0841", .driver_data = (kernel_ulong_t)&renesas_9fgv0841_info }, { } }; MODULE_DEVICE_TABLE(i2c, rs9_id); diff --git a/drivers/clk/clk-rp1.c b/drivers/clk/clk-rp1.c index fd144755b879..0f7d9fd77481 100644 --- a/drivers/clk/clk-rp1.c +++ b/drivers/clk/clk-rp1.c @@ -1174,12 +1174,6 @@ static unsigned long rp1_varsrc_recalc_rate(struct clk_hw *hw, return clock->cached_rate; } -static int rp1_varsrc_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return 0; -} - static const struct clk_ops rp1_pll_core_ops = { .is_prepared = rp1_pll_core_is_on, .prepare = rp1_pll_core_on, @@ -1227,7 +1221,7 @@ static const struct clk_ops rp1_clk_ops = { static const struct clk_ops rp1_varsrc_ops = { .set_rate = rp1_varsrc_set_rate, .recalc_rate = rp1_varsrc_recalc_rate, - .determine_rate = rp1_varsrc_determine_rate, + .determine_rate = clk_determine_rate_noop, }; static struct clk_hw *rp1_register_pll(struct rp1_clockman *clockman, diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index ff7ce12a5da6..fa5ac8f673f6 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -225,12 +225,12 @@ static void s2mps11_clk_remove(struct platform_device *pdev) } static const struct platform_device_id s2mps11_clk_id[] = { - { "s2mpg10-clk", S2MPG10}, - { "s2mps11-clk", S2MPS11X}, - { "s2mps13-clk", S2MPS13X}, - { "s2mps14-clk", S2MPS14X}, - { "s5m8767-clk", S5M8767X}, - { }, + { .name = "s2mpg10-clk", .driver_data = S2MPG10 }, + { .name = "s2mps11-clk", .driver_data = S2MPS11X }, + { .name = "s2mps13-clk", .driver_data = S2MPS13X }, + { .name = "s2mps14-clk", .driver_data = S2MPS14X }, + { .name = "s5m8767-clk", .driver_data = S5M8767X }, + { } }; MODULE_DEVICE_TABLE(platform, s2mps11_clk_id); diff --git a/drivers/clk/clk-scmi-oem.c b/drivers/clk/clk-scmi-oem.c new file mode 100644 index 000000000000..be11d359b4ec --- /dev/null +++ b/drivers/clk/clk-scmi-oem.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The Vendor OEM extension for System Control and Power Interface (SCMI) + * Protocol based clock driver + * + * Copyright 2025 NXP + */ + +#include +#include +#include +#include + +#include "clk-scmi.h" + +#define SCMI_CLOCK_CFG_IMX_SSC 0x80 +#define SCMI_CLOCK_IMX_SS_PERCENTAGE_MASK GENMASK(7, 0) +#define SCMI_CLOCK_IMX_SS_MOD_FREQ_MASK GENMASK(23, 8) +#define SCMI_CLOCK_IMX_SS_ENABLE_MASK BIT(24) + +/* + * Selection is based on SCMI vendor_id/sub_vendor_id and optional machine + * compatible string, without involving impl_ver. impl_ver‑specific behavior + * should be considered a bug and handled via SCMI Quirk framework. + */ +struct scmi_clk_oem_info { + char *vendor_id; + char *sub_vendor_id; + char *compatible; + const void *data; +}; + +static int +scmi_clk_imx_set_spread_spectrum(struct clk_hw *hw, + const struct clk_spread_spectrum *ss_conf) +{ + struct scmi_clk *clk = to_scmi_clk(hw); + int ret; + u32 val; + + /* + * extConfigValue[7:0] - spread percentage (%) + * extConfigValue[23:8] - Modulation Frequency + * extConfigValue[24] - Enable/Disable + * extConfigValue[31:25] - Reserved + */ + val = FIELD_PREP(SCMI_CLOCK_IMX_SS_PERCENTAGE_MASK, ss_conf->spread_bp / 10000); + val |= FIELD_PREP(SCMI_CLOCK_IMX_SS_MOD_FREQ_MASK, ss_conf->modfreq_hz); + if (ss_conf->method != CLK_SPREAD_NO) + val |= SCMI_CLOCK_IMX_SS_ENABLE_MASK; + ret = scmi_proto_clk_ops->config_oem_set(clk->ph, clk->id, + SCMI_CLOCK_CFG_IMX_SSC, + val, false); + if (ret) + dev_warn(clk->dev, + "Failed to set spread spectrum(%u,%u,%u) for clock ID %d\n", + ss_conf->modfreq_hz, ss_conf->spread_bp, ss_conf->method, + clk->id); + + return ret; +} + +static int +scmi_clk_imx_query_oem_feats(const struct scmi_protocol_handle *ph, u32 id, + unsigned int *feats_key) +{ + int ret; + u32 val; + + ret = scmi_proto_clk_ops->config_oem_get(ph, id, + SCMI_CLOCK_CFG_IMX_SSC, + &val, NULL, false); + if (!ret) + *feats_key |= BIT(SCMI_CLK_EXT_OEM_SSC_SUPPORTED); + + return 0; +} + +static const struct scmi_clk_oem scmi_clk_oem_imx = { + .query_ext_oem_feats = scmi_clk_imx_query_oem_feats, + .set_spread_spectrum = scmi_clk_imx_set_spread_spectrum, +}; + +static const struct scmi_clk_oem_info info[] = { + { SCMI_IMX_VENDOR, SCMI_IMX_SUBVENDOR, NULL, &scmi_clk_oem_imx }, +}; + +int scmi_clk_oem_init(struct scmi_device *sdev) +{ + const struct scmi_handle *handle = sdev->handle; + int i, size = ARRAY_SIZE(info); + + for (i = 0; i < size; i++) { + if (strcmp(handle->version->vendor_id, info[i].vendor_id) || + strcmp(handle->version->sub_vendor_id, info[i].sub_vendor_id)) + continue; + if (info[i].compatible && + !of_machine_is_compatible(info[i].compatible)) + continue; + + break; + } + + if (i < size) + dev_set_drvdata(&sdev->dev, (void *)info[i].data); + + return 0; +} diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c index 7c562559ad8b..2dd50c5b4ea8 100644 --- a/drivers/clk/clk-scmi.c +++ b/drivers/clk/clk-scmi.c @@ -13,32 +13,9 @@ #include #include -#define NOT_ATOMIC false -#define ATOMIC true +#include "clk-scmi.h" -enum scmi_clk_feats { - SCMI_CLK_ATOMIC_SUPPORTED, - SCMI_CLK_STATE_CTRL_SUPPORTED, - SCMI_CLK_RATE_CTRL_SUPPORTED, - SCMI_CLK_PARENT_CTRL_SUPPORTED, - SCMI_CLK_DUTY_CYCLE_SUPPORTED, - SCMI_CLK_FEATS_COUNT -}; - -#define SCMI_MAX_CLK_OPS BIT(SCMI_CLK_FEATS_COUNT) - -static const struct scmi_clk_proto_ops *scmi_proto_clk_ops; - -struct scmi_clk { - u32 id; - struct device *dev; - struct clk_hw hw; - const struct scmi_clock_info *info; - const struct scmi_protocol_handle *ph; - struct clk_parent_data *parent_data; -}; - -#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw) +const struct scmi_clk_proto_ops *scmi_proto_clk_ops; static unsigned long scmi_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) @@ -235,6 +212,7 @@ static int scmi_clk_ops_init(struct device *dev, struct scmi_clk *sclk, static const struct clk_ops * scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key) { + struct scmi_clk_oem *oem_data = dev_get_drvdata(dev); struct clk_ops *ops; ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL); @@ -281,11 +259,15 @@ scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key) ops->set_duty_cycle = scmi_clk_set_duty_cycle; } + if (oem_data && (feats_key & BIT(SCMI_CLK_EXT_OEM_SSC_SUPPORTED))) + ops->set_spread_spectrum = oem_data->set_spread_spectrum; + return ops; } /** * scmi_clk_ops_select() - Select a proper set of clock operations + * @sdev: pointer to the SCMI device * @sclk: A reference to an SCMI clock descriptor * @atomic_capable: A flag to indicate if atomic mode is supported by the * transport @@ -310,8 +292,8 @@ scmi_clk_ops_alloc(struct device *dev, unsigned long feats_key) * NULL otherwise. */ static const struct clk_ops * -scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable, - unsigned int atomic_threshold_us, +scmi_clk_ops_select(struct scmi_device *sdev, struct scmi_clk *sclk, + bool atomic_capable, unsigned int atomic_threshold_us, const struct clk_ops **clk_ops_db, size_t db_size) { int ret; @@ -319,6 +301,7 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable, const struct scmi_clock_info *ci = sclk->info; unsigned int feats_key = 0; const struct clk_ops *ops; + struct scmi_clk_oem *oem_data = dev_get_drvdata(&sdev->dev); /* * Note that when transport is atomic but SCMI protocol did not @@ -343,6 +326,9 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable, &val, NULL, false); if (!ret) feats_key |= BIT(SCMI_CLK_DUTY_CYCLE_SUPPORTED); + + if (oem_data && oem_data->query_ext_oem_feats) + oem_data->query_ext_oem_feats(sclk->ph, sclk->id, &feats_key); } if (WARN_ON(feats_key >= db_size)) @@ -400,6 +386,8 @@ static int scmi_clocks_probe(struct scmi_device *sdev) clk_data->num = count; hws = clk_data->hws; + scmi_clk_oem_init(sdev); + transport_is_atomic = handle->is_transport_atomic(handle, &atomic_threshold_us); @@ -431,7 +419,7 @@ static int scmi_clocks_probe(struct scmi_device *sdev) * to avoid sharing the devm_ allocated clk_ops between multiple * SCMI clk driver instances. */ - scmi_ops = scmi_clk_ops_select(sclk, transport_is_atomic, + scmi_ops = scmi_clk_ops_select(sdev, sclk, transport_is_atomic, atomic_threshold_us, scmi_clk_ops_db, ARRAY_SIZE(scmi_clk_ops_db)); diff --git a/drivers/clk/clk-scmi.h b/drivers/clk/clk-scmi.h new file mode 100644 index 000000000000..d7f63f36c56d --- /dev/null +++ b/drivers/clk/clk-scmi.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2025 NXP + */ + +#ifndef __SCMI_CLK_H +#define __SCMI_CLK_H + +#include +#include +#include +#include + +#define NOT_ATOMIC false +#define ATOMIC true + +enum scmi_clk_feats { + SCMI_CLK_ATOMIC_SUPPORTED, + SCMI_CLK_STATE_CTRL_SUPPORTED, + SCMI_CLK_RATE_CTRL_SUPPORTED, + SCMI_CLK_PARENT_CTRL_SUPPORTED, + SCMI_CLK_DUTY_CYCLE_SUPPORTED, + SCMI_CLK_EXT_OEM_SSC_SUPPORTED, + SCMI_CLK_FEATS_COUNT +}; + +#define SCMI_MAX_CLK_OPS BIT(SCMI_CLK_FEATS_COUNT) + +struct scmi_clk { + u32 id; + struct device *dev; + struct clk_hw hw; + const struct scmi_clock_info *info; + const struct scmi_protocol_handle *ph; + struct clk_parent_data *parent_data; +}; + +#define to_scmi_clk(clk) container_of(clk, struct scmi_clk, hw) + +extern const struct scmi_clk_proto_ops *scmi_proto_clk_ops; + +struct scmi_clk_oem { + int (*query_ext_oem_feats)(const struct scmi_protocol_handle *ph, + u32 id, unsigned int *feats_key); + int (*set_spread_spectrum)(struct clk_hw *hw, + const struct clk_spread_spectrum *ss_conf); +}; + +int scmi_clk_oem_init(struct scmi_device *dev); + +#endif diff --git a/drivers/clk/clk-scpi.c b/drivers/clk/clk-scpi.c index 24cee7c9fda6..2328d2abf6d8 100644 --- a/drivers/clk/clk-scpi.c +++ b/drivers/clk/clk-scpi.c @@ -32,18 +32,6 @@ static unsigned long scpi_clk_recalc_rate(struct clk_hw *hw, return clk->scpi_ops->clk_get_val(clk->id); } -static int scpi_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * We can't figure out what rate it will be, so just return the - * rate back to the caller. scpi_clk_recalc_rate() will be called - * after the rate is set and we'll know what rate the clock is - * running at then. - */ - return 0; -} - static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -54,7 +42,7 @@ static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops scpi_clk_ops = { .recalc_rate = scpi_clk_recalc_rate, - .determine_rate = scpi_clk_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = scpi_clk_set_rate, }; diff --git a/drivers/clk/clk-si514.c b/drivers/clk/clk-si514.c index f61590d70575..c0b433ad9372 100644 --- a/drivers/clk/clk-si514.c +++ b/drivers/clk/clk-si514.c @@ -379,7 +379,7 @@ static int si514_probe(struct i2c_client *client) } static const struct i2c_device_id si514_id[] = { - { "si514" }, + { .name = "si514" }, { } }; MODULE_DEVICE_TABLE(i2c, si514_id); diff --git a/drivers/clk/clk-si521xx.c b/drivers/clk/clk-si521xx.c index ceadc07bcb6d..e3fb8df712ef 100644 --- a/drivers/clk/clk-si521xx.c +++ b/drivers/clk/clk-si521xx.c @@ -364,9 +364,9 @@ static int __maybe_unused si521xx_resume(struct device *dev) } static const struct i2c_device_id si521xx_id[] = { - { "si52144", .driver_data = SI521XX_OE_MAP(0x5, 0xc0) }, - { "si52146", .driver_data = SI521XX_OE_MAP(0x15, 0xe0) }, - { "si52147", .driver_data = SI521XX_OE_MAP(0x17, 0xf8) }, + { .name = "si52144", .driver_data = SI521XX_OE_MAP(0x5, 0xc0) }, + { .name = "si52146", .driver_data = SI521XX_OE_MAP(0x15, 0xe0) }, + { .name = "si52147", .driver_data = SI521XX_OE_MAP(0x17, 0xf8) }, { } }; MODULE_DEVICE_TABLE(i2c, si521xx_id); diff --git a/drivers/clk/clk-si5341.c b/drivers/clk/clk-si5341.c index 2499b771cd83..5311c23532b4 100644 --- a/drivers/clk/clk-si5341.c +++ b/drivers/clk/clk-si5341.c @@ -1825,11 +1825,11 @@ static void si5341_remove(struct i2c_client *client) } static const struct i2c_device_id si5341_id[] = { - { "si5340", 0 }, - { "si5341", 1 }, - { "si5342", 2 }, - { "si5344", 4 }, - { "si5345", 5 }, + { .name = "si5340" }, + { .name = "si5341" }, + { .name = "si5342" }, + { .name = "si5344" }, + { .name = "si5345" }, { } }; MODULE_DEVICE_TABLE(i2c, si5341_id); diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index e755db545e2e..20b0c3c48b86 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -1425,10 +1425,10 @@ si53351_of_clk_get(struct of_phandle_args *clkspec, void *data) #endif /* CONFIG_OF */ static const struct i2c_device_id si5351_i2c_ids[] = { - { "si5351a", SI5351_VARIANT_A }, - { "si5351a-msop", SI5351_VARIANT_A3 }, - { "si5351b", SI5351_VARIANT_B }, - { "si5351c", SI5351_VARIANT_C }, + { .name = "si5351a", .driver_data = SI5351_VARIANT_A }, + { .name = "si5351a-msop", .driver_data = SI5351_VARIANT_A3 }, + { .name = "si5351b", .driver_data = SI5351_VARIANT_B }, + { .name = "si5351c", .driver_data = SI5351_VARIANT_C }, { } }; MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids); diff --git a/drivers/clk/clk-si544.c b/drivers/clk/clk-si544.c index 09c06ecec1a5..9c520128e0a2 100644 --- a/drivers/clk/clk-si544.c +++ b/drivers/clk/clk-si544.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Driver for Silicon Labs Si544 Programmable Oscillator + * Driver for Silicon Labs Si544/Si549 Programmable Oscillator * Copyright (C) 2018 Topic Embedded Products * Author: Mike Looijmans */ @@ -40,7 +40,9 @@ #define SI544_MIN_FREQ 200000U /* Si544 Internal oscillator runs at 55.05 MHz */ -#define FXO 55050000U +#define SI544_XO_FREQ 55050000U +/* Si549 Internal oscilator runs at 152.60 MHz */ +#define SI549_XO_FREQ 152600000U /* VCO range is 10.8 .. 12.1 GHz, max depends on speed grade */ #define FVCO_MIN 10800000000ULL @@ -56,11 +58,16 @@ #define DELTA_M_FRAC_NUM 19 #define DELTA_M_FRAC_DEN 20000 +struct si544_clk_desc { + unsigned long max_freq; + unsigned long xo_freq; +}; + struct clk_si544 { struct clk_hw hw; struct regmap *regmap; struct i2c_client *i2c_client; - unsigned long max_freq; + const struct si544_clk_desc *chip_info; }; #define to_clk_si544(_hw) container_of(_hw, struct clk_si544, hw) @@ -79,6 +86,7 @@ struct clk_si544_muldiv { u16 hs_div; u8 ls_div_bits; s32 delta_m; + u32 xo_freq; }; /* Enables or disables the output driver */ @@ -145,6 +153,8 @@ static int si544_get_muldiv(struct clk_si544 *data, settings->delta_m = reg[0] << 8 | reg[1] << 16 | reg[2] << 24; settings->delta_m >>= 8; + settings->xo_freq = data->chip_info->xo_freq; + return 0; } @@ -193,7 +203,7 @@ static bool is_valid_frequency(const struct clk_si544 *data, if (frequency < SI544_MIN_FREQ) return false; - return frequency <= data->max_freq; + return frequency <= data->chip_info->max_freq; } /* Calculate divider settings for a given frequency */ @@ -201,6 +211,7 @@ static int si544_calc_muldiv(struct clk_si544_muldiv *settings, unsigned long frequency) { u64 vco; + u32 fxo = settings->xo_freq; u32 ls_freq; u32 tmp; u8 res; @@ -238,13 +249,13 @@ static int si544_calc_muldiv(struct clk_si544_muldiv *settings, vco = (u64)ls_freq * settings->hs_div; /* Calculate the integer part of the feedback divider */ - tmp = do_div(vco, FXO); + tmp = do_div(vco, fxo); settings->fb_div_int = vco; /* And the fractional bits using the remainder */ vco = (u64)tmp << 32; - vco += FXO / 2; /* Round to nearest multiple */ - do_div(vco, FXO); + vco += fxo / 2; /* Round to nearest multiple */ + do_div(vco, fxo); settings->fb_div_frac = vco; /* Reset the frequency adjustment */ @@ -258,15 +269,16 @@ static unsigned long si544_calc_center_rate( const struct clk_si544_muldiv *settings) { u32 d = settings->hs_div * BIT(settings->ls_div_bits); + u32 fxo = settings->xo_freq; u64 vco; /* Calculate VCO from the fractional part */ - vco = (u64)settings->fb_div_frac * FXO; - vco += (FXO / 2); + vco = (u64)settings->fb_div_frac * fxo; + vco += (fxo / 2); vco >>= 32; /* Add the integer part of the VCO frequency */ - vco += (u64)settings->fb_div_int * FXO; + vco += (u64)settings->fb_div_int * fxo; /* Apply divider to obtain the generated frequency */ do_div(vco, d); @@ -446,7 +458,7 @@ static int si544_probe(struct i2c_client *client) init.num_parents = 0; data->hw.init = &init; data->i2c_client = client; - data->max_freq = (uintptr_t)i2c_get_match_data(client); + data->chip_info = i2c_get_match_data(client); if (of_property_read_string(client->dev.of_node, "clock-output-names", &init.name)) @@ -478,18 +490,54 @@ static int si544_probe(struct i2c_client *client) return 0; } +static const struct si544_clk_desc clk_si544a_info = { + .xo_freq = SI544_XO_FREQ, + .max_freq = 1500000000, +}; + +static const struct si544_clk_desc clk_si544b_info = { + .xo_freq = SI544_XO_FREQ, + .max_freq = 800000000, +}; + +static const struct si544_clk_desc clk_si544c_info = { + .xo_freq = SI544_XO_FREQ, + .max_freq = 325000000, +}; + +static const struct si544_clk_desc clk_si549a_info = { + .xo_freq = SI549_XO_FREQ, + .max_freq = 1500000000, +}; + +static const struct si544_clk_desc clk_si549b_info = { + .xo_freq = SI549_XO_FREQ, + .max_freq = 800000000, +}; + +static const struct si544_clk_desc clk_si549c_info = { + .xo_freq = SI549_XO_FREQ, + .max_freq = 325000000, +}; + static const struct i2c_device_id si544_id[] = { - { "si544a", 1500000000 }, - { "si544b", 800000000 }, - { "si544c", 350000000 }, + { .name = "si544a", .driver_data = (kernel_ulong_t)&clk_si544a_info }, + { .name = "si544b", .driver_data = (kernel_ulong_t)&clk_si544b_info }, + { .name = "si544c", .driver_data = (kernel_ulong_t)&clk_si544c_info }, + { .name = "si549a", .driver_data = (kernel_ulong_t)&clk_si549a_info }, + { .name = "si549b", .driver_data = (kernel_ulong_t)&clk_si549b_info }, + { .name = "si549c", .driver_data = (kernel_ulong_t)&clk_si549c_info }, { } }; MODULE_DEVICE_TABLE(i2c, si544_id); static const struct of_device_id clk_si544_of_match[] = { - { .compatible = "silabs,si544a", .data = (void *)1500000000 }, - { .compatible = "silabs,si544b", .data = (void *)800000000 }, - { .compatible = "silabs,si544c", .data = (void *)350000000 }, + { .compatible = "silabs,si544a", .data = &clk_si544a_info }, + { .compatible = "silabs,si544b", .data = &clk_si544b_info }, + { .compatible = "silabs,si544c", .data = &clk_si544c_info }, + { .compatible = "silabs,si549a", .data = &clk_si549a_info }, + { .compatible = "silabs,si549b", .data = &clk_si549b_info }, + { .compatible = "silabs,si549c", .data = &clk_si549c_info }, { } }; MODULE_DEVICE_TABLE(of, clk_si544_of_match); diff --git a/drivers/clk/clk-si570.c b/drivers/clk/clk-si570.c index b0b1830dd430..3063e38c66da 100644 --- a/drivers/clk/clk-si570.c +++ b/drivers/clk/clk-si570.c @@ -503,10 +503,10 @@ static const struct clk_si570_info clk_si590_info = { }; static const struct i2c_device_id si570_id[] = { - { "si570", (kernel_ulong_t)&clk_si570_info }, - { "si571", (kernel_ulong_t)&clk_si570_info }, - { "si598", (kernel_ulong_t)&clk_si590_info }, - { "si599", (kernel_ulong_t)&clk_si590_info }, + { .name = "si570", .driver_data = (kernel_ulong_t)&clk_si570_info }, + { .name = "si571", .driver_data = (kernel_ulong_t)&clk_si570_info }, + { .name = "si598", .driver_data = (kernel_ulong_t)&clk_si590_info }, + { .name = "si599", .driver_data = (kernel_ulong_t)&clk_si590_info }, { } }; MODULE_DEVICE_TABLE(i2c, si570_id); diff --git a/drivers/clk/clk-versaclock5.c b/drivers/clk/clk-versaclock5.c index 913fcc5675f1..b51148920216 100644 --- a/drivers/clk/clk-versaclock5.c +++ b/drivers/clk/clk-versaclock5.c @@ -1310,14 +1310,14 @@ static const struct vc5_chip_info idt_5p49v6975_info = { }; static const struct i2c_device_id vc5_id[] = { - { "5p49v5923", .driver_data = (kernel_ulong_t)&idt_5p49v5923_info }, - { "5p49v5925", .driver_data = (kernel_ulong_t)&idt_5p49v5925_info }, - { "5p49v5933", .driver_data = (kernel_ulong_t)&idt_5p49v5933_info }, - { "5p49v5935", .driver_data = (kernel_ulong_t)&idt_5p49v5935_info }, - { "5p49v60", .driver_data = (kernel_ulong_t)&idt_5p49v60_info }, - { "5p49v6901", .driver_data = (kernel_ulong_t)&idt_5p49v6901_info }, - { "5p49v6965", .driver_data = (kernel_ulong_t)&idt_5p49v6965_info }, - { "5p49v6975", .driver_data = (kernel_ulong_t)&idt_5p49v6975_info }, + { .name = "5p49v5923", .driver_data = (kernel_ulong_t)&idt_5p49v5923_info }, + { .name = "5p49v5925", .driver_data = (kernel_ulong_t)&idt_5p49v5925_info }, + { .name = "5p49v5933", .driver_data = (kernel_ulong_t)&idt_5p49v5933_info }, + { .name = "5p49v5935", .driver_data = (kernel_ulong_t)&idt_5p49v5935_info }, + { .name = "5p49v60", .driver_data = (kernel_ulong_t)&idt_5p49v60_info }, + { .name = "5p49v6901", .driver_data = (kernel_ulong_t)&idt_5p49v6901_info }, + { .name = "5p49v6965", .driver_data = (kernel_ulong_t)&idt_5p49v6965_info }, + { .name = "5p49v6975", .driver_data = (kernel_ulong_t)&idt_5p49v6975_info }, { } }; MODULE_DEVICE_TABLE(i2c, vc5_id); diff --git a/drivers/clk/clk-versaclock7.c b/drivers/clk/clk-versaclock7.c index adcc603e3259..1340d2f512be 100644 --- a/drivers/clk/clk-versaclock7.c +++ b/drivers/clk/clk-versaclock7.c @@ -1197,7 +1197,7 @@ static int vc7_probe(struct i2c_client *client) if (ret) { dev_err_probe(&client->dev, ret, "unable to register output %d\n", i); - return ret; + goto err_clk; } switch (bank_src_map.type) { @@ -1288,8 +1288,8 @@ static const struct regmap_config vc7_regmap_config = { }; static const struct i2c_device_id vc7_i2c_id[] = { - { "rc21008a", .driver_data = (kernel_ulong_t)&vc7_rc21008a_info }, - {} + { .name = "rc21008a", .driver_data = (kernel_ulong_t)&vc7_rc21008a_info }, + { } }; MODULE_DEVICE_TABLE(i2c, vc7_i2c_id); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 048adfa86a5d..fef87167a60b 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -63,6 +63,57 @@ struct clk_parent_map { int index; }; +/** + * struct clk_core - The internal state of a clk in the clk tree. + * @name: Unique name of the clk for identification. + * @ops: Pointer to hardware-specific operations for this clk. + * @hw: Pointer for traversing from a struct clk to its + * corresponding hardware-specific structure. + * @owner: Kernel module owning this clk (for reference counting). + * @dev: Device associated with this clk (optional) + * @rpm_node: Node for runtime power management list management. + * @of_node: Device tree node associated with this clk (if applicable) + * @parent: Pointer to the current parent in the clock tree. + * @parents: Array of possible parents (for muxes/selectable parents). + * @num_parents: Number of possible parents. + * @new_parent_index: Index of the new parent during parent change operations. + * @rate: Current cached clock rate (Hz). + * @req_rate: The last rate requested by a call to clk_set_rate(). It's + * initialized to clk_core->rate. It's also updated to + * clk_core->rate every time the clock is reparented, and + * when we're doing the orphan -> !orphan transition. + * @new_rate: New rate to be set during a rate change operation. + * @new_parent: Pointer to new parent during parent change. This is also + * used when a clk's rate is changed. + * @new_child: Pointer to new child during reparenting. This is also + * used when a clk's rate is changed. + * @flags: Clock property and capability flags. See + * `clk framework flags`. + * @orphan: True if this clk is currently orphaned. + * @rpm_enabled: True if runtime power management is enabled for this clk. + * @enable_count: Reference count of enables. + * @prepare_count: Reference count of prepares. + * @protect_count: Protection reference count against disable. + * @min_rate: Minimum supported clock rate (Hz). + * @max_rate: Maximum supported clock rate (Hz). + * @accuracy: Accuracy of the clock rate (parts per billion). + * @phase: Current phase (degrees). + * @duty: Current duty cycle configuration (as ratio: num/den). + * @children: All of the children of this clk. + * @child_node: Node for linking as a child in the parent's list. + * @hashtable_node: Node for hash table that allows fast clk lookup by name. + * @clks: All of the clk consumers registered. + * @notifier_count: Number of notifiers registered for this clk. + * @dentry: DebugFS entry for this clk. + * @debug_node: DebugFS node for this clk. + * @ref: Reference count for structure lifetime management. + * + * Managed by the clk framework. Clk providers and consumers do not interact + * with this structure directly. Instead, clk operations flow through the + * framework and the framework manipulates this structure to keep track of + * parent/child relationships, rate, enable state, etc. + * + */ struct clk_core { const char *name; const struct clk_ops *ops; @@ -882,6 +933,24 @@ int clk_hw_determine_rate_no_reparent(struct clk_hw *hw, } EXPORT_SYMBOL_GPL(clk_hw_determine_rate_no_reparent); +/** + * clk_determine_rate_noop - clk_ops::determine_rate noop implementation + * @hw: clk to determine rate on + * @req: rate request + * + * Noop determine rate for clocks where the rate rounding is handled by the + * firmware/hardware, or the clock is capable of any rate. The requested rate is + * passed through unchanged, and the actual rate will be learned via + * recalc_rate() after the rate is set. + * + * Returns: 0 always + */ +int clk_determine_rate_noop(struct clk_hw *hw, struct clk_rate_request *req) +{ + return 0; +} +EXPORT_SYMBOL_GPL(clk_determine_rate_noop); + /*** clk api ***/ static void clk_core_rate_unprotect(struct clk_core *core) @@ -1846,10 +1915,10 @@ static int __clk_notify(struct clk_core *core, unsigned long msg, } /** - * __clk_recalc_accuracies + * __clk_recalc_accuracies - recalculate all accuracies in the clk subtree * @core: first clk in the subtree * - * Walks the subtree of clks starting with clk and recalculates accuracies as + * Walks the subtree of clks starting with @core and recalculates accuracies as * it goes. Note that if a clk does not implement the .recalc_accuracy * callback then it is assumed that the clock will take on the accuracy of its * parent. @@ -1919,16 +1988,16 @@ static unsigned long clk_recalc(struct clk_core *core, } /** - * __clk_recalc_rates + * __clk_recalc_rates - recalculate all rates in the clk subtree * @core: first clk in the subtree * @update_req: Whether req_rate should be updated with the new rate * @msg: notification type (see include/linux/clk.h) * - * Walks the subtree of clks starting with clk and recalculates rates as it + * Walks the subtree of clks starting with @core and recalculates rates as it * goes. Note that if a clk does not implement the .recalc_rate callback then * it is assumed that the clock will take on the rate of its parent. * - * clk_recalc_rates also propagates the POST_RATE_CHANGE notification, + * __clk_recalc_rates also propagates the POST_RATE_CHANGE notification, * if necessary. */ static void __clk_recalc_rates(struct clk_core *core, bool update_req, @@ -2191,14 +2260,14 @@ static int __clk_set_parent(struct clk_core *core, struct clk_core *parent, } /** - * __clk_speculate_rates + * __clk_speculate_rates - speculate all rates in the clk subtree * @core: first clk in the subtree * @parent_rate: the "future" rate of clk's parent * - * Walks the subtree of clks starting with clk, speculating rates as it + * Walks the subtree of clks starting with @core, speculating rates as it * goes and firing off PRE_RATE_CHANGE notifications as necessary. * - * Unlike clk_recalc_rates, clk_speculate_rates exists only for sending + * Unlike __clk_recalc_rates, __clk_speculate_rates exists only for sending * pre-rate change notifications and returns early if no clks in the * subtree have subscribed to the notifications. Note that if a clk does not * implement the .recalc_rate callback then it is assumed that the clock will @@ -2774,6 +2843,33 @@ int clk_set_max_rate(struct clk *clk, unsigned long rate) } EXPORT_SYMBOL_GPL(clk_set_max_rate); +int clk_hw_set_spread_spectrum(struct clk_hw *hw, const struct clk_spread_spectrum *ss_conf) +{ + struct clk_core *core; + int ret; + + if (!hw) + return 0; + + core = hw->core; + + clk_prepare_lock(); + + ret = clk_pm_runtime_get(core); + if (ret) + goto fail; + + if (core->ops->set_spread_spectrum) + ret = core->ops->set_spread_spectrum(hw, ss_conf); + + clk_pm_runtime_put(core); + +fail: + clk_prepare_unlock(); + return ret; +} +EXPORT_SYMBOL_GPL(clk_hw_set_spread_spectrum); + /** * clk_get_parent - return the parent of a clk * @clk: the clk whose parent gets returned diff --git a/drivers/clk/clk_test.c b/drivers/clk/clk_test.c index b1961daac5e2..1c5c8b7c1f3c 100644 --- a/drivers/clk/clk_test.c +++ b/drivers/clk/clk_test.c @@ -7,6 +7,7 @@ #include #include #include +#include /* Needed for clk_hw_get_clk() */ #include "clk.h" @@ -21,13 +22,14 @@ static const struct clk_ops empty_clk_ops = { }; -#define DUMMY_CLOCK_INIT_RATE (42 * 1000 * 1000) -#define DUMMY_CLOCK_RATE_1 (142 * 1000 * 1000) -#define DUMMY_CLOCK_RATE_2 (242 * 1000 * 1000) +#define DUMMY_CLOCK_INIT_RATE (42 * HZ_PER_MHZ) +#define DUMMY_CLOCK_RATE_1 (142 * HZ_PER_MHZ) +#define DUMMY_CLOCK_RATE_2 (242 * HZ_PER_MHZ) struct clk_dummy_context { struct clk_hw hw; unsigned long rate; + struct clk_spread_spectrum sscs; }; static unsigned long clk_dummy_recalc_rate(struct clk_hw *hw, @@ -83,6 +85,17 @@ static int clk_dummy_set_rate(struct clk_hw *hw, return 0; } +static int clk_dummy_set_spread_spectrum(struct clk_hw *hw, + const struct clk_spread_spectrum *ss_conf) +{ + struct clk_dummy_context *ctx = + container_of(hw, struct clk_dummy_context, hw); + + ctx->sscs = *ss_conf; + + return 0; +} + static int clk_dummy_single_set_parent(struct clk_hw *hw, u8 index) { if (index >= clk_hw_get_num_parents(hw)) @@ -100,18 +113,21 @@ static const struct clk_ops clk_dummy_rate_ops = { .recalc_rate = clk_dummy_recalc_rate, .determine_rate = clk_dummy_determine_rate, .set_rate = clk_dummy_set_rate, + .set_spread_spectrum = clk_dummy_set_spread_spectrum, }; static const struct clk_ops clk_dummy_maximize_rate_ops = { .recalc_rate = clk_dummy_recalc_rate, .determine_rate = clk_dummy_maximize_rate, .set_rate = clk_dummy_set_rate, + .set_spread_spectrum = clk_dummy_set_spread_spectrum, }; static const struct clk_ops clk_dummy_minimize_rate_ops = { .recalc_rate = clk_dummy_recalc_rate, .determine_rate = clk_dummy_minimize_rate, .set_rate = clk_dummy_set_rate, + .set_spread_spectrum = clk_dummy_set_spread_spectrum, }; static const struct clk_ops clk_dummy_single_parent_ops = { @@ -2232,7 +2248,7 @@ static void clk_leaf_mux_set_rate_parent_determine_rate_test_case_to_desc( const struct clk_leaf_mux_set_rate_parent_determine_rate_test_case *t, char *desc) { - strcpy(desc, t->desc); + strscpy(desc, t->desc, KUNIT_PARAM_DESC_SIZE); } static const struct clk_leaf_mux_set_rate_parent_determine_rate_test_case @@ -2644,7 +2660,7 @@ static void clk_register_clk_parent_data_test_case_to_desc( const struct clk_register_clk_parent_data_test_case *t, char *desc) { - strcpy(desc, t->desc); + strscpy(desc, t->desc, KUNIT_PARAM_DESC_SIZE); } static const struct clk_register_clk_parent_data_test_case @@ -3097,6 +3113,7 @@ struct clk_assigned_rates_context { * @overlay_end: Pointer to end of DT overlay to apply for test * @rate0: Initial rate of first clk * @rate1: Initial rate of second clk + * @sscs: Initial spread spectrum settings * @consumer_test: true if a consumer is being tested */ struct clk_assigned_rates_test_param { @@ -3105,6 +3122,7 @@ struct clk_assigned_rates_test_param { u8 *overlay_end; unsigned long rate0; unsigned long rate1; + struct clk_spread_spectrum sscs; bool consumer_test; }; @@ -3116,7 +3134,7 @@ static void clk_assigned_rates_register_clk(struct kunit *test, struct clk_dummy_context *ctx, struct device_node *np, const char *name, - unsigned long rate) + unsigned long rate, const struct clk_spread_spectrum *sscs) { struct clk_init_data init = { }; @@ -3124,6 +3142,7 @@ clk_assigned_rates_register_clk(struct kunit *test, init.ops = &clk_dummy_rate_ops; ctx->hw.init = &init; ctx->rate = rate; + ctx->sscs = *sscs; KUNIT_ASSERT_EQ(test, 0, of_clk_hw_register_kunit(test, np, &ctx->hw)); KUNIT_ASSERT_EQ(test, ctx->rate, rate); @@ -3167,14 +3186,16 @@ static int clk_assigned_rates_test_init(struct kunit *test) KUNIT_ASSERT_LT(test, clk_cells, 2); clk_assigned_rates_register_clk(test, &ctx->clk0, np, - "test_assigned_rate0", test_param->rate0); + "test_assigned_rate0", test_param->rate0, + &test_param->sscs); if (clk_cells == 0) { KUNIT_ASSERT_EQ(test, 0, of_clk_add_hw_provider_kunit(test, np, of_clk_hw_simple_get, &ctx->clk0.hw)); } else if (clk_cells == 1) { clk_assigned_rates_register_clk(test, &ctx->clk1, np, - "test_assigned_rate1", test_param->rate1); + "test_assigned_rate1", test_param->rate1, + &test_param->sscs); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, data = kunit_kzalloc(test, struct_size(data, hws, 2), GFP_KERNEL)); @@ -3403,6 +3424,182 @@ static struct kunit_suite clk_assigned_rates_suite = { .init = clk_assigned_rates_test_init, }; +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_one); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_one_consumer); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_multiple); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_multiple_consumer); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_without); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_without_consumer); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_zero); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_zero_consumer); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_null); +OF_OVERLAY_DECLARE(kunit_clk_assigned_sscs_null_consumer); + +static void clk_assigned_sscs_assigns_one(struct kunit *test) +{ + struct clk_assigned_rates_context *ctx = test->priv; + + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.modfreq_hz, ASSIGNED_SSCS_0_MODFREQ); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.spread_bp, ASSIGNED_SSCS_0_SPREAD); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.method, ASSIGNED_SSCS_0_METHOD); +} + +/* Test cases that assign sscs for one clk */ +static const struct clk_assigned_rates_test_param clk_assigned_sscs_assigns_one_test_params[] = { + { + /* + * Test that a single cell assigned-clock-sscs property + * assigns the sscs when the property is in the provider. + */ + .desc = "provider assigns", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_one), + }, + { + /* + * Test that a single cell assigned-clock-sscs property + * assigns the sscs when the property is in the consumer. + */ + .desc = "consumer assigns", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_one_consumer), + .consumer_test = true, + }, +}; +KUNIT_ARRAY_PARAM_DESC(clk_assigned_sscs_assigns_one, + clk_assigned_sscs_assigns_one_test_params, desc) + +static void clk_assigned_sscs_assigns_multiple(struct kunit *test) +{ + struct clk_assigned_rates_context *ctx = test->priv; + + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.modfreq_hz, ASSIGNED_SSCS_0_MODFREQ); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.spread_bp, ASSIGNED_SSCS_0_SPREAD); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.method, ASSIGNED_SSCS_0_METHOD); + KUNIT_EXPECT_EQ(test, ctx->clk1.sscs.modfreq_hz, ASSIGNED_SSCS_1_MODFREQ); + KUNIT_EXPECT_EQ(test, ctx->clk1.sscs.spread_bp, ASSIGNED_SSCS_1_SPREAD); + KUNIT_EXPECT_EQ(test, ctx->clk1.sscs.method, ASSIGNED_SSCS_1_METHOD); +} + +/* Test cases that assign sscs for multiple clks */ +static const +struct clk_assigned_rates_test_param clk_assigned_sscs_assigns_multiple_test_params[] = { + { + /* + * Test that a multiple cell assigned-clock-sscs property + * assigns the sscs when the property is in the provider. + */ + .desc = "provider assigns", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_multiple), + }, + { + /* + * Test that a multiple cell assigned-clock-sscs property + * assigns the sscs when the property is in the consumer. + */ + .desc = "consumer assigns", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_multiple_consumer), + .consumer_test = true, + }, +}; +KUNIT_ARRAY_PARAM_DESC(clk_assigned_sscs_assigns_multiple, + clk_assigned_sscs_assigns_multiple_test_params, + desc) + +static void clk_assigned_sscs_skips(struct kunit *test) +{ + struct clk_assigned_rates_context *ctx = test->priv; + const struct clk_assigned_rates_test_param *test_param = test->param_value; + + KUNIT_EXPECT_NE(test, ctx->clk0.sscs.modfreq_hz, ASSIGNED_SSCS_0_MODFREQ); + KUNIT_EXPECT_NE(test, ctx->clk0.sscs.spread_bp, ASSIGNED_SSCS_0_SPREAD); + KUNIT_EXPECT_NE(test, ctx->clk0.sscs.method, ASSIGNED_SSCS_0_METHOD); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.modfreq_hz, test_param->sscs.modfreq_hz); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.spread_bp, test_param->sscs.spread_bp); + KUNIT_EXPECT_EQ(test, ctx->clk0.sscs.method, test_param->sscs.method); +} + +/* Test cases that skip changing the sscs due to malformed DT */ +static const struct clk_assigned_rates_test_param clk_assigned_sscs_skips_test_params[] = { + { + /* + * Test that an assigned-clock-sscs property without an assigned-clocks + * property fails when the property is in the provider. + */ + .desc = "provider missing assigned-clocks", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_without), + .sscs = {50000, 60000, 3}, + }, + { + /* + * Test that an assigned-clock-sscs property without an assigned-clocks + * property fails when the property is in the consumer. + */ + .desc = "consumer missing assigned-clocks", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_without_consumer), + .sscs = {50000, 60000, 3}, + .consumer_test = true, + }, + { + /* + * Test that an assigned-clock-sscs property of zero doesn't + * set sscs when the property is in the provider. + */ + .desc = "provider assigned-clock-sscs of zero", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_zero), + .sscs = {50000, 60000, 3}, + }, + { + /* + * Test that an assigned-clock-sscs property of zero doesn't + * set sscs when the property is in the consumer. + */ + .desc = "consumer assigned-clock-sscs of zero", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_zero_consumer), + .sscs = {50000, 60000, 3}, + .consumer_test = true, + }, + { + /* + * Test that an assigned-clocks property with a null phandle + * doesn't set sscs when the property is in the provider. + */ + .desc = "provider assigned-clocks null phandle", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_null), + .sscs = {50000, 60000, 3}, + }, + { + /* + * Test that an assigned-clocks property with a null phandle + * doesn't set sscs when the property is in the consumer. + */ + .desc = "consumer assigned-clocks null phandle", + TEST_PARAM_OVERLAY(kunit_clk_assigned_sscs_null_consumer), + .sscs = {50000, 60000, 3}, + .consumer_test = true, + }, +}; +KUNIT_ARRAY_PARAM_DESC(clk_assigned_sscs_skips, + clk_assigned_sscs_skips_test_params, + desc) + +static struct kunit_case clk_assigned_sscs_test_cases[] = { + KUNIT_CASE_PARAM(clk_assigned_sscs_assigns_one, + clk_assigned_sscs_assigns_one_gen_params), + KUNIT_CASE_PARAM(clk_assigned_sscs_assigns_multiple, + clk_assigned_sscs_assigns_multiple_gen_params), + KUNIT_CASE_PARAM(clk_assigned_sscs_skips, + clk_assigned_sscs_skips_gen_params), + {} +}; + +/* + * Test suite for assigned-clock-sscs DT property. + */ +static struct kunit_suite clk_assigned_sscs_suite = { + .name = "clk_assigned_sscs", + .test_cases = clk_assigned_sscs_test_cases, + .init = clk_assigned_rates_test_init, +}; + static const struct clk_init_data clk_hw_get_dev_of_node_init_data = { .name = "clk_hw_get_dev_of_node", .ops = &empty_clk_ops, @@ -3544,6 +3741,7 @@ static struct kunit_suite clk_hw_get_dev_of_node_test_suite = { kunit_test_suites( &clk_assigned_rates_suite, + &clk_assigned_sscs_suite, &clk_hw_get_dev_of_node_test_suite, &clk_leaf_mux_set_rate_parent_test_suite, &clk_test_suite, diff --git a/drivers/clk/eswin/Kconfig b/drivers/clk/eswin/Kconfig index 0406ec499ec9..37857bd77335 100644 --- a/drivers/clk/eswin/Kconfig +++ b/drivers/clk/eswin/Kconfig @@ -13,3 +13,16 @@ config COMMON_CLK_EIC7700 SoC. The clock controller generates and supplies clocks to various peripherals within the SoC. Say yes here to support the clock controller on the EIC7700 SoC. + +config COMMON_CLK_EIC7700_HSP + tristate "EIC7700 HSP Clock Driver" + depends on ARCH_ESWIN || COMPILE_TEST + select AUXILIARY_BUS + select COMMON_CLK_EIC7700 + select RESET_EIC7700_HSP if RESET_CONTROLLER + select REGMAP_MMIO + help + This driver provides support for clock controller on ESWIN EIC7700 + HSP. The clock controller generates and supplies clocks to high + speed peripherals within the SoC. + Say yes here to support the clock controller on the EIC7700 HSP. diff --git a/drivers/clk/eswin/Makefile b/drivers/clk/eswin/Makefile index 4a7c2af82164..21a09a3396df 100644 --- a/drivers/clk/eswin/Makefile +++ b/drivers/clk/eswin/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_COMMON_CLK_ESWIN) += clk.o obj-$(CONFIG_COMMON_CLK_EIC7700) += clk-eic7700.o +obj-$(CONFIG_COMMON_CLK_EIC7700_HSP) += clk-eic7700-hsp.o diff --git a/drivers/clk/eswin/clk-eic7700-hsp.c b/drivers/clk/eswin/clk-eic7700-hsp.c new file mode 100644 index 000000000000..6ddb8fbd605e --- /dev/null +++ b/drivers/clk/eswin/clk-eic7700-hsp.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * ESWIN EIC7700 HSP Clock Driver + * + * Authors: Xuyang Dong + */ + +#include +#include +#include +#include + +#include + +#include "common.h" + +#define EIC7700_HSP_SATA_REG 0x300 +#define EIC7700_HSP_MSHC0_REG 0x510 +#define EIC7700_HSP_MSHC1_REG 0x610 +#define EIC7700_HSP_MSHC2_REG 0x710 +#define EIC7700_HSP_USB0_REG 0x800 +#define EIC7700_HSP_USB0_REF_REG 0x83c +#define EIC7700_HSP_USB1_REG 0x900 +#define EIC7700_HSP_USB1_REF_REG 0x93c + +#define USB_REF_XTAL24M 0x2a +#define EIC7700_HSP_NR_CLKS (EIC7700_HSP_CLK_GATE_SATA + 1) + +struct eic7700_hsp_clk_gate { + struct clk_hw hw; + unsigned int id; + struct regmap *regmap; + unsigned int reg; + unsigned int ref_reg; + const char *name; + const struct clk_parent_data *parent_data; + unsigned long flags; + unsigned int offset; + unsigned int ref_offset; + u8 bit_idx; +}; + +static const struct regmap_config eic7700_hsp_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .max_register = 0x1ffc, + .reg_stride = 4, + .fast_io = true, + .use_raw_spinlock = true, +}; + +static inline struct eic7700_hsp_clk_gate *to_gate_clk(struct clk_hw *hw) +{ + return container_of(hw, struct eic7700_hsp_clk_gate, hw); +} + +#define EIC7700_HSP_GATE(_id, _name, _pdata, _flags, _offset, _idx, \ + _ref_offset) \ + { \ + .id = _id, \ + .name = _name, \ + .parent_data = _pdata, \ + .flags = _flags, \ + .offset = _offset, \ + .ref_offset = _ref_offset, \ + .bit_idx = _idx, \ + } + +static void hsp_clk_gate_endisable(struct clk_hw *hw, bool enable) +{ + struct eic7700_hsp_clk_gate *gate = to_gate_clk(hw); + + if (enable) { + /* + * Hardware bug: The USB reference clock must be 24MHz. + * The default register value after reset is invalid. + * Workaround: Rewrite the correct value before enabling + * the USB gate clock. + */ + regmap_update_bits(gate->regmap, gate->ref_reg, 0x3f, + USB_REF_XTAL24M); + } + regmap_assign_bits(gate->regmap, gate->reg, BIT(gate->bit_idx), enable); +} + +static int hsp_clk_gate_enable(struct clk_hw *hw) +{ + hsp_clk_gate_endisable(hw, true); + + return 0; +} + +static void hsp_clk_gate_disable(struct clk_hw *hw) +{ + hsp_clk_gate_endisable(hw, false); +} + +static int hsp_clk_gate_is_enabled(struct clk_hw *hw) +{ + struct eic7700_hsp_clk_gate *gate = to_gate_clk(hw); + unsigned int val; + int ret; + + ret = regmap_read(gate->regmap, gate->reg, &val); + if (ret != 0) + return ret; + + return !!(val & BIT(gate->bit_idx)); +} + +static const struct clk_ops hsp_clk_gate_ops = { + .enable = hsp_clk_gate_enable, + .disable = hsp_clk_gate_disable, + .is_enabled = hsp_clk_gate_is_enabled, +}; + +static struct clk_hw * +hsp_clk_register_gate(struct device *dev, unsigned int id, const char *name, + const struct clk_parent_data *parent_data, + unsigned long flags, struct regmap *regmap, + unsigned int reg, unsigned int ref_reg, u8 bit_idx) +{ + struct eic7700_hsp_clk_gate *gate; + struct clk_init_data init = {}; + struct clk_hw *hw; + int ret; + + gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &hsp_clk_gate_ops; + init.flags = flags; + init.parent_data = parent_data; + init.num_parents = 1; + + gate->id = id; + gate->regmap = regmap; + gate->reg = reg; + gate->ref_reg = ref_reg; + gate->bit_idx = bit_idx; + gate->hw.init = &init; + + hw = &gate->hw; + ret = devm_clk_hw_register(dev, hw); + if (ret) + hw = ERR_PTR(ret); + + return hw; +} + +static const struct clk_parent_data hsp_cfg[] = { + { .index = 0 } +}; + +static const struct clk_parent_data hsp_mmc[] = { + { .index = 1 } +}; + +static const struct clk_parent_data hsp_usb_sata[] = { + { .index = 2 } +}; + +static struct eswin_fixed_factor_clock eic7700_hsp_factor_clks[] = { + ESWIN_FACTOR(EIC7700_HSP_CLK_FAC_CFG_DIV2, "factor_hsp_cfg_div2", + hsp_cfg, 1, 2, 0), + ESWIN_FACTOR(EIC7700_HSP_CLK_FAC_CFG_DIV4, "factor_hsp_cfg_div4", + hsp_cfg, 1, 4, 0), + ESWIN_FACTOR(EIC7700_HSP_CLK_FAC_MMC_DIV10, "factor_hsp_mmc_div10", + hsp_mmc, 1, 10, 0), +}; + +static struct eswin_gate_clock eic7700_hsp_gate_clks[] = { + ESWIN_GATE(EIC7700_HSP_CLK_GATE_SATA, "gate_clk_hsp_sata", hsp_usb_sata, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_SATA_REG, 28, 0), + ESWIN_GATE(EIC7700_HSP_CLK_GATE_MSHC0_TMR, "gate_clk_hsp_mshc0_tmr", + hsp_mmc, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_MSHC0_REG, 8, 0), + ESWIN_GATE(EIC7700_HSP_CLK_GATE_MSHC1_TMR, "gate_clk_hsp_mshc1_tmr", + hsp_mmc, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_MSHC1_REG, 8, 0), + ESWIN_GATE(EIC7700_HSP_CLK_GATE_MSHC2_TMR, "gate_clk_hsp_mshc2_tmr", + hsp_mmc, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_MSHC2_REG, 8, 0), +}; + +static struct eic7700_hsp_clk_gate eic7700_hsp_spec_gate_clks[] = { + EIC7700_HSP_GATE(EIC7700_HSP_CLK_GATE_USB0, "gate_clk_hsp_usb0", + hsp_usb_sata, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_USB0_REG, 28, EIC7700_HSP_USB0_REF_REG), + EIC7700_HSP_GATE(EIC7700_HSP_CLK_GATE_USB1, "gate_clk_hsp_usb1", + hsp_usb_sata, CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_USB1_REG, 28, EIC7700_HSP_USB1_REF_REG), +}; + +static const struct clk_parent_data mux_mmc_3mux1_p[] = { + { .fw_name = "cfg" }, + { .hw = &eic7700_hsp_factor_clks[0].hw }, + { .hw = &eic7700_hsp_factor_clks[1].hw }, +}; + +static const struct clk_parent_data mux_mmc_2mux1_p[] = { + { .fw_name = "mmc" }, + { .hw = &eic7700_hsp_factor_clks[2].hw }, +}; + +static u32 mux_mmc_3mux1_tbl[] = { 0x0, 0x1, 0x3 }; + +static struct eswin_mux_clock eic7700_hsp_mux_clks[] = { + ESWIN_MUX_TBL(EIC7700_HSP_CLK_MUX_EMMC_3MUX1, "mux_hsp_emmc_3mux1", + mux_mmc_3mux1_p, ARRAY_SIZE(mux_mmc_3mux1_p), + CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC0_REG, 16, 2, 0, + mux_mmc_3mux1_tbl), + ESWIN_MUX_TBL(EIC7700_HSP_CLK_MUX_SD0_3MUX1, "mux_hsp_sd0_3mux1", + mux_mmc_3mux1_p, ARRAY_SIZE(mux_mmc_3mux1_p), + CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC1_REG, 16, 2, 0, + mux_mmc_3mux1_tbl), + ESWIN_MUX_TBL(EIC7700_HSP_CLK_MUX_SD1_3MUX1, "mux_hsp_sd1_3mux1", + mux_mmc_3mux1_p, ARRAY_SIZE(mux_mmc_3mux1_p), + CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC2_REG, 16, 2, 0, + mux_mmc_3mux1_tbl), + ESWIN_MUX(EIC7700_HSP_CLK_MUX_EMMC_CQE_2MUX1, "mux_hsp_emmc_cqe_2mux1", + mux_mmc_2mux1_p, ARRAY_SIZE(mux_mmc_2mux1_p), + CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC0_REG, 0, 1, 0), + ESWIN_MUX(EIC7700_HSP_CLK_MUX_SD0_CQE_2MUX1, "mux_hsp_sd0_cqe_2mux1", + mux_mmc_2mux1_p, ARRAY_SIZE(mux_mmc_2mux1_p), + CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC1_REG, 0, 1, 0), + ESWIN_MUX(EIC7700_HSP_CLK_MUX_SD1_CQE_2MUX1, "mux_hsp_sd1_cqe_2mux1", + mux_mmc_2mux1_p, ARRAY_SIZE(mux_mmc_2mux1_p), + CLK_SET_RATE_PARENT, EIC7700_HSP_MSHC2_REG, 0, 1, 0), +}; + +static struct eswin_clk_info eic7700_hsp_clks[] = { + ESWIN_GATE_TYPE(EIC7700_HSP_CLK_GATE_EMMC, "gate_clk_hsp_emmc", + EIC7700_HSP_CLK_MUX_EMMC_3MUX1, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_MSHC0_REG, 24, 0), + ESWIN_GATE_TYPE(EIC7700_HSP_CLK_GATE_SD0, "gate_clk_hsp_sd0", + EIC7700_HSP_CLK_MUX_SD0_3MUX1, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_MSHC1_REG, 24, 0), + ESWIN_GATE_TYPE(EIC7700_HSP_CLK_GATE_SD1, "gate_clk_hsp_sd1", + EIC7700_HSP_CLK_MUX_SD1_3MUX1, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + EIC7700_HSP_MSHC2_REG, 24, 0), +}; + +static int eic7700_hsp_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct auxiliary_device *adev; + struct eswin_clock_data *data; + struct regmap *regmap; + struct clk_hw *hw; + int i, ret; + + data = eswin_clk_init(pdev, EIC7700_HSP_NR_CLKS); + if (IS_ERR(data)) + return dev_err_probe(dev, PTR_ERR(data), + "failed to get clk data!\n"); + + regmap = devm_regmap_init_mmio(dev, data->base, + &eic7700_hsp_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "failed to get regmap!\n"); + + ret = eswin_clk_register_fixed_factor(dev, eic7700_hsp_factor_clks, + ARRAY_SIZE(eic7700_hsp_factor_clks), + data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register fixed factor clock\n"); + + ret = eswin_clk_register_gate(dev, eic7700_hsp_gate_clks, + ARRAY_SIZE(eic7700_hsp_gate_clks), data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register gate clock\n"); + + ret = eswin_clk_register_mux(dev, eic7700_hsp_mux_clks, + ARRAY_SIZE(eic7700_hsp_mux_clks), + data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register mux clock\n"); + + ret = eswin_clk_register_clks(dev, eic7700_hsp_clks, + ARRAY_SIZE(eic7700_hsp_clks), data); + if (ret) + return dev_err_probe(dev, ret, + "failed to register clock\n"); + + for (i = 0; i < ARRAY_SIZE(eic7700_hsp_spec_gate_clks); i++) { + struct eic7700_hsp_clk_gate *gate; + + gate = &eic7700_hsp_spec_gate_clks[i]; + hw = hsp_clk_register_gate(dev, gate->id, gate->name, + gate->parent_data, gate->flags, + regmap, gate->offset, + gate->ref_offset, gate->bit_idx); + if (IS_ERR(hw)) + return dev_err_probe(dev, PTR_ERR(hw), + "failed to register gate clock\n"); + + data->clk_data.hws[gate->id] = hw; + } + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &data->clk_data); + if (ret) + return dev_err_probe(dev, ret, "add clk provider failed\n"); + + adev = devm_auxiliary_device_create(dev, "hsp-reset", NULL); + if (!adev) + return dev_err_probe(dev, -ENODEV, + "register hsp-reset device failed\n"); + + return 0; +} + +static const struct of_device_id eic7700_hsp_clock_dt_ids[] = { + { .compatible = "eswin,eic7700-hspcrg", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, eic7700_hsp_clock_dt_ids); + +static struct platform_driver eic7700_hsp_clock_driver = { + .probe = eic7700_hsp_clk_probe, + .driver = { + .name = "eic7700-hsp-clock", + .of_match_table = eic7700_hsp_clock_dt_ids, + }, +}; + +module_platform_driver(eic7700_hsp_clock_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Xuyang Dong "); +MODULE_DESCRIPTION("ESWIN EIC7700 HSP clock controller driver"); diff --git a/drivers/clk/eswin/clk-eic7700.c b/drivers/clk/eswin/clk-eic7700.c index be81d74192da..43a47fe16ab1 100644 --- a/drivers/clk/eswin/clk-eic7700.c +++ b/drivers/clk/eswin/clk-eic7700.c @@ -791,7 +791,8 @@ static struct eswin_clk_info eic7700_clks[] = { EIC7700_CLK_MUX_CPU_ROOT_3MUX1_GFREE, CLK_SET_RATE_PARENT, EIC7700_REG_OFFSET_U84, 27, 0), ESWIN_GATE_TYPE(EIC7700_CLK_GATE_NOC_NSP_CLK, "gate_noc_nsp_clk", - EIC7700_CLK_DIV_NOC_NSP_DYNM, CLK_SET_RATE_PARENT, + EIC7700_CLK_DIV_NOC_NSP_DYNM, + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, EIC7700_REG_OFFSET_NOC, 31, 0), ESWIN_GATE_TYPE(EIC7700_CLK_GATE_BOOTSPI, "gate_clk_bootspi", EIC7700_CLK_MUX_BOOTSPI_CLK_2MUX1_GFREE, diff --git a/drivers/clk/eswin/clk.c b/drivers/clk/eswin/clk.c index e09a52cc3587..79d1e4c5e637 100644 --- a/drivers/clk/eswin/clk.c +++ b/drivers/clk/eswin/clk.c @@ -204,7 +204,7 @@ int eswin_clk_register_pll(struct device *dev, struct eswin_pll_clock *clks, int nums, struct eswin_clock_data *data) { struct eswin_clk_pll *p_clk = NULL; - struct clk_init_data init; + struct clk_init_data init = {}; struct clk_hw *clk_hw; int i, ret; @@ -419,7 +419,7 @@ struct clk_hw *eswin_register_clkdiv(struct device *dev, unsigned int id, unsigned long priv_flag, spinlock_t *lock) { struct eswin_divider_clock *dclk; - struct clk_init_data init; + struct clk_init_data init = {}; struct clk_hw *clk_hw; int ret; diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig index c1ec75aa4ccd..e70bcfa9105b 100644 --- a/drivers/clk/hisilicon/Kconfig +++ b/drivers/clk/hisilicon/Kconfig @@ -1,8 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-only +config COMMON_CLK_HISI + bool + default ARCH_HISI + config COMMON_CLK_HI3516CV300 tristate "HI3516CV300 Clock Driver" depends on ARCH_HISI || COMPILE_TEST select RESET_HISI + select COMMON_CLK_HISI default ARCH_HISI help Build the clock driver for hi3516cv300. @@ -11,6 +16,7 @@ config COMMON_CLK_HI3519 tristate "Hi3519 Clock Driver" depends on ARCH_HISI || COMPILE_TEST select RESET_HISI + select COMMON_CLK_HISI default ARCH_HISI help Build the clock driver for hi3519. @@ -19,6 +25,7 @@ config COMMON_CLK_HI3559A bool "Hi3559A Clock Driver" depends on ARCH_HISI || COMPILE_TEST select RESET_HISI + select COMMON_CLK_HISI default ARCH_HISI help Build the clock driver for hi3559a. @@ -26,6 +33,7 @@ config COMMON_CLK_HI3559A config COMMON_CLK_HI3660 bool "Hi3660 Clock Driver" depends on ARCH_HISI || COMPILE_TEST + select COMMON_CLK_HISI default ARCH_HISI help Build the clock driver for hi3660. @@ -33,6 +41,7 @@ config COMMON_CLK_HI3660 config COMMON_CLK_HI3670 bool "Hi3670 Clock Driver" depends on ARCH_HISI || COMPILE_TEST + select COMMON_CLK_HISI default ARCH_HISI help Build the clock driver for hi3670. @@ -41,6 +50,7 @@ config COMMON_CLK_HI3798CV200 tristate "Hi3798CV200 Clock Driver" depends on ARCH_HISI || COMPILE_TEST select RESET_HISI + select COMMON_CLK_HISI default ARCH_HISI help Build the clock driver for hi3798cv200. @@ -48,6 +58,7 @@ config COMMON_CLK_HI3798CV200 config COMMON_CLK_HI6220 bool "Hi6220 Clock Driver" depends on ARCH_HISI || COMPILE_TEST + select COMMON_CLK_HISI default ARCH_HISI help Build the Hisilicon Hi6220 clock driver based on the common clock framework. @@ -55,6 +66,7 @@ config COMMON_CLK_HI6220 config RESET_HISI bool "HiSilicon Reset Controller Driver" depends on ARCH_HISI || COMPILE_TEST + select COMMON_CLK_HISI select RESET_CONTROLLER help Build reset controller driver for HiSilicon device chipsets. @@ -63,6 +75,7 @@ config STUB_CLK_HI6220 bool "Hi6220 Stub Clock Driver" if EXPERT depends on (COMMON_CLK_HI6220 || COMPILE_TEST) depends on MAILBOX + select COMMON_CLK_HISI default COMMON_CLK_HI6220 help Build the Hisilicon Hi6220 stub clock driver. @@ -71,6 +84,7 @@ config STUB_CLK_HI3660 bool "Hi3660 Stub Clock Driver" if EXPERT depends on (COMMON_CLK_HI3660 || COMPILE_TEST) depends on MAILBOX + select COMMON_CLK_HISI default COMMON_CLK_HI3660 help Build the Hisilicon Hi3660 stub clock driver. diff --git a/drivers/clk/hisilicon/clk-hi3660-stub.c b/drivers/clk/hisilicon/clk-hi3660-stub.c index 7c8b00ee6019..d28118f4cfb8 100644 --- a/drivers/clk/hisilicon/clk-hi3660-stub.c +++ b/drivers/clk/hisilicon/clk-hi3660-stub.c @@ -67,16 +67,6 @@ static unsigned long hi3660_stub_clk_recalc_rate(struct clk_hw *hw, return stub_clk->rate; } -static int hi3660_stub_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * LPM3 handles rate rounding so just return whatever - * rate is requested. - */ - return 0; -} - static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -97,7 +87,7 @@ static int hi3660_stub_clk_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops hi3660_stub_clk_ops = { .recalc_rate = hi3660_stub_clk_recalc_rate, - .determine_rate = hi3660_stub_clk_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = hi3660_stub_clk_set_rate, }; @@ -124,7 +114,6 @@ static struct clk_hw *hi3660_stub_clk_hw_get(struct of_phandle_args *clkspec, static int hi3660_stub_clk_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; unsigned int i; int ret; @@ -139,12 +128,9 @@ static int hi3660_stub_clk_probe(struct platform_device *pdev) if (IS_ERR(stub_clk_chan.mbox)) return PTR_ERR(stub_clk_chan.mbox); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - freq_reg = devm_ioremap(dev, res->start, resource_size(res)); - if (!freq_reg) - return -ENOMEM; + freq_reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(freq_reg)) + return PTR_ERR(freq_reg); freq_reg += HI3660_STUB_CLOCK_DATA; diff --git a/drivers/clk/hisilicon/reset.c b/drivers/clk/hisilicon/reset.c index 93cee17db8b1..c3b7daac9313 100644 --- a/drivers/clk/hisilicon/reset.c +++ b/drivers/clk/hisilicon/reset.c @@ -91,7 +91,7 @@ struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev) { struct hisi_reset_controller *rstc; - rstc = devm_kmalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL); + rstc = devm_kzalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL); if (!rstc) return NULL; diff --git a/drivers/clk/imx/clk-scu.c b/drivers/clk/imx/clk-scu.c index 9b33df9967ec..44220e9c5c2a 100644 --- a/drivers/clk/imx/clk-scu.c +++ b/drivers/clk/imx/clk-scu.c @@ -262,23 +262,6 @@ static unsigned long clk_scu_recalc_rate(struct clk_hw *hw, return le32_to_cpu(msg.data.resp.rate); } -/* - * clk_scu_determine_rate - Returns the closest rate for a SCU clock - * @hw: clock to round rate for - * @req: clock rate request - * - * Returns 0 on success, a negative error on failure - */ -static int clk_scu_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * Assume we support all the requested rate and let the SCU firmware - * to handle the left work - */ - return 0; -} - static int clk_scu_atf_set_cpu_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -436,7 +419,7 @@ static void clk_scu_unprepare(struct clk_hw *hw) static const struct clk_ops clk_scu_ops = { .recalc_rate = clk_scu_recalc_rate, - .determine_rate = clk_scu_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = clk_scu_set_rate, .get_parent = clk_scu_get_parent, .set_parent = clk_scu_set_parent, @@ -446,7 +429,7 @@ static const struct clk_ops clk_scu_ops = { static const struct clk_ops clk_scu_cpu_ops = { .recalc_rate = clk_scu_recalc_rate, - .determine_rate = clk_scu_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = clk_scu_atf_set_cpu_rate, .prepare = clk_scu_prepare, .unprepare = clk_scu_unprepare, @@ -454,7 +437,7 @@ static const struct clk_ops clk_scu_cpu_ops = { static const struct clk_ops clk_scu_pi_ops = { .recalc_rate = clk_scu_recalc_rate, - .determine_rate = clk_scu_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = clk_scu_set_rate, }; @@ -475,7 +458,6 @@ struct clk_hw *__imx_clk_scu(struct device *dev, const char *name, clk->clk_type = clk_type; init.name = name; - init.ops = &clk_scu_ops; if (rsrc_id == IMX_SC_R_A35 || rsrc_id == IMX_SC_R_A53 || rsrc_id == IMX_SC_R_A72) init.ops = &clk_scu_cpu_ops; else if (rsrc_id == IMX_SC_R_PI_0_PLL) diff --git a/drivers/clk/kunit_clk_assigned_rates.h b/drivers/clk/kunit_clk_assigned_rates.h index df2d84dcaa93..d7ae5ec2d25b 100644 --- a/drivers/clk/kunit_clk_assigned_rates.h +++ b/drivers/clk/kunit_clk_assigned_rates.h @@ -1,8 +1,18 @@ /* SPDX-License-Identifier: GPL-2.0 */ + +#include + #ifndef _KUNIT_CLK_ASSIGNED_RATES_H #define _KUNIT_CLK_ASSIGNED_RATES_H #define ASSIGNED_RATES_0_RATE 1600000 #define ASSIGNED_RATES_1_RATE 9700000 +#define ASSIGNED_SSCS_0_MODFREQ 10000 +#define ASSIGNED_SSCS_0_SPREAD 30000 +#define ASSIGNED_SSCS_0_METHOD CLK_SSC_CENTER_SPREAD +#define ASSIGNED_SSCS_1_MODFREQ 20000 +#define ASSIGNED_SSCS_1_SPREAD 40000 +#define ASSIGNED_SSCS_1_METHOD CLK_SSC_UP_SPREAD + #endif diff --git a/drivers/clk/kunit_clk_assigned_rates_u64_multiple.dtso b/drivers/clk/kunit_clk_assigned_rates_u64_multiple.dtso index 389b4e2eb7f7..3a717dab2d00 100644 --- a/drivers/clk/kunit_clk_assigned_rates_u64_multiple.dtso +++ b/drivers/clk/kunit_clk_assigned_rates_u64_multiple.dtso @@ -12,5 +12,11 @@ <&clk 1>; assigned-clock-rates-u64 = /bits/ 64 , /bits/ 64 ; + assigned-clock-sscs = , + ; }; }; diff --git a/drivers/clk/kunit_clk_assigned_rates_u64_multiple_consumer.dtso b/drivers/clk/kunit_clk_assigned_rates_u64_multiple_consumer.dtso index 3e117fd59b7d..cbee7cbad068 100644 --- a/drivers/clk/kunit_clk_assigned_rates_u64_multiple_consumer.dtso +++ b/drivers/clk/kunit_clk_assigned_rates_u64_multiple_consumer.dtso @@ -16,5 +16,11 @@ <&clk 1>; assigned-clock-rates-u64 = /bits/ 64 , /bits/ 64 ; + assigned-clock-sscs = , + ; }; }; diff --git a/drivers/clk/kunit_clk_assigned_rates_u64_one.dtso b/drivers/clk/kunit_clk_assigned_rates_u64_one.dtso index 87041264e8f5..9b04d6927f08 100644 --- a/drivers/clk/kunit_clk_assigned_rates_u64_one.dtso +++ b/drivers/clk/kunit_clk_assigned_rates_u64_one.dtso @@ -10,5 +10,8 @@ #clock-cells = <0>; assigned-clocks = <&clk>; assigned-clock-rates-u64 = /bits/ 64 ; + assigned-clock-sscs = ; }; }; diff --git a/drivers/clk/kunit_clk_assigned_rates_u64_one_consumer.dtso b/drivers/clk/kunit_clk_assigned_rates_u64_one_consumer.dtso index 3259c003aec0..4784d40520f4 100644 --- a/drivers/clk/kunit_clk_assigned_rates_u64_one_consumer.dtso +++ b/drivers/clk/kunit_clk_assigned_rates_u64_one_consumer.dtso @@ -14,5 +14,8 @@ compatible = "test,clk-consumer"; assigned-clocks = <&clk>; assigned-clock-rates-u64 = /bits/ 64 ; + assigned-clock-sscs = ; }; }; diff --git a/drivers/clk/kunit_clk_assigned_sscs_multiple.dtso b/drivers/clk/kunit_clk_assigned_sscs_multiple.dtso new file mode 100644 index 000000000000..e3472f95987c --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_multiple.dtso @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <1>; + assigned-clocks = <&clk 0>, + <&clk 1>; + assigned-clock-sscs = , + ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_multiple_consumer.dtso b/drivers/clk/kunit_clk_assigned_sscs_multiple_consumer.dtso new file mode 100644 index 000000000000..6e8971bd272a --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_multiple_consumer.dtso @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <1>; + }; + + kunit-clock-consumer { + compatible = "test,clk-consumer"; + assigned-clocks = <&clk 0>, + <&clk 1>; + assigned-clock-sscs = , + ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_null.dtso b/drivers/clk/kunit_clk_assigned_sscs_null.dtso new file mode 100644 index 000000000000..43b2068c845d --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_null.dtso @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + assigned-clocks = <0>; + assigned-clock-sscs = ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_null_consumer.dtso b/drivers/clk/kunit_clk_assigned_sscs_null_consumer.dtso new file mode 100644 index 000000000000..bda008f5aaa3 --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_null_consumer.dtso @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + }; + + kunit-clock-consumer { + compatible = "test,clk-consumer"; + assigned-clocks = <0>; + assigned-clock-sscs = ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_one.dtso b/drivers/clk/kunit_clk_assigned_sscs_one.dtso new file mode 100644 index 000000000000..91f585b5d8c9 --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_one.dtso @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + assigned-clocks = <&clk>; + assigned-clock-sscs = ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_one_consumer.dtso b/drivers/clk/kunit_clk_assigned_sscs_one_consumer.dtso new file mode 100644 index 000000000000..0bc8a03c2041 --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_one_consumer.dtso @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + }; + + kunit-clock-consumer { + compatible = "test,clk-consumer"; + assigned-clocks = <&clk>; + assigned-clock-sscs = ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_without.dtso b/drivers/clk/kunit_clk_assigned_sscs_without.dtso new file mode 100644 index 000000000000..08660846b55c --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_without.dtso @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + assigned-clock-sscs = ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_without_consumer.dtso b/drivers/clk/kunit_clk_assigned_sscs_without_consumer.dtso new file mode 100644 index 000000000000..e1c089c6f0c0 --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_without_consumer.dtso @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +#include "kunit_clk_assigned_rates.h" + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + }; + + kunit-clock-consumer { + compatible = "test,clk-consumer"; + assigned-clock-sscs = ; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_zero.dtso b/drivers/clk/kunit_clk_assigned_sscs_zero.dtso new file mode 100644 index 000000000000..f39f4e754e53 --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_zero.dtso @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + assigned-clocks = <&clk>; + assigned-clock-sscs = <0 0 0>; + }; +}; diff --git a/drivers/clk/kunit_clk_assigned_sscs_zero_consumer.dtso b/drivers/clk/kunit_clk_assigned_sscs_zero_consumer.dtso new file mode 100644 index 000000000000..d6bd7dfada7e --- /dev/null +++ b/drivers/clk/kunit_clk_assigned_sscs_zero_consumer.dtso @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/dts-v1/; +/plugin/; + +&{/} { + clk: kunit-clock { + compatible = "test,clk-assigned-rates"; + #clock-cells = <0>; + }; + + kunit-clock-consumer { + compatible = "test,clk-consumer"; + assigned-clocks = <&clk>; + assigned-clock-sscs = <0 0 0>; + }; +}; diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig index 2c09fd729bab..b059d44577f1 100644 --- a/drivers/clk/mediatek/Kconfig +++ b/drivers/clk/mediatek/Kconfig @@ -537,6 +537,16 @@ config COMMON_CLK_MT8173_IMGSYS help This driver supports MediaTek MT8173 imgsys clocks. +config COMMON_CLK_MT8173_MFGTOP + tristate "Clock and power driver for MediaTek MT8173 mfgtop" + depends on COMMON_CLK_MT8173 + depends on PM + default COMMON_CLK_MT8173 + select PM_GENERIC_DOMAINS + select PM_GENERIC_DOMAINS_OF + help + This driver supports MediaTek MT8173 mfgtop clocks and power domain. + config COMMON_CLK_MT8173_MMSYS tristate "Clock driver for MediaTek MT8173 mmsys" depends on COMMON_CLK_MT8173 @@ -1006,6 +1016,7 @@ config COMMON_CLK_MT8196 tristate "Clock driver for MediaTek MT8196" depends on ARM64 || COMPILE_TEST select COMMON_CLK_MEDIATEK + select REGMAP_MMIO default ARCH_MEDIATEK help This driver supports MediaTek MT8196 basic clocks. diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index d8736a060dbd..892a54eeb281 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_COMMON_CLK_MT8167_VDECSYS) += clk-mt8167-vdec.o obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173-apmixedsys.o clk-mt8173-infracfg.o \ clk-mt8173-pericfg.o clk-mt8173-topckgen.o obj-$(CONFIG_COMMON_CLK_MT8173_IMGSYS) += clk-mt8173-img.o +obj-$(CONFIG_COMMON_CLK_MT8173_MFGTOP) += clk-mt8173-mfgtop.o obj-$(CONFIG_COMMON_CLK_MT8173_MMSYS) += clk-mt8173-mm.o obj-$(CONFIG_COMMON_CLK_MT8173_VDECSYS) += clk-mt8173-vdecsys.o obj-$(CONFIG_COMMON_CLK_MT8173_VENCSYS) += clk-mt8173-vencsys.o diff --git a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c index 9e30c089a209..b6eb6a581c31 100644 --- a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c +++ b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c @@ -102,9 +102,12 @@ static int clk_mt6735_apmixed_probe(struct platform_device *pdev) ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get, clk_data); - if (ret) + if (ret) { dev_err(&pdev->dev, "Failed to register clock provider: %d\n", ret); + mtk_clk_unregister_plls(apmixedsys_plls, ARRAY_SIZE(apmixedsys_plls), + clk_data); + } return ret; } diff --git a/drivers/clk/mediatek/clk-mt6735-vdecsys.c b/drivers/clk/mediatek/clk-mt6735-vdecsys.c index 8817085fc1db..d53ebf92e878 100644 --- a/drivers/clk/mediatek/clk-mt6735-vdecsys.c +++ b/drivers/clk/mediatek/clk-mt6735-vdecsys.c @@ -63,6 +63,7 @@ static const struct of_device_id of_match_mt6735_vdecsys[] = { { .compatible = "mediatek,mt6735-vdecsys", .data = &vdecsys_clks }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, of_match_mt6735_vdecsys); static struct platform_driver clk_mt6735_vdecsys = { .probe = mtk_clk_simple_probe, diff --git a/drivers/clk/mediatek/clk-mt6735-vencsys.c b/drivers/clk/mediatek/clk-mt6735-vencsys.c index 8dec7f98492a..64ba9da9fd42 100644 --- a/drivers/clk/mediatek/clk-mt6735-vencsys.c +++ b/drivers/clk/mediatek/clk-mt6735-vencsys.c @@ -37,6 +37,7 @@ static const struct of_device_id of_match_mt6735_vencsys[] = { { .compatible = "mediatek,mt6735-vencsys", .data = &vencsys_clks }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, of_match_mt6735_vencsys); static struct platform_driver clk_mt6735_vencsys = { .probe = mtk_clk_simple_probe, diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c index 084e48a554c2..1d20e15608f7 100644 --- a/drivers/clk/mediatek/clk-mt8135.c +++ b/drivers/clk/mediatek/clk-mt8135.c @@ -409,6 +409,9 @@ static const struct mtk_gate_regs infra_cg_regs = { GATE_MTK_FLAGS(_id, _name, _parent, &infra_cg_regs, _shift, \ &mtk_clk_gate_ops_setclr, CLK_IS_CRITICAL) +#define GATE_ICG_INV(_id, _name, _parent, _shift) \ + GATE_MTK(_id, _name, _parent, &infra_cg_regs, _shift, &mtk_clk_gate_ops_setclr_inv) + static const struct mtk_gate infra_clks[] = { GATE_DUMMY(CLK_DUMMY, "infra_dummy"), GATE_ICG(CLK_INFRA_PMIC_WRAP, "pmic_wrap_ck", "axi_sel", 23), @@ -419,7 +422,7 @@ static const struct mtk_gate infra_clks[] = { GATE_ICG(CLK_INFRA_CPUM, "cpum_ck", "cpum_tck_in", 15), GATE_ICG_AO(CLK_INFRA_M4U, "m4u_ck", "mem_sel", 8), GATE_ICG(CLK_INFRA_MFGAXI, "mfgaxi_ck", "axi_sel", 7), - GATE_ICG(CLK_INFRA_DEVAPC, "devapc_ck", "axi_sel", 6), + GATE_ICG_INV(CLK_INFRA_DEVAPC, "devapc_ck", "axi_sel", 6), GATE_ICG(CLK_INFRA_AUDIO, "audio_ck", "aud_intbus_sel", 5), GATE_ICG(CLK_INFRA_MFG_BUS, "mfg_bus_ck", "axi_sel", 2), GATE_ICG(CLK_INFRA_SMI, "smi_ck", "smi_sel", 1), diff --git a/drivers/clk/mediatek/clk-mt8173-mfgtop.c b/drivers/clk/mediatek/clk-mt8173-mfgtop.c new file mode 100644 index 000000000000..5669ca9954dc --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8173-mfgtop.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Google LLC + * Author: Chen-Yu Tsai + * + * Based on driver in downstream ChromeOS v5.15 kernel. + * + * Copyright (c) 2014 MediaTek Inc. + * Author: Chiawen Lee + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-gate.h" +#include "clk-mtk.h" + +static const struct mtk_gate_regs mfg_cg_regs = { + .sta_ofs = 0x0000, + .clr_ofs = 0x0008, + .set_ofs = 0x0004, +}; + +#define GATE_MFG(_id, _name, _parent, _shift, _flags) \ + GATE_MTK_FLAGS(_id, _name, _parent, &mfg_cg_regs, _shift, &mtk_clk_gate_ops_setclr, _flags) + +/* TODO: The block actually has dividers for the core and mem clocks. */ +static const struct mtk_gate mfg_clks[] = { + GATE_MFG(CLK_MFG_AXI, "mfg_axi", "axi_mfg_in_sel", 0, CLK_SET_RATE_PARENT), + GATE_MFG(CLK_MFG_MEM, "mfg_mem", "mem_mfg_in_sel", 1, CLK_SET_RATE_PARENT), + GATE_MFG(CLK_MFG_G3D, "mfg_g3d", "mfg_sel", 2, CLK_SET_RATE_PARENT), + GATE_MFG(CLK_MFG_26M, "mfg_26m", "clk26m", 3, 0), +}; + +struct mt8173_mfgtop_data { + struct clk_hw_onecell_data *clk_data; + struct regmap *regmap; + struct generic_pm_domain genpd; + struct of_phandle_args parent_pd, child_pd; + struct clk *clk_26m; +}; + +/* Delay count in clock cycles */ +#define MFG_ACTIVE_POWER_CON0 0x24 + #define RST_B_DELAY_CNT GENMASK(7, 0) /* pwr_rst_b de-assert delay during power-up */ + #define CLK_EN_DELAY_CNT GENMASK(15, 8) /* CLK_DIS deassert delay during power-up */ + #define CLK_DIS_DELAY_CNT GENMASK(23, 16) /* CLK_DIS assert delay during power-down */ + #define FORCE_ABORT BIT(30) /* write 1 to force abort a power event */ + #define ACTIVE_PWRCTL_EN BIT(31) /* enable ACTIVE_POWER */ + +#define MFG_ACTIVE_POWER_CON1 0x28 + #define PWR_ON_S_DELAY_CNT GENMASK(7, 0) /* pwr_on_s assert delay during power-up */ + #define ISO_DELAY_CNT GENMASK(15, 8) /* ISO assert delay during power-down */ + #define ISOOFF_DELAY_CNT GENMASK(23, 16) /* ISO de-assert delay during power-up */ + #define RST_DELAY_CNT GENMASK(31, 24) /* pwr_rsb_b assert delay during power-down */ + +static int clk_mt8173_mfgtop_power_on(struct generic_pm_domain *domain) +{ + struct mt8173_mfgtop_data *data = container_of(domain, struct mt8173_mfgtop_data, genpd); + int ret; + + /* drives internal power management */ + ret = clk_prepare_enable(data->clk_26m); + if (ret) + return ret; + + /* Power on/off delays for various signals */ + regmap_write(data->regmap, MFG_ACTIVE_POWER_CON0, + FIELD_PREP(RST_B_DELAY_CNT, 77) | + FIELD_PREP(CLK_EN_DELAY_CNT, 61) | + FIELD_PREP(CLK_DIS_DELAY_CNT, 60) | + FIELD_PREP(ACTIVE_PWRCTL_EN, 0)); + regmap_write(data->regmap, MFG_ACTIVE_POWER_CON1, + FIELD_PREP(PWR_ON_S_DELAY_CNT, 11) | + FIELD_PREP(ISO_DELAY_CNT, 68) | + FIELD_PREP(ISOOFF_DELAY_CNT, 69) | + FIELD_PREP(RST_DELAY_CNT, 77)); + + /* Magic numbers related to core switch sequence and delays */ + regmap_write(data->regmap, 0xe0, 0x7a710184); + regmap_write(data->regmap, 0xe4, 0x835f6856); + regmap_write(data->regmap, 0xe8, 0x002b0234); + regmap_write(data->regmap, 0xec, 0x80000000); + regmap_write(data->regmap, 0xa0, 0x08000000); + + return 0; +} + +static int clk_mt8173_mfgtop_power_off(struct generic_pm_domain *domain) +{ + struct mt8173_mfgtop_data *data = container_of(domain, struct mt8173_mfgtop_data, genpd); + + /* Magic numbers related to core switch sequence and delays */ + regmap_write(data->regmap, 0xec, 0); + + /* drives internal power management */ + clk_disable_unprepare(data->clk_26m); + + return 0; +} + +static int clk_mt8173_mfgtop_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct mt8173_mfgtop_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + + data->clk_data = mtk_devm_alloc_clk_data(dev, ARRAY_SIZE(mfg_clks)); + if (!data->clk_data) + return -ENOMEM; + + /* MTK clock gates also uses regmap */ + data->regmap = device_node_to_regmap(node); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), "Failed to get regmap\n"); + + data->child_pd.np = node; + data->child_pd.args_count = 0; + ret = of_parse_phandle_with_args(node, "power-domains", "#power-domain-cells", 0, + &data->parent_pd); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse power domain\n"); + + devm_pm_runtime_enable(dev); + /* + * Do a pm_runtime_resume_and_get() to workaround a possible + * deadlock between clk_register() and the genpd framework. + */ + ret = pm_runtime_resume_and_get(dev); + if (ret) { + dev_err_probe(dev, ret, "Failed to runtime resume device\n"); + goto put_of_node; + } + + ret = mtk_clk_register_gates(dev, node, mfg_clks, ARRAY_SIZE(mfg_clks), + data->clk_data); + if (ret) { + dev_err_probe(dev, ret, "Failed to register clock gates\n"); + goto put_pm_runtime; + } + + data->clk_26m = clk_hw_get_clk(data->clk_data->hws[CLK_MFG_26M], "26m"); + if (IS_ERR(data->clk_26m)) { + ret = dev_err_probe(dev, PTR_ERR(data->clk_26m), "Failed to get 26 MHz clock\n"); + goto unregister_clks; + } + + ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, data->clk_data); + if (ret) { + dev_err_probe(dev, ret, "Failed to add clk OF provider\n"); + goto put_26m_clk; + } + + data->genpd.name = "mfg-top"; + data->genpd.power_on = clk_mt8173_mfgtop_power_on; + data->genpd.power_off = clk_mt8173_mfgtop_power_off; + ret = pm_genpd_init(&data->genpd, NULL, true); + if (ret) { + dev_err_probe(dev, ret, "Failed to add power domain\n"); + goto del_clk_provider; + } + + ret = of_genpd_add_provider_simple(node, &data->genpd); + if (ret) { + dev_err_probe(dev, ret, "Failed to add power domain OF provider\n"); + goto remove_pd; + } + + ret = of_genpd_add_subdomain(&data->parent_pd, &data->child_pd); + if (ret) { + dev_err_probe(dev, ret, "Failed to link PM domains\n"); + goto del_pd_provider; + } + + pm_runtime_put(dev); + return 0; + +del_pd_provider: + of_genpd_del_provider(node); +remove_pd: + pm_genpd_remove(&data->genpd); +del_clk_provider: + of_clk_del_provider(node); +put_26m_clk: + clk_put(data->clk_26m); +unregister_clks: + mtk_clk_unregister_gates(mfg_clks, ARRAY_SIZE(mfg_clks), data->clk_data); +put_pm_runtime: + pm_runtime_put_sync(dev); +put_of_node: + of_node_put(data->parent_pd.np); + return ret; +} + +static void clk_mt8173_mfgtop_remove(struct platform_device *pdev) +{ + struct mt8173_mfgtop_data *data = platform_get_drvdata(pdev); + struct device_node *node = pdev->dev.of_node; + + of_genpd_remove_subdomain(&data->parent_pd, &data->child_pd); + of_genpd_del_provider(node); + pm_genpd_remove(&data->genpd); + of_clk_del_provider(node); + clk_put(data->clk_26m); + mtk_clk_unregister_gates(mfg_clks, ARRAY_SIZE(mfg_clks), data->clk_data); + of_node_put(data->parent_pd.np); +} + +static const struct of_device_id of_match_clk_mt8173_mfgtop[] = { + { .compatible = "mediatek,mt8173-mfgtop" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, of_match_clk_mt8173_mfgtop); + +static struct platform_driver clk_mt8173_mfgtop_drv = { + .probe = clk_mt8173_mfgtop_probe, + .remove = clk_mt8173_mfgtop_remove, + .driver = { + .name = "clk-mt8173-mfgtop", + .of_match_table = of_match_clk_mt8173_mfgtop, + }, +}; +module_platform_driver(clk_mt8173_mfgtop_drv); + +MODULE_DESCRIPTION("MediaTek MT8173 mfgtop clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/mediatek/clk-mt8196-vlpckgen.c b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c index 7dcc164627c5..ee88dc4a231a 100644 --- a/drivers/clk/mediatek/clk-mt8196-vlpckgen.c +++ b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c @@ -629,7 +629,6 @@ static const struct regmap_config vlpckgen_regmap_config = { .val_bits = 32, .reg_stride = 4, .max_register = 0x1000, - .fast_io = true, }; static int clk_mt8196_vlp_probe(struct platform_device *pdev) diff --git a/drivers/clk/mediatek/clk-pllfh.c b/drivers/clk/mediatek/clk-pllfh.c index aa95cd9197b3..6249fb87b1f5 100644 --- a/drivers/clk/mediatek/clk-pllfh.c +++ b/drivers/clk/mediatek/clk-pllfh.c @@ -197,12 +197,56 @@ static void mtk_clk_unregister_pllfh(struct clk_hw *hw) kfree(fh); } +static void mtk_clk_cleanup_pllfhs(void __iomem *iomem_base, + const struct mtk_pll_data *plls, int num_plls, + void __iomem *iomem_fhctl_base, + struct mtk_pllfh_data *pllfhs, int num_fhs, + struct clk_hw_onecell_data *clk_data) +{ + void __iomem *base = iomem_base; + void __iomem *fhctl_base = iomem_fhctl_base; + int i; + + for (i = num_plls - 1; i >= 0; i--) { + const struct mtk_pll_data *pll = &plls[i]; + struct mtk_pllfh_data *pllfh; + bool use_fhctl; + + if (IS_ERR_OR_NULL(clk_data->hws[pll->id])) + continue; + + pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id); + use_fhctl = fhctl_is_supported_and_enabled(pllfh); + + if (!base) + base = mtk_clk_pll_get_base(clk_data->hws[pll->id], + pll); + + if (use_fhctl) { + if (!fhctl_base) + fhctl_base = pllfh->state.base; + mtk_clk_unregister_pllfh(clk_data->hws[pll->id]); + } else { + mtk_clk_unregister_pll(clk_data->hws[pll->id]); + } + + clk_data->hws[pll->id] = ERR_PTR(-ENOENT); + } + + if (fhctl_base) + iounmap(fhctl_base); + + if (base) + iounmap(base); +} + + int mtk_clk_register_pllfhs(struct device *dev, const struct mtk_pll_data *plls, int num_plls, struct mtk_pllfh_data *pllfhs, int num_fhs, struct clk_hw_onecell_data *clk_data) { - void __iomem *base; + void __iomem *base, *fhctl_base = NULL; int i; struct clk_hw *hw; @@ -238,24 +282,8 @@ int mtk_clk_register_pllfhs(struct device *dev, return 0; err: - while (--i >= 0) { - const struct mtk_pll_data *pll = &plls[i]; - struct mtk_pllfh_data *pllfh; - bool use_fhctl; - - pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id); - use_fhctl = fhctl_is_supported_and_enabled(pllfh); - - if (use_fhctl) - mtk_clk_unregister_pllfh(clk_data->hws[pll->id]); - else - mtk_clk_unregister_pll(clk_data->hws[pll->id]); - - clk_data->hws[pll->id] = ERR_PTR(-ENOENT); - } - - iounmap(base); - + mtk_clk_cleanup_pllfhs(base, plls, i, fhctl_base, pllfhs, num_fhs, + clk_data); return PTR_ERR(hw); } EXPORT_SYMBOL_GPL(mtk_clk_register_pllfhs); @@ -264,38 +292,10 @@ void mtk_clk_unregister_pllfhs(const struct mtk_pll_data *plls, int num_plls, struct mtk_pllfh_data *pllfhs, int num_fhs, struct clk_hw_onecell_data *clk_data) { - void __iomem *base = NULL, *fhctl_base = NULL; - int i; - if (!clk_data) return; - for (i = num_plls; i > 0; i--) { - const struct mtk_pll_data *pll = &plls[i - 1]; - struct mtk_pllfh_data *pllfh; - bool use_fhctl; - - if (IS_ERR_OR_NULL(clk_data->hws[pll->id])) - continue; - - pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id); - use_fhctl = fhctl_is_supported_and_enabled(pllfh); - - if (use_fhctl) { - fhctl_base = pllfh->state.base; - mtk_clk_unregister_pllfh(clk_data->hws[pll->id]); - } else { - base = mtk_clk_pll_get_base(clk_data->hws[pll->id], - pll); - mtk_clk_unregister_pll(clk_data->hws[pll->id]); - } - - clk_data->hws[pll->id] = ERR_PTR(-ENOENT); - } - - if (fhctl_base) - iounmap(fhctl_base); - - iounmap(base); + mtk_clk_cleanup_pllfhs(NULL, plls, num_plls, NULL, pllfhs, + num_fhs, clk_data); } EXPORT_SYMBOL_GPL(mtk_clk_unregister_pllfhs); diff --git a/drivers/clk/microchip/clk-mpfs.c b/drivers/clk/microchip/clk-mpfs.c index ee58304913ef..7f044775aea8 100644 --- a/drivers/clk/microchip/clk-mpfs.c +++ b/drivers/clk/microchip/clk-mpfs.c @@ -285,7 +285,7 @@ static int mpfs_cfg_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned mask = clk_div_mask(cfg->width) << cfg->shift; val = divider_setting << cfg->shift; - regmap_update_bits(cfg->map, cfg->map_offset, val, mask); + regmap_update_bits(cfg->map, cfg->map_offset, mask, val); return 0; } diff --git a/drivers/clk/mmp/clk-pxa1908-apbc.c b/drivers/clk/mmp/clk-pxa1908-apbc.c index 438ece4f047d..16c1f13bca1f 100644 --- a/drivers/clk/mmp/clk-pxa1908-apbc.c +++ b/drivers/clk/mmp/clk-pxa1908-apbc.c @@ -114,7 +114,7 @@ static void pxa1908_clk_reset_init(struct device_node *np, cells[i].bits = BIT(2); cells[i].flags = 0; cells[i].lock = apbc_gate_clks[i].lock; - }; + } mmp_clk_reset_register(np, cells, nr_cells); } diff --git a/drivers/clk/mmp/clk-pxa1908-apbcp.c b/drivers/clk/mmp/clk-pxa1908-apbcp.c index 1aa476103553..4e646e8b7440 100644 --- a/drivers/clk/mmp/clk-pxa1908-apbcp.c +++ b/drivers/clk/mmp/clk-pxa1908-apbcp.c @@ -61,7 +61,7 @@ static void pxa1908_clk_reset_init(struct device_node *np, cells[i].bits = BIT(2); cells[i].flags = 0; cells[i].lock = apbcp_gate_clks[i].lock; - }; + } mmp_clk_reset_register(np, cells, nr_cells); } diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c index 26d52ecaac50..24196e288fe2 100644 --- a/drivers/clk/mvebu/clk-cpu.c +++ b/drivers/clk/mvebu/clk-cpu.c @@ -153,7 +153,7 @@ static int clk_cpu_on_set_rate(struct clk_hw *hwclk, unsigned long rate, static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, unsigned long parent_rate) { - if (__clk_is_enabled(hwclk->clk)) + if (clk_hw_is_enabled(hwclk)) return clk_cpu_on_set_rate(hwclk, rate, parent_rate); else return clk_cpu_off_set_rate(hwclk, rate, parent_rate); diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c index ed061d82fb65..f4f62b241193 100644 --- a/drivers/clk/mvebu/kirkwood.c +++ b/drivers/clk/mvebu/kirkwood.c @@ -253,8 +253,8 @@ struct clk_muxing_soc_desc { struct clk_muxing_ctrl { spinlock_t *lock; - struct clk **muxes; int num_muxes; + struct clk *muxes[] __counted_by(num_muxes); }; static const char *powersave_parents[] = { @@ -297,21 +297,18 @@ static void __init kirkwood_clk_muxing_setup(struct device_node *np, if (WARN_ON(!base)) return; - ctrl = kzalloc_obj(*ctrl); - if (WARN_ON(!ctrl)) - goto ctrl_out; - - /* lock must already be initialized */ - ctrl->lock = &ctrl_gating_lock; - /* Count, allocate, and register clock muxes */ for (n = 0; desc[n].name;) n++; + ctrl = kzalloc_flex(*ctrl, muxes, n); + if (WARN_ON(!ctrl)) + goto ctrl_out; + ctrl->num_muxes = n; - ctrl->muxes = kzalloc_objs(struct clk *, ctrl->num_muxes); - if (WARN_ON(!ctrl->muxes)) - goto muxes_out; + + /* lock must already be initialized */ + ctrl->lock = &ctrl_gating_lock; for (n = 0; n < ctrl->num_muxes; n++) { ctrl->muxes[n] = clk_register_mux(NULL, desc[n].name, @@ -324,8 +321,6 @@ static void __init kirkwood_clk_muxing_setup(struct device_node *np, of_clk_add_provider(np, clk_muxing_get_src, ctrl); return; -muxes_out: - kfree(ctrl); ctrl_out: iounmap(base); } diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c index 4620acfe47e8..c7c0dc91a012 100644 --- a/drivers/clk/nuvoton/clk-ma35d1-pll.c +++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c @@ -48,7 +48,7 @@ #define PLL_CTL1_PD BIT(0) #define PLL_CTL1_BP BIT(1) #define PLL_CTL1_OUTDIV GENMASK(6, 4) -#define PLL_CTL1_FRAC GENMASK(31, 24) +#define PLL_CTL1_FRAC GENMASK(31, 8) #define PLL_CTL2_SLOPE GENMASK(23, 0) #define INDIV_MIN 1 @@ -92,7 +92,7 @@ static unsigned long ma35d1_calc_smic_pll_freq(u32 pll0_ctl0, p = FIELD_GET(SPLL0_CTL0_OUTDIV, pll0_ctl0); outdiv = 1 << p; pll_freq = (u64)parent_rate * n; - div_u64(pll_freq, m * outdiv); + pll_freq = div_u64(pll_freq, m * outdiv); return pll_freq; } @@ -110,12 +110,12 @@ static unsigned long ma35d1_calc_pll_freq(u8 mode, u32 *reg_ctl, unsigned long p if (mode == PLL_MODE_INT) { pll_freq = (u64)parent_rate * n; - div_u64(pll_freq, m * p); + pll_freq = div_u64(pll_freq, m * p); } else { x = FIELD_GET(PLL_CTL1_FRAC, reg_ctl[1]); - /* 2 decimal places floating to integer (ex. 1.23 to 123) */ - n = n * 100 + ((x * 100) / FIELD_MAX(PLL_CTL1_FRAC)); - pll_freq = div_u64(parent_rate * n, 100 * m * p); + /* convert 24-bit fraction to 3 decimal digits, rounding to closest */ + n = n * 1000 + DIV_ROUND_CLOSEST_ULL((u64)x * 1000, 1ULL << 24); + pll_freq = div_u64((u64)parent_rate * n, 1000 * m * p); } return pll_freq; } @@ -255,32 +255,32 @@ static int ma35d1_clk_pll_determine_rate(struct clk_hw *hw, if (req->best_parent_rate < PLL_FREF_MIN_FREQ || req->best_parent_rate > PLL_FREF_MAX_FREQ) return -EINVAL; - ret = ma35d1_pll_find_closest(pll, req->rate, req->best_parent_rate, - reg_ctl, &pll_freq); - if (ret < 0) - return ret; - switch (pll->id) { case CAPLL: - reg_ctl[0] = readl_relaxed(pll->ctl0_base); - pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], req->best_parent_rate); - req->rate = pll_freq; - - return 0; case DDRPLL: + /* Read-only PLLs: return current rate */ + reg_ctl[0] = readl_relaxed(pll->ctl0_base); + if (pll->id == CAPLL) { + pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], req->best_parent_rate); + } else { + reg_ctl[1] = readl_relaxed(pll->ctl1_base); + pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, req->best_parent_rate); + } + req->rate = pll_freq; + return 0; case APLL: case EPLL: case VPLL: - reg_ctl[0] = readl_relaxed(pll->ctl0_base); - reg_ctl[1] = readl_relaxed(pll->ctl1_base); - pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, req->best_parent_rate); + /* Configurable PLLs: find closest achievable rate */ + ret = ma35d1_pll_find_closest(pll, req->rate, req->best_parent_rate, + reg_ctl, &pll_freq); + if (ret < 0) + return ret; req->rate = pll_freq; - return 0; } req->rate = 0; - return 0; } diff --git a/drivers/clk/qcom/clk-rpm.c b/drivers/clk/qcom/clk-rpm.c index be0145631197..74f569b58017 100644 --- a/drivers/clk/qcom/clk-rpm.c +++ b/drivers/clk/qcom/clk-rpm.c @@ -351,17 +351,6 @@ static int clk_rpm_set_rate(struct clk_hw *hw, return 0; } -static int clk_rpm_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * RPM handles rate rounding and we don't have a way to - * know what the rate will be, so just return whatever - * rate is requested. - */ - return 0; -} - static unsigned long clk_rpm_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -383,7 +372,7 @@ static const struct clk_ops clk_rpm_xo_ops = { static const struct clk_ops clk_rpm_fixed_ops = { .prepare = clk_rpm_fixed_prepare, .unprepare = clk_rpm_fixed_unprepare, - .determine_rate = clk_rpm_determine_rate, + .determine_rate = clk_determine_rate_noop, .recalc_rate = clk_rpm_recalc_rate, }; @@ -391,7 +380,7 @@ static const struct clk_ops clk_rpm_ops = { .prepare = clk_rpm_prepare, .unprepare = clk_rpm_unprepare, .set_rate = clk_rpm_set_rate, - .determine_rate = clk_rpm_determine_rate, + .determine_rate = clk_determine_rate_noop, .recalc_rate = clk_rpm_recalc_rate, }; diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c index 6367b2a5a4e0..a224a94ae273 100644 --- a/drivers/clk/qcom/clk-rpmh.c +++ b/drivers/clk/qcom/clk-rpmh.c @@ -319,12 +319,6 @@ static int clk_rpmh_bcm_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static int clk_rpmh_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return 0; -} - static unsigned long clk_rpmh_bcm_recalc_rate(struct clk_hw *hw, unsigned long prate) { @@ -337,7 +331,7 @@ static const struct clk_ops clk_rpmh_bcm_ops = { .prepare = clk_rpmh_bcm_prepare, .unprepare = clk_rpmh_bcm_unprepare, .set_rate = clk_rpmh_bcm_set_rate, - .determine_rate = clk_rpmh_determine_rate, + .determine_rate = clk_determine_rate_noop, .recalc_rate = clk_rpmh_bcm_recalc_rate, }; diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 0b624ed4715c..487d715ed5d2 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -370,17 +370,6 @@ static int clk_smd_rpm_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -static int clk_smd_rpm_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * RPM handles rate rounding and we don't have a way to - * know what the rate will be, so just return whatever - * rate is requested. - */ - return 0; -} - static unsigned long clk_smd_rpm_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -427,7 +416,7 @@ static const struct clk_ops clk_smd_rpm_ops = { .prepare = clk_smd_rpm_prepare, .unprepare = clk_smd_rpm_unprepare, .set_rate = clk_smd_rpm_set_rate, - .determine_rate = clk_smd_rpm_determine_rate, + .determine_rate = clk_determine_rate_noop, .recalc_rate = clk_smd_rpm_recalc_rate, }; diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index 975b705d3a2b..a899c37dd4cd 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -944,12 +944,6 @@ static unsigned long rzg2l_cpg_sipll5_recalc_rate(struct clk_hw *hw, return pll5_rate; } -static int rzg2l_cpg_sipll5_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return 0; -} - static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) @@ -1021,7 +1015,7 @@ static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw, static const struct clk_ops rzg2l_cpg_sipll5_ops = { .recalc_rate = rzg2l_cpg_sipll5_recalc_rate, - .determine_rate = rzg2l_cpg_sipll5_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = rzg2l_cpg_sipll5_set_rate, }; diff --git a/drivers/clk/samsung/clk-acpm.c b/drivers/clk/samsung/clk-acpm.c index 953ca8d5720a..7680bc8c5275 100644 --- a/drivers/clk/samsung/clk-acpm.c +++ b/drivers/clk/samsung/clk-acpm.c @@ -72,18 +72,6 @@ static unsigned long acpm_clk_recalc_rate(struct clk_hw *hw, clk->id); } -static int acpm_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * We can't figure out what rate it will be, so just return the - * rate back to the caller. acpm_clk_recalc_rate() will be called - * after the rate is set and we'll know what rate the clock is - * running at then. - */ - return 0; -} - static int acpm_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -95,7 +83,7 @@ static int acpm_clk_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops acpm_clk_ops = { .recalc_rate = acpm_clk_recalc_rate, - .determine_rate = acpm_clk_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = acpm_clk_set_rate, }; @@ -166,8 +154,8 @@ static int acpm_clk_probe(struct platform_device *pdev) } static const struct platform_device_id acpm_clk_id[] = { - { "gs101-acpm-clk" }, - {} + { .name = "gs101-acpm-clk" }, + { } }; MODULE_DEVICE_TABLE(platform, acpm_clk_id); diff --git a/drivers/clk/sprd/pll.c b/drivers/clk/sprd/pll.c index bc2db19aec0e..4aec30777d8a 100644 --- a/drivers/clk/sprd/pll.c +++ b/drivers/clk/sprd/pll.c @@ -254,16 +254,10 @@ static int sprd_pll_clk_prepare(struct clk_hw *hw) return 0; } -static int sprd_pll_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return 0; -} - const struct clk_ops sprd_pll_ops = { .prepare = sprd_pll_clk_prepare, .recalc_rate = sprd_pll_recalc_rate, - .determine_rate = sprd_pll_determine_rate, + .determine_rate = clk_determine_rate_noop, .set_rate = sprd_pll_set_rate, }; EXPORT_SYMBOL_GPL(sprd_pll_ops); diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index 9eb2cb83fa69..17551c6a879a 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -260,7 +260,7 @@ static int __clkgen_pll_enable(struct clk_hw *hw) if (pll->data->switch2pll_en) CLKGEN_WRITE(pll, switch2pll, 0); - pr_debug("%s:%s enabled\n", __clk_get_name(hw->clk), __func__); + pr_debug("%s:%s enabled\n", clk_hw_get_name(hw), __func__); } return ret; @@ -295,7 +295,7 @@ static void __clkgen_pll_disable(struct clk_hw *hw) CLKGEN_WRITE(pll, pdn_ctrl, 1); - pr_debug("%s:%s disabled\n", __clk_get_name(hw->clk), __func__); + pr_debug("%s:%s disabled\n", clk_hw_get_name(hw), __func__); } static void clkgen_pll_disable(struct clk_hw *hw) @@ -405,14 +405,14 @@ static int stm_pll3200c32_determine_rate(struct clk_hw *hw, &req->rate); else { pr_debug("%s: %s rate %ld Invalid\n", __func__, - __clk_get_name(hw->clk), req->rate); + clk_hw_get_name(hw), req->rate); req->rate = 0; return 0; } pr_debug("%s: %s new rate %ld [ndiv=%u] [idf=%u]\n", - __func__, __clk_get_name(hw->clk), + __func__, clk_hw_get_name(hw), req->rate, (unsigned int)params.ndiv, (unsigned int)params.idf); @@ -434,7 +434,7 @@ static int set_rate_stm_pll3200c32(struct clk_hw *hw, unsigned long rate, clk_pll3200c32_get_rate(parent_rate, ¶ms, &hwrate); pr_debug("%s: %s new rate %ld [ndiv=0x%x] [idf=0x%x]\n", - __func__, __clk_get_name(hw->clk), + __func__, clk_hw_get_name(hw), hwrate, (unsigned int)params.ndiv, (unsigned int)params.idf); @@ -547,7 +547,7 @@ static unsigned long recalc_stm_pll4600c28(struct clk_hw *hw, clk_pll4600c28_get_rate(parent_rate, ¶ms, &rate); - pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate); + pr_debug("%s:%s rate %lu\n", clk_hw_get_name(hw), __func__, rate); return rate; } @@ -562,14 +562,14 @@ static int stm_pll4600c28_determine_rate(struct clk_hw *hw, &req->rate); } else { pr_debug("%s: %s rate %ld Invalid\n", __func__, - __clk_get_name(hw->clk), req->rate); + clk_hw_get_name(hw), req->rate); req->rate = 0; return 0; } pr_debug("%s: %s new rate %ld [ndiv=%u] [idf=%u]\n", - __func__, __clk_get_name(hw->clk), + __func__, clk_hw_get_name(hw), req->rate, (unsigned int)params.ndiv, (unsigned int)params.idf); @@ -591,12 +591,12 @@ static int set_rate_stm_pll4600c28(struct clk_hw *hw, unsigned long rate, clk_pll4600c28_get_rate(parent_rate, ¶ms, &hwrate); } else { pr_debug("%s: %s rate %ld Invalid\n", __func__, - __clk_get_name(hw->clk), rate); + clk_hw_get_name(hw), rate); return -EINVAL; } pr_debug("%s: %s new rate %ld [ndiv=0x%x] [idf=0x%x]\n", - __func__, __clk_get_name(hw->clk), + __func__, clk_hw_get_name(hw), hwrate, (unsigned int)params.ndiv, (unsigned int)params.idf); diff --git a/drivers/clk/stm32/clk-stm32mp21.c b/drivers/clk/stm32/clk-stm32mp21.c index c8a37b716bd5..bdb17419908c 100644 --- a/drivers/clk/stm32/clk-stm32mp21.c +++ b/drivers/clk/stm32/clk-stm32mp21.c @@ -4,6 +4,7 @@ * Author: Gabriel Fernandez for STMicroelectronics. */ +#include #include #include #include diff --git a/drivers/clk/stm32/clk-stm32mp25.c b/drivers/clk/stm32/clk-stm32mp25.c index 52f0e8a12926..eb0bc918ecee 100644 --- a/drivers/clk/stm32/clk-stm32mp25.c +++ b/drivers/clk/stm32/clk-stm32mp25.c @@ -4,6 +4,7 @@ * Author: Gabriel Fernandez for STMicroelectronics. */ +#include #include #include #include diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c index f6d2b934228b..067a8555c4e7 100644 --- a/drivers/clk/tegra/clk-bpmp.c +++ b/drivers/clk/tegra/clk-bpmp.c @@ -367,7 +367,15 @@ static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id, if (err < 0) return err; - strscpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN); + if (dev_to_node(bpmp->dev) == NUMA_NO_NODE) { + strscpy(info->name, response.name, sizeof(info->name)); + } else { + err = snprintf(info->name, sizeof(info->name), "%d-%s", + dev_to_node(bpmp->dev), response.name); + if (WARN_ON(err >= sizeof(info->name))) + return -E2BIG; + } + info->num_parents = response.num_parents; for (i = 0; i < info->num_parents; i++) diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index d86003b6d94f..adfb74f111ef 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -575,6 +575,7 @@ static int _calc_rate(struct clk_hw *hw, struct tegra_clk_pll_freq_table *cfg, break; case 9600000: case 28800000: + case 48000000: /* * PLL_P_OUT1 rate is not listed in PLLA table */ diff --git a/drivers/clk/tegra/clk-tegra114.c b/drivers/clk/tegra/clk-tegra114.c index a4f40533cc43..f1bca81b7776 100644 --- a/drivers/clk/tegra/clk-tegra114.c +++ b/drivers/clk/tegra/clk-tegra114.c @@ -620,10 +620,6 @@ static const char *mux_plld_out0_plld2_out0[] = { }; #define mux_plld_out0_plld2_out0_idx NULL -static const char *mux_pllmcp_clkm[] = { - "pll_m_out0", "pll_c_out0", "pll_p_out0", "clk_m", "pll_m_ud", -}; - static const struct clk_div_table pll_re_div_table[] = { { .val = 0, .div = 1 }, { .val = 1, .div = 2 }, @@ -670,7 +666,6 @@ static struct tegra_clk tegra114_clks[tegra_clk_max] __initdata = { [tegra_clk_csi] = { .dt_id = TEGRA114_CLK_CSI, .present = true }, [tegra_clk_i2c2] = { .dt_id = TEGRA114_CLK_I2C2, .present = true }, [tegra_clk_uartc] = { .dt_id = TEGRA114_CLK_UARTC, .present = true }, - [tegra_clk_emc] = { .dt_id = TEGRA114_CLK_EMC, .present = true }, [tegra_clk_usb2] = { .dt_id = TEGRA114_CLK_USB2, .present = true }, [tegra_clk_usb3] = { .dt_id = TEGRA114_CLK_USB3, .present = true }, [tegra_clk_vde_8] = { .dt_id = TEGRA114_CLK_VDE, .present = true }, @@ -1051,14 +1046,7 @@ static __init void tegra114_periph_clk_init(void __iomem *clk_base, periph_clk_enb_refcnt); clks[TEGRA114_CLK_CSUS] = clk; - /* emc mux */ - clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), - CLK_SET_RATE_NO_REPARENT, - clk_base + CLK_SOURCE_EMC, - 29, 3, 0, &emc_lock); - - clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, + clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC, &emc_lock); clks[TEGRA114_CLK_MC] = clk; @@ -1324,6 +1312,26 @@ static int tegra114_reset_deassert(unsigned long id) return 0; } +static struct clk *tegra114_clk_src_onecell_get(struct of_phandle_args *clkspec, + void *data) +{ + struct clk_hw *hw; + struct clk *clk; + + clk = of_clk_src_onecell_get(clkspec, data); + if (IS_ERR(clk)) + return clk; + + hw = __clk_get_hw(clk); + + if (clkspec->args[0] == TEGRA114_CLK_EMC) { + if (!tegra124_clk_emc_driver_available(hw)) + return ERR_PTR(-EPROBE_DEFER); + } + + return clk; +} + static void __init tegra114_clock_init(struct device_node *np) { struct device_node *node; @@ -1371,7 +1379,10 @@ static void __init tegra114_clock_init(struct device_node *np) tegra_init_special_resets(1, tegra114_reset_assert, tegra114_reset_deassert); - tegra_add_of_provider(np, of_clk_src_onecell_get); + tegra_add_of_provider(np, tegra114_clk_src_onecell_get); + clks[TEGRA114_CLK_EMC] = tegra124_clk_register_emc(clk_base, np, + &emc_lock); + tegra_register_devclks(devclks, ARRAY_SIZE(devclks)); tegra_clk_apply_init_table = tegra114_clock_apply_init_table; diff --git a/drivers/clk/tegra/clk-tegra124-emc.c b/drivers/clk/tegra/clk-tegra124-emc.c index f3b2c96fdcfc..94ac24ea1e6a 100644 --- a/drivers/clk/tegra/clk-tegra124-emc.c +++ b/drivers/clk/tegra/clk-tegra124-emc.c @@ -537,6 +537,7 @@ struct clk *tegra124_clk_register_emc(void __iomem *base, struct device_node *np clk = clk_register(NULL, &tegra->hw); if (IS_ERR(clk)) { + of_node_put(tegra->emc_node); kfree(tegra); return clk; } diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index 0c86cff719e3..df1d7b471745 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -1460,7 +1460,7 @@ static int tegra210_pllx_dyn_ramp(struct tegra_clk_pll *pllx, udelay(1); pr_debug("%s: dynamic ramp to m = %u n = %u p = %u, Fout = %lu kHz\n", - __clk_get_name(pllx->hw.clk), cfg->m, cfg->n, cfg->p, + clk_hw_get_name(&pllx->hw), cfg->m, cfg->n, cfg->p, cfg->input_rate / cfg->m * cfg->n / pllx->params->pdiv_tohw[cfg->p].pdiv / 1000); diff --git a/drivers/clk/ti/adpll.c b/drivers/clk/ti/adpll.c index e305fcbac647..8885d28face5 100644 --- a/drivers/clk/ti/adpll.c +++ b/drivers/clk/ti/adpll.c @@ -483,7 +483,7 @@ static const struct clk_ops ti_adpll_ops = { static int ti_adpll_init_dco(struct ti_adpll_data *d) { - struct clk_init_data init; + struct clk_init_data init = {}; struct clk *clock; const char *postfix; int width, err; @@ -576,7 +576,7 @@ static int ti_adpll_init_clkout(struct ti_adpll_data *d, struct clk *clk1) { struct ti_adpll_clkout_data *co; - struct clk_init_data init; + struct clk_init_data init = {}; struct clk_ops *ops; const char *parent_names[2]; const char *child_name; diff --git a/drivers/clk/ti/composite.c b/drivers/clk/ti/composite.c index c379bbdae25a..01eae8995254 100644 --- a/drivers/clk/ti/composite.c +++ b/drivers/clk/ti/composite.c @@ -52,7 +52,7 @@ static const struct clk_ops ti_composite_gate_ops = { struct component_clk { int num_parents; - const char **parent_names; + struct clk_parent_data *parent_data; struct device_node *node; int type; struct clk_hw *hw; @@ -116,7 +116,7 @@ static void __init _register_composite(void *user, struct clk_hw_omap_comp *cclk = to_clk_hw_comp(hw); struct component_clk *comp; int num_parents = 0; - const char **parent_names = NULL; + struct clk_parent_data *parent_data = NULL; const char *name; int i; int ret; @@ -155,7 +155,7 @@ static void __init _register_composite(void *user, continue; if (comp->num_parents) { num_parents = comp->num_parents; - parent_names = comp->parent_names; + parent_data = comp->parent_data; break; } } @@ -166,8 +166,8 @@ static void __init _register_composite(void *user, } name = ti_dt_clk_name(node); - clk = clk_register_composite(NULL, name, - parent_names, num_parents, + clk = clk_register_composite_pdata(NULL, name, + parent_data, num_parents, _get_hw(cclk, CLK_COMPONENT_TYPE_MUX), &ti_clk_mux_ops, _get_hw(cclk, CLK_COMPONENT_TYPE_DIVIDER), @@ -190,7 +190,7 @@ cleanup: if (!cclk->comp_clks[i]) continue; list_del(&cclk->comp_clks[i]->link); - kfree(cclk->comp_clks[i]->parent_names); + kfree(cclk->comp_clks[i]->parent_data); kfree(cclk->comp_clks[i]); } @@ -237,8 +237,9 @@ int __init ti_clk_add_component(struct device_node *node, struct clk_hw *hw, int type) { unsigned int num_parents; - const char **parent_names; + struct clk_parent_data *parent_data; struct component_clk *clk; + unsigned int i; num_parents = of_clk_get_parent_count(node); @@ -247,20 +248,21 @@ int __init ti_clk_add_component(struct device_node *node, struct clk_hw *hw, return -EINVAL; } - parent_names = kcalloc(num_parents, sizeof(char *), GFP_KERNEL); - if (!parent_names) + parent_data = kcalloc(num_parents, sizeof(*parent_data), GFP_KERNEL); + if (!parent_data) return -ENOMEM; - of_clk_parent_fill(node, parent_names, num_parents); + for (i = 0; i < num_parents; i++) + parent_data[i].index = i; clk = kzalloc_obj(*clk); if (!clk) { - kfree(parent_names); + kfree(parent_data); return -ENOMEM; } clk->num_parents = num_parents; - clk->parent_names = parent_names; + clk->parent_data = parent_data; clk->hw = hw; clk->node = node; clk->type = type; diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index af923b8cb0ed..3b438c2d68aa 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -311,7 +311,7 @@ static struct clk *_register_divider(struct device_node *node, u32 flags, struct clk_omap_divider *div) { - struct clk_init_data init; + struct clk_init_data init = {}; const char *parent_name; const char *name; diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c index d6a0ccfd81db..0fef60e82107 100644 --- a/drivers/clk/ti/mux.c +++ b/drivers/clk/ti/mux.c @@ -119,14 +119,14 @@ const struct clk_ops ti_clk_mux_ops = { }; static struct clk *_register_mux(struct device_node *node, const char *name, - const char * const *parent_names, + const struct clk_parent_data *parent_data, u8 num_parents, unsigned long flags, struct clk_omap_reg *reg, u8 shift, u32 mask, s8 latch, u8 clk_mux_flags, u32 *table) { + struct clk_init_data init = {}; struct clk_omap_mux *mux; struct clk *clk; - struct clk_init_data init; /* allocate the mux */ mux = kzalloc_obj(*mux); @@ -136,7 +136,7 @@ static struct clk *_register_mux(struct device_node *node, const char *name, init.name = name; init.ops = &ti_clk_mux_ops; init.flags = flags; - init.parent_names = parent_names; + init.parent_data = parent_data; init.num_parents = num_parents; /* struct clk_mux assignments */ @@ -167,24 +167,26 @@ static void of_mux_clk_setup(struct device_node *node) struct clk *clk; struct clk_omap_reg reg; unsigned int num_parents; - const char **parent_names; + struct clk_parent_data *parent_data; const char *name; u8 clk_mux_flags = 0; u32 mask = 0; u32 shift = 0; s32 latch = -EINVAL; u32 flags = CLK_SET_RATE_NO_REPARENT; + int i; num_parents = of_clk_get_parent_count(node); if (num_parents < 2) { pr_err("mux-clock %pOFn must have parents\n", node); return; } - parent_names = kcalloc(num_parents, sizeof(char *), GFP_KERNEL); - if (!parent_names) - goto cleanup; + parent_data = kcalloc(num_parents, sizeof(*parent_data), GFP_KERNEL); + if (!parent_data) + return; - of_clk_parent_fill(node, parent_names, num_parents); + for (i = 0; i < num_parents; i++) + parent_data[i].index = i; if (ti_clk_get_reg_addr(node, 0, ®)) goto cleanup; @@ -207,7 +209,7 @@ static void of_mux_clk_setup(struct device_node *node) mask = (1 << fls(mask)) - 1; name = ti_dt_clk_name(node); - clk = _register_mux(node, name, parent_names, num_parents, + clk = _register_mux(node, name, parent_data, num_parents, flags, ®, shift, mask, latch, clk_mux_flags, NULL); @@ -215,7 +217,7 @@ static void of_mux_clk_setup(struct device_node *node) of_clk_add_provider(node, of_clk_src_simple_get, clk); cleanup: - kfree(parent_names); + kfree(parent_data); } CLK_OF_DECLARE(mux_clk, "ti,mux-clock", of_mux_clk_setup); diff --git a/drivers/clk/ultrarisc/Kconfig b/drivers/clk/ultrarisc/Kconfig new file mode 100644 index 000000000000..2eecc6ac3119 --- /dev/null +++ b/drivers/clk/ultrarisc/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config CLK_ULTRARISC + tristate + depends on OF + depends on ARCH_ULTRARISC || COMPILE_TEST + +config CLK_ULTRARISC_DP1000 + tristate "UltraRISC DP1000 clock controller" + select CLK_ULTRARISC + depends on OF && HAS_IOMEM + depends on ARCH_ULTRARISC || COMPILE_TEST + default ARCH_ULTRARISC + help + This driver provides the clock controller for the UltraRISC + DP1000 SoC. It exposes the PLL output, derived fixed-factor + clocks, programmable divider clocks, and peripheral gate + clocks to Linux consumers. diff --git a/drivers/clk/ultrarisc/Makefile b/drivers/clk/ultrarisc/Makefile new file mode 100644 index 000000000000..b013708c9444 --- /dev/null +++ b/drivers/clk/ultrarisc/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_CLK_ULTRARISC) += clk-ultrarisc.o +obj-$(CONFIG_CLK_ULTRARISC_DP1000) += clk-dp1000.o diff --git a/drivers/clk/ultrarisc/clk-dp1000.c b/drivers/clk/ultrarisc/clk-dp1000.c new file mode 100644 index 000000000000..ffea77c4cca0 --- /dev/null +++ b/drivers/clk/ultrarisc/clk-dp1000.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd. + */ + +#include + +#include + +#include "clk-ultrarisc.h" + +#define DP1000_PLL_CFG1_OFFSET 0x400 +#define DP1000_PLL_CFG2_OFFSET 0x404 + +#define DP1000_CCR_UART_OFFSET 0x220 +#define DP1000_CCR_I2C_OFFSET 0x224 +#define DP1000_CCR_GMAC_OFFSET 0x228 +#define DP1000_CCR_SPI_OFFSET 0x22c +#define DP1000_PERI_CLKENA_OFFSET 0x270 + +#define DP1000_CCR_LOAD BIT(16) + +#define DP1000_PERI_MAX_RATE 62500000UL +#define DP1000_CLK_NUM 21 + +static const struct ultrarisc_pll_layout dp1000_pll_layout = { + .cfg1_offset = DP1000_PLL_CFG1_OFFSET, + .cfg2_offset = DP1000_PLL_CFG2_OFFSET, + .frac_mask = GENMASK(23, 0), + .m_mask = GENMASK(23, 16), + .n_mask = GENMASK(11, 6), + .oddiv1_mask = GENMASK(1, 0), + .oddiv2_mask = GENMASK(4, 3), +}; + +static const struct ultrarisc_pll_desc dp1000_plls[] = { + { + .id = DP1000_CLK_SYSPLL, + .name = "syspll_clk", + }, +}; + +#define DP1000_FIXED_FACTOR(_id, _name, _parent, _mult, _div) \ + { \ + .id = (_id), \ + .name = (_name), \ + .parent_id = (_parent), \ + .mult = (_mult), \ + .div = (_div), \ + } + +#define DP1000_DIV(_id, _name, _offset, _parent, _max_rate) \ + { \ + .id = (_id), \ + .name = (_name), \ + .offset = (_offset), \ + .parent_id = (_parent), \ + .max_rate = (_max_rate), \ + .load_mask = DP1000_CCR_LOAD, \ + .div_shift = 8, \ + .div_width = 4, \ + .gate_bit = 0, \ + .divider_flags = CLK_DIVIDER_ONE_BASED, \ + .gate_flags = 0, \ + } + +#define DP1000_GATE(_id, _name, _parent, _bit) \ + { \ + .id = (_id), \ + .name = (_name), \ + .offset = DP1000_PERI_CLKENA_OFFSET, \ + .parent_id = (_parent), \ + .gate_bit = (_bit), \ + .gate_flags = 0, \ + } + +static const struct ultrarisc_fixed_factor_desc dp1000_fixed_factor_clks[] = { + DP1000_FIXED_FACTOR(DP1000_CLK_SYSPLL_DIV2, "syspll_div2_clk", + DP1000_CLK_SYSPLL, 1, 2), + DP1000_FIXED_FACTOR(DP1000_CLK_SUBSYS, "subsys_clk", + DP1000_CLK_SYSPLL_DIV2, 1, 2), + DP1000_FIXED_FACTOR(DP1000_CLK_PCIE_DBI, "pcie_dbi_clk", + DP1000_CLK_SYSPLL, 1, 10), + DP1000_FIXED_FACTOR(DP1000_CLK_PCIEX4_CORE, "pciex4_core_clk", + DP1000_CLK_SYSPLL, 1, 2), + DP1000_FIXED_FACTOR(DP1000_CLK_PCIEX16_CORE, "pciex16_core_clk", + DP1000_CLK_SYSPLL, 1, 1), + DP1000_FIXED_FACTOR(DP1000_CLK_PCIE_AUX, "pcie_aux_clk", + DP1000_CLK_SYSPLL, 1, 40), +}; + +static const struct ultrarisc_divider_desc dp1000_divider_clks[] = { + DP1000_DIV(DP1000_CLK_GMAC, "gmac_clk", DP1000_CCR_GMAC_OFFSET, + DP1000_CLK_SYSPLL_DIV2, 0), + DP1000_DIV(DP1000_CLK_UART_ROOT, "uart_root_clk", + DP1000_CCR_UART_OFFSET, DP1000_CLK_SUBSYS, + DP1000_PERI_MAX_RATE), + DP1000_DIV(DP1000_CLK_I2C_ROOT, "i2c_root_clk", + DP1000_CCR_I2C_OFFSET, DP1000_CLK_SUBSYS, + DP1000_PERI_MAX_RATE), + DP1000_DIV(DP1000_CLK_SPI_ROOT, "spi_root_clk", + DP1000_CCR_SPI_OFFSET, DP1000_CLK_SUBSYS, + DP1000_PERI_MAX_RATE), +}; + +static const struct ultrarisc_gate_desc dp1000_gate_clks[] = { + DP1000_GATE(DP1000_CLK_UART0, "uart0_clk", DP1000_CLK_UART_ROOT, 0), + DP1000_GATE(DP1000_CLK_UART1, "uart1_clk", DP1000_CLK_UART_ROOT, 1), + DP1000_GATE(DP1000_CLK_UART2, "uart2_clk", DP1000_CLK_UART_ROOT, 2), + DP1000_GATE(DP1000_CLK_UART3, "uart3_clk", DP1000_CLK_UART_ROOT, 3), + DP1000_GATE(DP1000_CLK_I2C0, "i2c0_clk", DP1000_CLK_I2C_ROOT, 4), + DP1000_GATE(DP1000_CLK_I2C1, "i2c1_clk", DP1000_CLK_I2C_ROOT, 5), + DP1000_GATE(DP1000_CLK_I2C2, "i2c2_clk", DP1000_CLK_I2C_ROOT, 6), + DP1000_GATE(DP1000_CLK_I2C3, "i2c3_clk", DP1000_CLK_I2C_ROOT, 7), + DP1000_GATE(DP1000_CLK_SPI0, "spi0_clk", DP1000_CLK_SPI_ROOT, 8), + DP1000_GATE(DP1000_CLK_SPI1, "spi1_clk", DP1000_CLK_SPI_ROOT, 9), +}; + +static const struct ultrarisc_clk_soc_data dp1000_clk_soc_data = { + .num_clks = DP1000_CLK_NUM, + .pll_layout = &dp1000_pll_layout, + .plls = dp1000_plls, + .num_plls = ARRAY_SIZE(dp1000_plls), + .fixed_factors = dp1000_fixed_factor_clks, + .num_fixed_factors = ARRAY_SIZE(dp1000_fixed_factor_clks), + .dividers = dp1000_divider_clks, + .num_dividers = ARRAY_SIZE(dp1000_divider_clks), + .gates = dp1000_gate_clks, + .num_gates = ARRAY_SIZE(dp1000_gate_clks), +}; + +static int dp1000_clk_probe(struct platform_device *pdev) +{ + return ultrarisc_clk_probe(pdev, &dp1000_clk_soc_data); +} + +static const struct of_device_id dp1000_clk_of_match[] = { + { .compatible = "ultrarisc,dp1000-clk" }, + { } +}; +MODULE_DEVICE_TABLE(of, dp1000_clk_of_match); + +static struct platform_driver dp1000_clk_driver = { + .probe = dp1000_clk_probe, + .driver = { + .name = "ultrarisc-dp1000-clk", + .of_match_table = dp1000_clk_of_match, + }, +}; +module_platform_driver(dp1000_clk_driver); + +MODULE_IMPORT_NS("CLK_ULTRARISC"); +MODULE_DESCRIPTION("UltraRISC DP1000 clock controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/ultrarisc/clk-ultrarisc.c b/drivers/clk/ultrarisc/clk-ultrarisc.c new file mode 100644 index 000000000000..bbcd2234ea71 --- /dev/null +++ b/drivers/clk/ultrarisc/clk-ultrarisc.c @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "clk-ultrarisc.h" + +struct ultrarisc_pll_clk { + struct clk_hw hw; + void __iomem *base; + const struct ultrarisc_pll_layout *layout; +}; + +struct ultrarisc_divider_clk { + struct clk_divider divider; + struct clk_gate gate; + u32 load_mask; +}; + +#define to_ultrarisc_pll_clk(_hw) \ + container_of(_hw, struct ultrarisc_pll_clk, hw) + +static inline struct ultrarisc_divider_clk *to_ultrarisc_divider_clk(struct clk_hw *hw) +{ + struct clk_divider *divider = to_clk_divider(hw); + + return container_of(divider, struct ultrarisc_divider_clk, divider); +} + +static unsigned long ultrarisc_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ultrarisc_pll_clk *pll = to_ultrarisc_pll_clk(hw); + const struct ultrarisc_pll_layout *layout = pll->layout; + u32 oddiv1_div, oddiv2_div; + u64 mult, rate, den; + u32 frac, m, n; + u32 cfg1, cfg2; + + cfg1 = readl_relaxed(pll->base + layout->cfg1_offset); + cfg2 = readl_relaxed(pll->base + layout->cfg2_offset); + + frac = field_get(layout->frac_mask, cfg1); + m = field_get(layout->m_mask, cfg2); + n = field_get(layout->n_mask, cfg2); + if (!n) + return 0; + + oddiv1_div = 1U << field_get(layout->oddiv1_mask, cfg2); + oddiv2_div = 1U << field_get(layout->oddiv2_mask, cfg2); + + /* + * The output frequency is calculated as: + * fvco = parent * (m + frac / 2^24) / n + * fout = fvco / (2^oddiv1_raw * 2^oddiv2_raw) + * + * The output divider values are derived from the raw register field values as: + * oddivX_div = 1 << oddivX_raw + */ + mult = ((u64)m << 24) + frac; + rate = (u64)parent_rate * mult; + den = ((u64)n << 24) * oddiv1_div * oddiv2_div; + + return div64_u64(rate + (den >> 1), den); +} + +static const struct clk_ops ultrarisc_pll_ro_ops = { + .recalc_rate = ultrarisc_pll_recalc_rate, +}; + +static unsigned long ultrarisc_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_divider *divider = to_clk_divider(hw); + u32 val; + + val = readl_relaxed(divider->reg) >> divider->shift; + val &= clk_div_mask(divider->width); + + return divider_recalc_rate(hw, parent_rate, val, divider->table, + divider->flags, divider->width); +} + +static int ultrarisc_divider_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_divider *divider = to_clk_divider(hw); + + return divider_determine_rate(hw, req, divider->table, divider->width, + divider->flags); +} + +static int ultrarisc_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ultrarisc_divider_clk *divider_clk = to_ultrarisc_divider_clk(hw); + struct clk_divider *divider = ÷r_clk->divider; + int value; + u32 val; + + value = divider_get_val(rate, parent_rate, divider->table, + divider->width, divider->flags); + if (value < 0) + return value; + + scoped_guard(spinlock_irqsave, divider->lock) { + val = readl_relaxed(divider->reg); + val &= ~(clk_div_mask(divider->width) << divider->shift); + val |= value << divider->shift; + writel_relaxed(val, divider->reg); + + if (divider_clk->load_mask) { + /* + * Program the new divider field, then write 1 to the + * load bit to trigger the update. The load bit is + * write-triggered and reads back as 0 on this hardware. + */ + writel_relaxed(val | divider_clk->load_mask, divider->reg); + } + } + + return 0; +} + +static const struct clk_ops ultrarisc_divider_ops = { + .recalc_rate = ultrarisc_divider_recalc_rate, + .determine_rate = ultrarisc_divider_determine_rate, + .set_rate = ultrarisc_divider_set_rate, +}; + +static struct clk_hw *ultrarisc_clk_register_pll(struct device *dev, + const struct ultrarisc_pll_desc *desc, + const struct ultrarisc_pll_layout *layout, + void __iomem *base) +{ + struct clk_parent_data pdata = { .index = 0 }; + struct ultrarisc_pll_clk *pll; + struct clk_init_data init = { + .name = desc->name, + .ops = &ultrarisc_pll_ro_ops, + .parent_data = &pdata, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + }; + int ret; + + pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base = base; + pll->layout = layout; + pll->hw.init = &init; + + ret = devm_clk_hw_register(dev, &pll->hw); + if (ret) + return ERR_PTR(ret); + + return &pll->hw; +} + +static struct clk_hw * +ultrarisc_clk_register_divider(struct device *dev, + const struct ultrarisc_divider_desc *desc, + struct clk_hw *parent_hw, void __iomem *base, + spinlock_t *lock) +{ + const struct clk_parent_data pdata = { .hw = parent_hw }; + void __iomem *reg = base + desc->offset; + struct ultrarisc_divider_clk *divider; + + if (!desc->div_width) + return ERR_PTR(-EINVAL); + + if (!lock) + return ERR_PTR(-EINVAL); + + divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL); + if (!divider) + return ERR_PTR(-ENOMEM); + + divider->divider.reg = reg; + divider->divider.shift = desc->div_shift; + divider->divider.width = desc->div_width; + divider->divider.flags = desc->divider_flags; + divider->divider.lock = lock; + divider->load_mask = desc->load_mask; + divider->gate.reg = reg; + divider->gate.bit_idx = desc->gate_bit; + divider->gate.flags = desc->gate_flags; + divider->gate.lock = lock; + + return devm_clk_hw_register_composite_pdata(dev, desc->name, + &pdata, 1, NULL, NULL, + ÷r->divider.hw, + &ultrarisc_divider_ops, + ÷r->gate.hw, + &clk_gate_ops, 0); +} + +static int ultrarisc_clk_register_fixed_factors(struct device *dev, + struct clk_hw_onecell_data *clk_data, + const struct ultrarisc_clk_soc_data *soc_data) +{ + u32 i; + + for (i = 0; i < soc_data->num_fixed_factors; i++) { + const struct ultrarisc_fixed_factor_desc *desc; + struct clk_hw *parent_hw; + struct clk_hw *hw; + + desc = &soc_data->fixed_factors[i]; + if (desc->id >= clk_data->num || desc->parent_id >= clk_data->num) + return -EINVAL; + + parent_hw = clk_data->hws[desc->parent_id]; + if (!parent_hw) + return -EINVAL; + + hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, desc->name, + parent_hw, 0, + desc->mult, + desc->div); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + clk_data->hws[desc->id] = hw; + } + + return 0; +} + +static int ultrarisc_clk_register_plls(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data, + const struct ultrarisc_clk_soc_data *soc_data, + void __iomem *base) +{ + struct device *dev = &pdev->dev; + u32 i; + + for (i = 0; i < soc_data->num_plls; i++) { + const struct ultrarisc_pll_desc *desc = &soc_data->plls[i]; + struct clk_hw *hw; + + if (desc->id >= clk_data->num) { + dev_err(dev, "%s invalid clock ID %u >= %u\n", + desc->name, desc->id, clk_data->num); + return -EINVAL; + } + + hw = ultrarisc_clk_register_pll(dev, desc, soc_data->pll_layout, base); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + clk_data->hws[desc->id] = hw; + } + + return 0; +} + +static int ultrarisc_clk_register_dividers(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data, + const struct ultrarisc_clk_soc_data *soc_data, + void __iomem *base, + spinlock_t *lock) +{ + struct device *dev = &pdev->dev; + u32 i; + + for (i = 0; i < soc_data->num_dividers; i++) { + const struct ultrarisc_divider_desc *desc; + struct clk_hw *parent_hw; + struct clk_hw *hw; + + desc = &soc_data->dividers[i]; + if (desc->id >= clk_data->num || desc->parent_id >= clk_data->num) + return -EINVAL; + + parent_hw = clk_data->hws[desc->parent_id]; + if (!parent_hw) + return -EINVAL; + + hw = ultrarisc_clk_register_divider(dev, desc, parent_hw, base, + lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + if (desc->max_rate) + clk_hw_set_rate_range(hw, 0, desc->max_rate); + + clk_data->hws[desc->id] = hw; + } + + return 0; +} + +static int ultrarisc_clk_register_gates(struct platform_device *pdev, + struct clk_hw_onecell_data *clk_data, + const struct ultrarisc_clk_soc_data *soc_data, + void __iomem *base, + spinlock_t *lock) +{ + struct device *dev = &pdev->dev; + u32 i; + + for (i = 0; i < soc_data->num_gates; i++) { + const struct ultrarisc_gate_desc *desc; + struct clk_hw *parent_hw; + struct clk_hw *hw; + + desc = &soc_data->gates[i]; + if (desc->id >= clk_data->num || desc->parent_id >= clk_data->num) + return -EINVAL; + + parent_hw = clk_data->hws[desc->parent_id]; + if (!parent_hw) + return -EINVAL; + + hw = devm_clk_hw_register_gate_parent_hw(dev, desc->name, + parent_hw, 0, + base + desc->offset, + desc->gate_bit, + desc->gate_flags, + lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + clk_data->hws[desc->id] = hw; + } + + return 0; +} + +int ultrarisc_clk_probe(struct platform_device *pdev, + const struct ultrarisc_clk_soc_data *soc_data) +{ + struct clk_hw_onecell_data *clk_data; + struct device *dev = &pdev->dev; + void __iomem *base; + spinlock_t *lock; + int ret; + + if (!soc_data) + return -EINVAL; + + lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL); + if (!lock) + return -ENOMEM; + + spin_lock_init(lock); + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, + soc_data->num_clks), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = soc_data->num_clks; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + ret = ultrarisc_clk_register_plls(pdev, clk_data, soc_data, base); + if (ret) + return ret; + + ret = ultrarisc_clk_register_fixed_factors(dev, clk_data, soc_data); + if (ret) + return ret; + + ret = ultrarisc_clk_register_dividers(pdev, clk_data, soc_data, base, lock); + if (ret) + return ret; + + ret = ultrarisc_clk_register_gates(pdev, clk_data, soc_data, base, lock); + if (ret) + return ret; + + for (int i = 0; i < clk_data->num; i++) { + if (!clk_data->hws[i]) { + dev_err(dev, "missing clock ID %u\n", i); + return -EINVAL; + } + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); +} +EXPORT_SYMBOL_NS_GPL(ultrarisc_clk_probe, "CLK_ULTRARISC"); + +MODULE_DESCRIPTION("UltraRISC clock core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/ultrarisc/clk-ultrarisc.h b/drivers/clk/ultrarisc/clk-ultrarisc.h new file mode 100644 index 000000000000..8202b7ed0e31 --- /dev/null +++ b/drivers/clk/ultrarisc/clk-ultrarisc.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __ULTRARISC_CLK_ULTRARISC_H +#define __ULTRARISC_CLK_ULTRARISC_H + +#include +#include +#include + +struct ultrarisc_pll_layout { + u32 cfg1_offset; + u32 cfg2_offset; + u32 frac_mask; + u32 m_mask; + u32 n_mask; + u32 oddiv1_mask; + u32 oddiv2_mask; +}; + +struct ultrarisc_pll_desc { + u32 id; + const char *name; +}; + +struct ultrarisc_fixed_factor_desc { + u32 id; + const char *name; + u32 parent_id; + u32 mult; + u32 div; +}; + +struct ultrarisc_divider_desc { + u32 id; + const char *name; + u32 offset; + u32 parent_id; + unsigned long max_rate; + u32 load_mask; + u8 div_shift; + u8 div_width; + u8 gate_bit; + u16 divider_flags; + u8 gate_flags; +}; + +struct ultrarisc_gate_desc { + u32 id; + const char *name; + u32 offset; + u32 parent_id; + u8 gate_bit; + u8 gate_flags; +}; + +struct ultrarisc_clk_soc_data { + const struct ultrarisc_pll_layout *pll_layout; + const struct ultrarisc_pll_desc *plls; + u32 num_plls; + const struct ultrarisc_fixed_factor_desc *fixed_factors; + u32 num_fixed_factors; + const struct ultrarisc_divider_desc *dividers; + u32 num_dividers; + const struct ultrarisc_gate_desc *gates; + u32 num_gates; + u32 num_clks; +}; + +int ultrarisc_clk_probe(struct platform_device *pdev, + const struct ultrarisc_clk_soc_data *soc_data); + +#endif /* __ULTRARISC_CLK_ULTRARISC_H */ diff --git a/drivers/clk/visconti/clkc.c b/drivers/clk/visconti/clkc.c index d0b193b5d0b3..4018d1298880 100644 --- a/drivers/clk/visconti/clkc.c +++ b/drivers/clk/visconti/clkc.c @@ -81,9 +81,9 @@ static struct clk_hw *visconti_clk_register_gate(struct device *dev, u8 rs_idx, spinlock_t *lock) { + struct clk_init_data init = {}; struct visconti_clk_gate *gate; struct clk_parent_data *pdata; - struct clk_init_data init; struct clk_hw *hw; int ret; diff --git a/drivers/clk/x86/clk-pmc-atom.c b/drivers/clk/x86/clk-pmc-atom.c index 99291ba65da7..08c83e0abc41 100644 --- a/drivers/clk/x86/clk-pmc-atom.c +++ b/drivers/clk/x86/clk-pmc-atom.c @@ -160,6 +160,9 @@ static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id, return ERR_PTR(-ENOMEM); init.name = kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id); + if (!init.name) + return ERR_PTR(-ENOMEM); + init.ops = &plt_clk_ops; init.flags = 0; init.parent_names = parent_names; diff --git a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c index 4a0136349f71..edf8c0eaca27 100644 --- a/drivers/clk/xilinx/clk-xlnx-clock-wizard.c +++ b/drivers/clk/xilinx/clk-xlnx-clock-wizard.c @@ -35,6 +35,10 @@ #define WZRD_DIVCLK 21 #define WZRD_CLKFBOUT_4 51 #define WZRD_CLKFBOUT_3 48 +#define WZRD_CP 18 +#define WZRD_LOCK 27 +#define WZRD_LOCK_REF_DLY 28 +#define WZRD_RES 30 #define WZRD_DUTY_CYCLE 2 #define WZRD_O_DIV 4 @@ -49,6 +53,10 @@ #define WZRD_P5FEDGE_SHIFT 15 #define WZRD_CLKOUT0_PREDIV2 BIT(11) #define WZRD_EDGE_SHIFT 8 +#define WZRD_CP_MASK GENMASK(3, 0) +#define WZRD_RES_MASK GENMASK(4, 1) +#define WZRD_LOCK_FB_DLY_MASK GENMASK(14, 10) +#define WZRD_LOCK_REF_DLY_LOCK_REF_DLY_MASK GENMASK(14, 10) #define WZRD_CLKFBOUT_MULT_SHIFT 8 #define WZRD_CLKFBOUT_MULT_MASK (0xff << WZRD_CLKFBOUT_MULT_SHIFT) @@ -105,7 +113,6 @@ #define VER_WZRD_VCO_MAX 4320000000ULL #define VER_WZRD_O_MIN 2 #define VER_WZRD_O_MAX 511 -#define WZRD_MIN_ERR 20000 #define WZRD_FRAC_POINTS 1000 /* Get the mask from width */ @@ -406,7 +413,7 @@ static int clk_wzrd_get_divisors(struct clk_hw *hw, unsigned long rate, if (o < omin || o > omax) continue; freq = DIV_ROUND_CLOSEST_ULL(vco_freq, o); - diff = freq - rate; + diff = abs(freq - rate); if (diff < best_diff) { best_diff = diff; divider->m = m >> 3; @@ -414,10 +421,13 @@ static int clk_wzrd_get_divisors(struct clk_hw *hw, unsigned long rate, divider->d = d; divider->o = o >> 3; divider->o_frac = (o - (divider->o << 3)) * 125; + + if (!diff) + return 0; } } } - return best_diff < WZRD_MIN_ERR ? 0 : -EBUSY; + return best_diff != -1ULL ? 0 : -EBUSY; } static int clk_wzrd_reconfig(struct clk_wzrd_divider *divider, void __iomem *div_addr) @@ -440,6 +450,130 @@ static int clk_wzrd_reconfig(struct clk_wzrd_divider *divider, void __iomem *div WZRD_USEC_POLL, WZRD_TIMEOUT_POLL); } +struct wzrd_pll_filter { + u32 m_min; + u32 m_max; + u32 cp; + u32 res; +}; + +static const struct wzrd_pll_filter wzrd_cp_res_table[] = { + { 4, 4, 5, 15 }, + { 5, 5, 6, 15 }, + { 6, 6, 7, 15 }, + { 7, 7, 13, 15 }, + { 8, 8, 14, 15 }, + { 9, 9, 15, 15 }, + { 10, 10, 14, 7 }, + { 11, 11, 15, 7 }, + { 12, 13, 15, 11 }, + { 14, 14, 15, 13 }, + { 15, 15, 15, 3 }, + { 16, 17, 14, 5 }, + { 18, 19, 15, 5 }, + { 20, 21, 15, 9 }, + { 22, 23, 14, 14 }, + { 24, 26, 15, 14 }, + { 27, 28, 14, 1 }, + { 29, 33, 15, 1 }, + { 34, 37, 14, 6 }, + { 38, 44, 15, 6 }, + { 45, 57, 15, 10 }, + { 58, 63, 13, 12 }, + { 64, 70, 14, 12 }, + { 71, 86, 15, 12 }, + { 87, 94, 14, 2 }, + { 95, 145, 15, 2 }, + { 146, 163, 12, 4 }, + { 164, 181, 13, 4 }, + { 182, 200, 14, 4 }, + { 201, 273, 15, 4 }, + { 274, 300, 13, 8 }, + { 301, 325, 14, 8 }, + { 326, 432, 15, 8 }, +}; + +struct wzrd_lock_timing { + u32 m_min; + u32 m_max; + u32 ref_dly; + u32 fb_dly; + u32 lock_cnt; +}; + +static const struct wzrd_lock_timing wzrd_lock_table[] = { + { 4, 4, 4, 4, 1000 }, + { 5, 5, 6, 6, 1000 }, + { 6, 8, 7, 7, 1000 }, + { 9, 12, 8, 8, 1000 }, + { 13, 13, 10, 10, 1000 }, + { 14, 16, 13, 13, 1000 }, + { 17, 17, 16, 16, 825 }, + { 18, 18, 16, 16, 750 }, + { 19, 20, 16, 16, 700 }, + { 21, 21, 16, 16, 650 }, + { 22, 23, 16, 16, 625 }, + { 24, 24, 16, 16, 575 }, + { 25, 25, 16, 16, 550 }, + { 26, 28, 16, 16, 525 }, + { 29, 30, 16, 16, 475 }, + { 31, 31, 16, 16, 450 }, + { 32, 33, 16, 16, 425 }, + { 34, 36, 16, 16, 400 }, + { 37, 37, 16, 16, 375 }, + { 38, 40, 16, 16, 350 }, + { 41, 43, 16, 16, 325 }, + { 44, 47, 16, 16, 300 }, + { 48, 51, 16, 16, 275 }, + { 52, 205, 16, 16, 250 }, + { 206, 432, 16, 16, 225 }, +}; + +static void clk_wzrd_update_cp_res_lock(struct clk_wzrd_divider *divider, u32 m) +{ + u32 lock_ref_dly = 16, lock_fb_dly = 16, lock_cnt = 250, cp = 15, res = 15; + void __iomem *base = divider->base; + u32 reg; + int i; + + for (i = 0; i < ARRAY_SIZE(wzrd_cp_res_table); i++) { + if (m >= wzrd_cp_res_table[i].m_min && + m <= wzrd_cp_res_table[i].m_max) { + cp = wzrd_cp_res_table[i].cp; + res = wzrd_cp_res_table[i].res; + break; + } + } + + for (i = 0; i < ARRAY_SIZE(wzrd_lock_table); i++) { + if (m >= wzrd_lock_table[i].m_min && + m <= wzrd_lock_table[i].m_max) { + lock_ref_dly = wzrd_lock_table[i].ref_dly; + lock_fb_dly = wzrd_lock_table[i].fb_dly; + lock_cnt = wzrd_lock_table[i].lock_cnt; + break; + } + } + + reg = readl(base + WZRD_CLK_CFG_REG(1, WZRD_CP)); + reg &= ~WZRD_CP_MASK; + reg |= FIELD_PREP(WZRD_CP_MASK, cp); + writel(reg, base + WZRD_CLK_CFG_REG(1, WZRD_CP)); + + reg = readl(base + WZRD_CLK_CFG_REG(1, WZRD_RES)); + reg &= ~WZRD_RES_MASK; + reg |= FIELD_PREP(WZRD_RES_MASK, res); + writel(reg, base + WZRD_CLK_CFG_REG(1, WZRD_RES)); + + reg = lock_cnt | FIELD_PREP(WZRD_LOCK_FB_DLY_MASK, lock_fb_dly); + writel(reg, base + WZRD_CLK_CFG_REG(1, WZRD_LOCK)); + + reg = readl(base + WZRD_CLK_CFG_REG(1, WZRD_LOCK_REF_DLY)); + reg &= ~WZRD_LOCK_REF_DLY_LOCK_REF_DLY_MASK; + reg |= FIELD_PREP(WZRD_LOCK_REF_DLY_LOCK_REF_DLY_MASK, lock_ref_dly); + writel(reg, base + WZRD_CLK_CFG_REG(1, WZRD_LOCK_REF_DLY)); +} + static int clk_wzrd_dynamic_ver_all_nolock(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -471,6 +605,8 @@ static int clk_wzrd_dynamic_ver_all_nolock(struct clk_hw *hw, unsigned long rate writel(regval1, divider->base + WZRD_CLK_CFG_REG(1, WZRD_CLKFBOUT_2)); + clk_wzrd_update_cp_res_lock(divider, m); + value2 = divider->d; edged = value2 % WZRD_DUTY_CYCLE; regh = (value2 / WZRD_DUTY_CYCLE); @@ -663,8 +799,8 @@ static int clk_wzrd_determine_rate_all(struct clk_hw *hw, d = divider->d; o = divider->o; - req->rate = div_u64(req->best_parent_rate * (m * 1000 + divider->m_frac), - d * (o * 1000 + divider->o_frac)); + req->rate = mult_frac(req->best_parent_rate, m * 1000 + divider->m_frac, + d * (o * 1000 + divider->o_frac)); return 0; } diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 6a22cbbc1235..777187744f4e 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -186,7 +186,11 @@ static void __init zynq_clk_register_periph_clk(enum zynq_clk clk0, spin_lock_init(lock); mux_name = kasprintf(GFP_KERNEL, "%s_mux", clk_name0); + if (!mux_name) + goto err_mux_name; div_name = kasprintf(GFP_KERNEL, "%s_div", clk_name0); + if (!div_name) + goto err_div_name; clk_register_mux(NULL, mux_name, parents, 4, CLK_SET_RATE_NO_REPARENT, clk_ctrl, 4, 2, 0, lock); @@ -205,6 +209,10 @@ static void __init zynq_clk_register_periph_clk(enum zynq_clk clk0, return; +err_div_name: + kfree(mux_name); +err_mux_name: + kfree(lock); err: clks[clk0] = ERR_PTR(-ENOMEM); if (two_gates) diff --git a/drivers/clk/zynq/pll.c b/drivers/clk/zynq/pll.c index 44c609378364..fe90b50e1545 100644 --- a/drivers/clk/zynq/pll.c +++ b/drivers/clk/zynq/pll.c @@ -42,11 +42,11 @@ struct zynq_pll { #define PLL_FBDIV_MAX 66 /** - * zynq_pll_round_rate() - Round a clock frequency + * zynq_pll_determine_rate() - Round a clock frequency * @hw: Handle between common and hardware-specific interfaces - * @rate: Desired clock frequency - * @prate: Clock frequency of parent clock - * Return: frequency closest to @rate the hardware can generate. + * @req: Clock rate request, updated with the frequency closest to the + * requested one that the hardware can generate + * Return: 0 always */ static int zynq_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) diff --git a/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c b/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c index b0b6497e7eed..c9a1f20e939b 100644 --- a/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c +++ b/drivers/phy/mediatek/phy-mtk-hdmi-mt2701.c @@ -90,12 +90,6 @@ static void mtk_hdmi_pll_unprepare(struct clk_hw *hw) usleep_range(80, 100); } -static int mtk_hdmi_pll_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return 0; -} - static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { @@ -170,7 +164,7 @@ static const struct clk_ops mtk_hdmi_phy_pll_ops = { .prepare = mtk_hdmi_pll_prepare, .unprepare = mtk_hdmi_pll_unprepare, .set_rate = mtk_hdmi_pll_set_rate, - .determine_rate = mtk_hdmi_pll_determine_rate, + .determine_rate = clk_determine_rate_noop, .recalc_rate = mtk_hdmi_pll_recalc_rate, }; diff --git a/drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c b/drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c index 3b1d202f89dc..5efd7a38f4da 100644 --- a/drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c +++ b/drivers/pmdomain/mediatek/airoha-cpu-pmdomain.c @@ -21,12 +21,6 @@ struct airoha_cpu_pmdomain_priv { struct generic_pm_domain pd; }; -static int airoha_cpu_pmdomain_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return 0; -} - static unsigned long airoha_cpu_pmdomain_clk_get(struct clk_hw *hw, unsigned long parent_rate) { @@ -48,7 +42,7 @@ static int airoha_cpu_pmdomain_clk_is_enabled(struct clk_hw *hw) static const struct clk_ops airoha_cpu_pmdomain_clk_ops = { .recalc_rate = airoha_cpu_pmdomain_clk_get, .is_enabled = airoha_cpu_pmdomain_clk_is_enabled, - .determine_rate = airoha_cpu_pmdomain_clk_determine_rate, + .determine_rate = clk_determine_rate_noop, }; static int airoha_cpu_pmdomain_set_performance_state(struct generic_pm_domain *domain, diff --git a/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c b/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c index 53bdab66cf15..cdcd1d1a812f 100644 --- a/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c +++ b/drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c @@ -310,25 +310,6 @@ static unsigned long mtk_mfg_recalc_rate_gpu(struct clk_hw *hw, return readl(mfg->shared_mem + GF_REG_FREQ_OUT_GPU) * HZ_PER_KHZ; } -static int mtk_mfg_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - /* - * The determine_rate callback needs to be implemented to avoid returning - * the current clock frequency, rather than something even remotely - * close to the frequency that was asked for. - * - * Instead of writing considerable amounts of possibly slow code just to - * somehow figure out which of the three PLLs to round for, or even to - * do a search through one of two OPP tables in order to find the closest - * OPP of a frequency, just return the rate as-is. This avoids devfreq - * "rounding" a request for the lowest frequency to the possibly very - * high current frequency, breaking the powersave governor in the process. - */ - - return 0; -} - static unsigned long mtk_mfg_recalc_rate_stack(struct clk_hw *hw, unsigned long parent_rate) { @@ -339,12 +320,12 @@ static unsigned long mtk_mfg_recalc_rate_stack(struct clk_hw *hw, static const struct clk_ops mtk_mfg_clk_gpu_ops = { .recalc_rate = mtk_mfg_recalc_rate_gpu, - .determine_rate = mtk_mfg_determine_rate, + .determine_rate = clk_determine_rate_noop, }; static const struct clk_ops mtk_mfg_clk_stack_ops = { .recalc_rate = mtk_mfg_recalc_rate_stack, - .determine_rate = mtk_mfg_determine_rate, + .determine_rate = clk_determine_rate_noop, }; static const struct clk_init_data mtk_mfg_clk_gpu_init = { diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index d009eb0849a3..3168ab1eea88 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -83,6 +83,17 @@ config RESET_EIC7700 The driver supports eic7700 series chips and provides functionality for asserting and deasserting resets on the chip. +config RESET_EIC7700_HSP + tristate "EIC7700 HSP Reset controller" + depends on ARCH_ESWIN || COMPILE_TEST + select AUXILIARY_BUS + help + This enables the HSP reset controller driver for ESWIN SoCs. This + driver is specific to ESWIN SoCs and should only be enabled if using + such hardware. + The driver supports EIC7700 series chips and provides functionality + for asserting and deasserting resets on the chip. + config RESET_EYEQ bool "Mobileye EyeQ reset controller" depends on EYEQ || COMPILE_TEST @@ -300,6 +311,19 @@ config RESET_SKY1 help This enables the reset controller for Cix Sky1. +config RESET_SKY1_AUDSS + tristate "Cix Sky1 Audio Subsystem reset controller" + depends on ARCH_CIX || COMPILE_TEST + select AUXILIARY_BUS + default CLK_SKY1_AUDSS + help + Support for block-level software reset lines in the Cix Sky1 + Audio Subsystem (AUDSS) Clock and Reset Unit. Sixteen reset + outputs for audio peripherals are controlled through the CRU + register map. The driver binds as an auxiliary device from + the AUDSS clock driver. Say M or Y here if you want to build + this driver. + config RESET_SOCFPGA bool "SoCFPGA Reset Driver" if COMPILE_TEST && (!ARM || !ARCH_INTEL_SOCFPGA) default ARM && ARCH_INTEL_SOCFPGA diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 3e52569bd276..45ad17062462 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o obj-$(CONFIG_RESET_EIC7700) += reset-eic7700.o +obj-$(CONFIG_RESET_EIC7700_HSP) += reset-eic7700-hsp.o obj-$(CONFIG_RESET_EYEQ) += reset-eyeq.o obj-$(CONFIG_RESET_GPIO) += reset-gpio.o obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o @@ -39,6 +40,7 @@ obj-$(CONFIG_RESET_RZV2H_USB2PHY) += reset-rzv2h-usb2phy.o obj-$(CONFIG_RESET_SCMI) += reset-scmi.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_RESET_SKY1) += reset-sky1.o +obj-$(CONFIG_RESET_SKY1_AUDSS) += reset-sky1-audss.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o diff --git a/drivers/reset/reset-eic7700-hsp.c b/drivers/reset/reset-eic7700-hsp.c new file mode 100644 index 000000000000..1575ee4a20d6 --- /dev/null +++ b/drivers/reset/reset-eic7700-hsp.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * ESWIN EIC7700 HSP Reset Driver + * + * Authors: Xuyang Dong + */ + +#include +#include +#include +#include +#include + +#include + +/** + * struct eic7700_hsp_reset_data - reset controller information structure + * @rcdev: reset controller entity + * @regmap: regmap handle containing the memory-mapped reset registers + */ +struct eic7700_hsp_reset_data { + struct reset_controller_dev rcdev; + struct regmap *regmap; +}; + +struct eic7700_hsp_reg { + u32 reg; + u32 bit; + bool active_low; +}; + +static inline struct eic7700_hsp_reset_data * +to_eic7700_hsp_reset(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct eic7700_hsp_reset_data, rcdev); +} + +static const struct eic7700_hsp_reg eic7700_hsp_reset[] = { + [EIC7700_HSP_RST_SATA_P0] = {0x340, BIT(0), false}, + [EIC7700_HSP_RST_SATA_PHY] = {0x340, BIT(1), false}, + [EIC7700_HSP_RST_USB0] = {0x800, BIT(24), true}, + [EIC7700_HSP_RST_USB1] = {0x900, BIT(24), true}, + [EIC7700_HSP_RST_USB0_PHY] = {0x800, BIT(25), false}, + [EIC7700_HSP_RST_USB1_PHY] = {0x900, BIT(25), false}, +}; + +static int eic7700_hsp_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct eic7700_hsp_reset_data *data = to_eic7700_hsp_reset(rcdev); + + return regmap_assign_bits(data->regmap, eic7700_hsp_reset[id].reg, + eic7700_hsp_reset[id].bit, + !eic7700_hsp_reset[id].active_low); +} + +static int eic7700_hsp_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct eic7700_hsp_reset_data *data = to_eic7700_hsp_reset(rcdev); + + return regmap_assign_bits(data->regmap, eic7700_hsp_reset[id].reg, + eic7700_hsp_reset[id].bit, + eic7700_hsp_reset[id].active_low); +} + +static const struct reset_control_ops eic7700_hsp_reset_ops = { + .assert = eic7700_hsp_reset_assert, + .deassert = eic7700_hsp_reset_deassert, +}; + +static int eic7700_hsp_reset_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct eic7700_hsp_reset_data *data; + struct device *dev = &adev->dev; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = dev_get_regmap(dev->parent, NULL); + if (!data->regmap) + return dev_err_probe(dev, -ENODEV, "failed to get regmap!\n"); + + data->rcdev.owner = THIS_MODULE; + data->rcdev.ops = &eic7700_hsp_reset_ops; + data->rcdev.of_node = dev->parent->of_node; + data->rcdev.dev = dev; + data->rcdev.nr_resets = ARRAY_SIZE(eic7700_hsp_reset); + + return devm_reset_controller_register(dev, &data->rcdev); +} + +static const struct auxiliary_device_id eic7700_hsp_reset_ids[] = { + { .name = "clk_eic7700_hsp.hsp-reset", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(auxiliary, eic7700_hsp_reset_ids); + +static struct auxiliary_driver eic7700_hsp_reset_driver = { + .probe = eic7700_hsp_reset_probe, + .id_table = eic7700_hsp_reset_ids, +}; + +module_auxiliary_driver(eic7700_hsp_reset_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Xuyang Dong "); +MODULE_DESCRIPTION("ESWIN EIC7700 HSP Reset Controller Driver"); diff --git a/drivers/reset/reset-sky1-audss.c b/drivers/reset/reset-sky1-audss.c new file mode 100644 index 000000000000..d9735db681c7 --- /dev/null +++ b/drivers/reset/reset-sky1-audss.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Cix Sky1 Audio Subsystem reset controller driver + * + * Copyright 2026 Cix Technology Group Co., Ltd. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define SKY1_RESET_SLEEP_US 50 + +#define AUDSS_SW_RST 0x78 + +struct sky1_audss_reset_map { + unsigned int offset; + unsigned int mask; +}; + +struct sky1_audss_reset { + struct reset_controller_dev rcdev; + struct regmap *regmap; + const struct sky1_audss_reset_map *map; +}; + +static const struct sky1_audss_reset_map sky1_audss_reset_map[] = { + [AUDSS_I2S0_SW_RST] = { AUDSS_SW_RST, BIT(0) }, + [AUDSS_I2S1_SW_RST] = { AUDSS_SW_RST, BIT(1) }, + [AUDSS_I2S2_SW_RST] = { AUDSS_SW_RST, BIT(2) }, + [AUDSS_I2S3_SW_RST] = { AUDSS_SW_RST, BIT(3) }, + [AUDSS_I2S4_SW_RST] = { AUDSS_SW_RST, BIT(4) }, + [AUDSS_I2S5_SW_RST] = { AUDSS_SW_RST, BIT(5) }, + [AUDSS_I2S6_SW_RST] = { AUDSS_SW_RST, BIT(6) }, + [AUDSS_I2S7_SW_RST] = { AUDSS_SW_RST, BIT(7) }, + [AUDSS_I2S8_SW_RST] = { AUDSS_SW_RST, BIT(8) }, + [AUDSS_I2S9_SW_RST] = { AUDSS_SW_RST, BIT(9) }, + [AUDSS_WDT_SW_RST] = { AUDSS_SW_RST, BIT(10) }, + [AUDSS_TIMER_SW_RST] = { AUDSS_SW_RST, BIT(11) }, + [AUDSS_MB0_SW_RST] = { AUDSS_SW_RST, BIT(12) }, + [AUDSS_MB1_SW_RST] = { AUDSS_SW_RST, BIT(13) }, + [AUDSS_HDA_SW_RST] = { AUDSS_SW_RST, BIT(14) }, + [AUDSS_DMAC_SW_RST] = { AUDSS_SW_RST, BIT(15) }, +}; + +static struct sky1_audss_reset *to_sky1_audss_reset(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct sky1_audss_reset, rcdev); +} + +static int sky1_audss_reset_set(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct sky1_audss_reset *priv = to_sky1_audss_reset(rcdev); + const struct sky1_audss_reset_map *signal = &priv->map[id]; + int ret; + + ret = regmap_assign_bits(priv->regmap, signal->offset, + signal->mask, !assert); + if (ret) + return ret; + + fsleep(SKY1_RESET_SLEEP_US); + return 0; +} + +static int sky1_audss_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return sky1_audss_reset_set(rcdev, id, true); +} + +static int sky1_audss_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return sky1_audss_reset_set(rcdev, id, false); +} + +static const struct reset_control_ops sky1_audss_reset_ops = { + .assert = sky1_audss_reset_assert, + .deassert = sky1_audss_reset_deassert, +}; + +static int sky1_audss_reset_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct sky1_audss_reset *priv; + struct device *dev = &adev->dev; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = dev_get_regmap(dev->parent, NULL); + if (!priv->regmap) + return dev_err_probe(dev, -ENODEV, "failed to get parent regmap\n"); + + priv->map = sky1_audss_reset_map; + priv->rcdev.owner = THIS_MODULE; + priv->rcdev.nr_resets = ARRAY_SIZE(sky1_audss_reset_map); + priv->rcdev.ops = &sky1_audss_reset_ops; + priv->rcdev.of_node = dev->of_node; + priv->rcdev.dev = dev; + + return devm_reset_controller_register(dev, &priv->rcdev); +} + +static const struct auxiliary_device_id sky1_audss_reset_ids[] = { + { .name = "clk_sky1_audss.reset" }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, sky1_audss_reset_ids); + +static struct auxiliary_driver sky1_audss_reset_driver = { + .probe = sky1_audss_reset_probe, + .id_table = sky1_audss_reset_ids, +}; +module_auxiliary_driver(sky1_audss_reset_driver); + +MODULE_AUTHOR("Joakim Zhang "); +MODULE_DESCRIPTION("Cix Sky1 Audio Subsystem reset driver"); +MODULE_LICENSE("GPL"); diff --git a/include/dt-bindings/clock/aspeed,ast2700-scu.h b/include/dt-bindings/clock/aspeed,ast2700-scu.h index bacf712e8e04..138f78ce5f07 100644 --- a/include/dt-bindings/clock/aspeed,ast2700-scu.h +++ b/include/dt-bindings/clock/aspeed,ast2700-scu.h @@ -163,5 +163,7 @@ #define SCU1_CLK_GATE_PORTDUSB2CLK 85 #define SCU1_CLK_GATE_LTPI1TXCLK 86 #define SCU1_CLK_I3C 87 +#define SCU1_CLK_HPLL_DIV4 88 +#define SCU1_CLK_PECI 89 #endif diff --git a/include/dt-bindings/clock/cix,sky1-audss-cru.h b/include/dt-bindings/clock/cix,sky1-audss-cru.h new file mode 100644 index 000000000000..8c58ef8bf682 --- /dev/null +++ b/include/dt-bindings/clock/cix,sky1-audss-cru.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright 2026 Cix Technology Group Co., Ltd. + */ + +#ifndef _DT_BINDINGS_CLOCK_CIX_SKY1_AUDSS_CRU_H +#define _DT_BINDINGS_CLOCK_CIX_SKY1_AUDSS_CRU_H + +#define CLK_AUD_CLK4_DIV2 0 +#define CLK_AUD_CLK4_DIV4 1 +#define CLK_AUD_CLK5_DIV2 2 + +#define CLK_DSP_CLK 3 +#define CLK_DSP_BCLK 4 +#define CLK_DSP_PBCLK 5 + +#define CLK_SRAM_AXI 6 + +#define CLK_HDA_SYS 7 +#define CLK_HDA_HDA 8 + +#define CLK_DMAC_AXI 9 + +#define CLK_WDG_APB 10 +#define CLK_WDG_WDG 11 + +#define CLK_TIMER_APB 12 +#define CLK_TIMER_TIMER 13 + +#define CLK_MB_0_APB 14 /* MB0: ap->dsp */ +#define CLK_MB_1_APB 15 /* MB1: dsp->ap */ + +#define CLK_I2S0_APB 16 +#define CLK_I2S1_APB 17 +#define CLK_I2S2_APB 18 +#define CLK_I2S3_APB 19 +#define CLK_I2S4_APB 20 +#define CLK_I2S5_APB 21 +#define CLK_I2S6_APB 22 +#define CLK_I2S7_APB 23 +#define CLK_I2S8_APB 24 +#define CLK_I2S9_APB 25 +#define CLK_I2S0 26 +#define CLK_I2S1 27 +#define CLK_I2S2 28 +#define CLK_I2S3 29 +#define CLK_I2S4 30 +#define CLK_I2S5 31 +#define CLK_I2S6 32 +#define CLK_I2S7 33 +#define CLK_I2S8 34 +#define CLK_I2S9 35 + +#define CLK_MCLK0 36 +#define CLK_MCLK1 37 +#define CLK_MCLK2 38 +#define CLK_MCLK3 39 +#define CLK_MCLK4 40 + +#endif diff --git a/include/dt-bindings/clock/clock.h b/include/dt-bindings/clock/clock.h new file mode 100644 index 000000000000..155e2653a120 --- /dev/null +++ b/include/dt-bindings/clock/clock.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* + * Copyright 2025 NXP + */ + +#ifndef __DT_BINDINGS_CLOCK_H +#define __DT_BINDINGS_CLOCK_H + +#define CLK_SSC_NO_SPREAD 0 +#define CLK_SSC_CENTER_SPREAD 1 +#define CLK_SSC_UP_SPREAD 2 +#define CLK_SSC_DOWN_SPREAD 3 + +#endif /* __DT_BINDINGS_CLOCK_H */ diff --git a/include/dt-bindings/clock/eswin,eic7700-hspcrg.h b/include/dt-bindings/clock/eswin,eic7700-hspcrg.h new file mode 100644 index 000000000000..1d1ff15c1154 --- /dev/null +++ b/include/dt-bindings/clock/eswin,eic7700-hspcrg.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * Device Tree binding constants for EIC7700 HSP clock controller. + * + * Authors: Xuyang Dong + */ + +#ifndef _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_CLOCK_H_ +#define _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_CLOCK_H_ + +#define EIC7700_HSP_CLK_FAC_CFG_DIV2 0 +#define EIC7700_HSP_CLK_FAC_CFG_DIV4 1 +#define EIC7700_HSP_CLK_FAC_MMC_DIV10 2 +#define EIC7700_HSP_CLK_MUX_EMMC_3MUX1 3 +#define EIC7700_HSP_CLK_MUX_SD0_3MUX1 4 +#define EIC7700_HSP_CLK_MUX_SD1_3MUX1 5 +#define EIC7700_HSP_CLK_MUX_EMMC_CQE_2MUX1 6 +#define EIC7700_HSP_CLK_MUX_SD0_CQE_2MUX1 7 +#define EIC7700_HSP_CLK_MUX_SD1_CQE_2MUX1 8 +#define EIC7700_HSP_CLK_GATE_MSHC0_TMR 9 +#define EIC7700_HSP_CLK_GATE_EMMC 10 +#define EIC7700_HSP_CLK_GATE_MSHC1_TMR 11 +#define EIC7700_HSP_CLK_GATE_SD0 12 +#define EIC7700_HSP_CLK_GATE_MSHC2_TMR 13 +#define EIC7700_HSP_CLK_GATE_SD1 14 +#define EIC7700_HSP_CLK_GATE_USB0 15 +#define EIC7700_HSP_CLK_GATE_USB1 16 +#define EIC7700_HSP_CLK_GATE_SATA 17 + +#endif /* _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_CLOCK_H_ */ diff --git a/include/dt-bindings/clock/mt8173-clk.h b/include/dt-bindings/clock/mt8173-clk.h index 3d00c98b9654..89e982f771db 100644 --- a/include/dt-bindings/clock/mt8173-clk.h +++ b/include/dt-bindings/clock/mt8173-clk.h @@ -243,6 +243,13 @@ #define CLK_IMG_FD 7 #define CLK_IMG_NR_CLK 8 +/* MFG_SYS */ + +#define CLK_MFG_AXI 0 +#define CLK_MFG_MEM 1 +#define CLK_MFG_G3D 2 +#define CLK_MFG_26M 3 + /* MM_SYS */ #define CLK_MM_SMI_COMMON 1 diff --git a/include/dt-bindings/clock/ultrarisc,dp1000-clk.h b/include/dt-bindings/clock/ultrarisc,dp1000-clk.h new file mode 100644 index 000000000000..751125f99965 --- /dev/null +++ b/include/dt-bindings/clock/ultrarisc,dp1000-clk.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +#ifndef _DT_BINDINGS_CLOCK_ULTRARISC_DP1000_CLK_H +#define _DT_BINDINGS_CLOCK_ULTRARISC_DP1000_CLK_H + +#define DP1000_CLK_SYSPLL 0 +#define DP1000_CLK_SYSPLL_DIV2 1 +#define DP1000_CLK_SUBSYS 2 +#define DP1000_CLK_GMAC 3 +#define DP1000_CLK_UART_ROOT 4 +#define DP1000_CLK_I2C_ROOT 5 +#define DP1000_CLK_SPI_ROOT 6 +#define DP1000_CLK_PCIE_DBI 7 +#define DP1000_CLK_PCIEX4_CORE 8 +#define DP1000_CLK_PCIEX16_CORE 9 +#define DP1000_CLK_PCIE_AUX 10 +#define DP1000_CLK_UART0 11 +#define DP1000_CLK_UART1 12 +#define DP1000_CLK_UART2 13 +#define DP1000_CLK_UART3 14 +#define DP1000_CLK_I2C0 15 +#define DP1000_CLK_I2C1 16 +#define DP1000_CLK_I2C2 17 +#define DP1000_CLK_I2C3 18 +#define DP1000_CLK_SPI0 19 +#define DP1000_CLK_SPI1 20 + +#endif /* _DT_BINDINGS_CLOCK_ULTRARISC_DP1000_CLK_H */ diff --git a/include/dt-bindings/reset/airoha,en7581-reset.h b/include/dt-bindings/reset/airoha,en7581-reset.h index 6544a1790b83..25e75534daa9 100644 --- a/include/dt-bindings/reset/airoha,en7581-reset.h +++ b/include/dt-bindings/reset/airoha,en7581-reset.h @@ -62,5 +62,9 @@ #define EN7581_CPU_TIMER_RST 50 #define EN7581_PCIE_HB_RST 51 #define EN7581_XPON_MAC_RST 52 +/* RST_PCIC */ +#define EN7581_PCIC_PERSTOUT0_RST 53 +#define EN7581_PCIC_PERSTOUT1_RST 54 +#define EN7581_PCIC_PERSTOUT2_RST 55 #endif /* __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7581_H_ */ diff --git a/include/dt-bindings/reset/cix,sky1-audss-cru.h b/include/dt-bindings/reset/cix,sky1-audss-cru.h new file mode 100644 index 000000000000..55e9f3797b30 --- /dev/null +++ b/include/dt-bindings/reset/cix,sky1-audss-cru.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright 2026 Cix Technology Group Co., Ltd. + */ +#ifndef DT_BINDINGS_RESET_CIX_SKY1_AUDSS_CRU_H +#define DT_BINDINGS_RESET_CIX_SKY1_AUDSS_CRU_H + +#define AUDSS_I2S0_SW_RST 0 +#define AUDSS_I2S1_SW_RST 1 +#define AUDSS_I2S2_SW_RST 2 +#define AUDSS_I2S3_SW_RST 3 +#define AUDSS_I2S4_SW_RST 4 +#define AUDSS_I2S5_SW_RST 5 +#define AUDSS_I2S6_SW_RST 6 +#define AUDSS_I2S7_SW_RST 7 +#define AUDSS_I2S8_SW_RST 8 +#define AUDSS_I2S9_SW_RST 9 +#define AUDSS_WDT_SW_RST 10 +#define AUDSS_TIMER_SW_RST 11 +#define AUDSS_MB0_SW_RST 12 +#define AUDSS_MB1_SW_RST 13 +#define AUDSS_HDA_SW_RST 14 +#define AUDSS_DMAC_SW_RST 15 + +#endif diff --git a/include/dt-bindings/reset/eswin,eic7700-hspcrg.h b/include/dt-bindings/reset/eswin,eic7700-hspcrg.h new file mode 100644 index 000000000000..413fcd08c701 --- /dev/null +++ b/include/dt-bindings/reset/eswin,eic7700-hspcrg.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd.. + * All rights reserved. + * + * Device Tree binding constants for EIC7700 HSP reset controller. + * + * Authors: Xuyang Dong + */ + +#ifndef _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_RESET_H_ +#define _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_RESET_H_ + +#define EIC7700_HSP_RST_SATA_P0 0 +#define EIC7700_HSP_RST_SATA_PHY 1 +#define EIC7700_HSP_RST_USB0 2 +#define EIC7700_HSP_RST_USB1 3 +#define EIC7700_HSP_RST_USB0_PHY 4 +#define EIC7700_HSP_RST_USB1_PHY 5 + +#endif /* _DT_BINDINGS_ESWIN_EIC7700_HSPCRG_RESET_H_ */ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index aba71f5dcc21..dc8e161f9c2c 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -6,31 +6,44 @@ #ifndef __LINUX_CLK_PROVIDER_H #define __LINUX_CLK_PROVIDER_H +#include #include #include -/* - * flags used across common struct clk. these flags should only affect the - * top-level framework. custom flags for dealing with hardware specifics - * belong in struct clk_foo +/** + * DOC: clk framework flags * - * Please update clk_flags[] in drivers/clk/clk.c when making changes here! + * Flags used across common struct clk. These flags should only affect the + * top-level framework. Custom flags for dealing with hardware specifics + * belong in struct clk_foo. + * + * * CLK_SET_RATE_GATE - must be gated across rate change + * * CLK_SET_PARENT_GATE - must be gated across re-parent + * * CLK_SET_RATE_PARENT - propagate rate change up one level + * * CLK_IGNORE_UNUSED - do not gate even if unused + * * CLK_GET_RATE_NOCACHE - do not use the cached clk rate + * * CLK_SET_RATE_NO_REPARENT - don't re-parent on rate change + * * CLK_GET_ACCURACY_NOCACHE - do not use the cached clk accuracy + * * CLK_RECALC_NEW_RATES - recalc rates after notifications + * * CLK_SET_RATE_UNGATE - clock needs to run to set rate + * * CLK_IS_CRITICAL - do not gate, ever + * * CLK_OPS_PARENT_ENABLE - parents need enable during gate/ungate, set rate and re-parent + * * CLK_DUTY_CYCLE_PARENT - duty cycle call may be forwarded to the parent clock */ -#define CLK_SET_RATE_GATE BIT(0) /* must be gated across rate change */ -#define CLK_SET_PARENT_GATE BIT(1) /* must be gated across re-parent */ -#define CLK_SET_RATE_PARENT BIT(2) /* propagate rate change up one level */ -#define CLK_IGNORE_UNUSED BIT(3) /* do not gate even if unused */ +/* Please update clk_flags[] in drivers/clk/clk.c when making changes here! */ +#define CLK_SET_RATE_GATE BIT(0) +#define CLK_SET_PARENT_GATE BIT(1) +#define CLK_SET_RATE_PARENT BIT(2) +#define CLK_IGNORE_UNUSED BIT(3) /* unused */ /* unused */ -#define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ -#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ -#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */ -#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */ -#define CLK_SET_RATE_UNGATE BIT(10) /* clock needs to run to set rate */ -#define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */ -/* parents need enable during gate/ungate, set rate and re-parent */ +#define CLK_GET_RATE_NOCACHE BIT(6) +#define CLK_SET_RATE_NO_REPARENT BIT(7) +#define CLK_GET_ACCURACY_NOCACHE BIT(8) +#define CLK_RECALC_NEW_RATES BIT(9) +#define CLK_SET_RATE_UNGATE BIT(10) +#define CLK_IS_CRITICAL BIT(11) #define CLK_OPS_PARENT_ENABLE BIT(12) -/* duty cycle call may be forwarded to the parent clock */ #define CLK_DUTY_CYCLE_PARENT BIT(13) struct clk; @@ -84,6 +97,26 @@ struct clk_duty { unsigned int den; }; +enum clk_ssc_method { + CLK_SPREAD_NO = CLK_SSC_NO_SPREAD, + CLK_SPREAD_CENTER = CLK_SSC_CENTER_SPREAD, + CLK_SPREAD_UP = CLK_SSC_UP_SPREAD, + CLK_SPREAD_DOWN = CLK_SSC_DOWN_SPREAD, +}; + +/** + * struct clk_spread_spectrum - Structure encoding spread spectrum of a clock + * + * @modfreq_hz: Modulation frequency + * @spread_bp: Modulation percent in permyriad + * @method: Modulation method + */ +struct clk_spread_spectrum { + u32 modfreq_hz; + u32 spread_bp; + enum clk_ssc_method method; +}; + /** * struct clk_ops - Callback operations for hardware clocks; these are to * be provided by the clock implementation, and will be called by drivers @@ -174,6 +207,12 @@ struct clk_duty { * separately via calls to .set_parent and .set_rate. * Returns 0 on success, -EERROR otherwise. * + * @set_spread_spectrum: Optional callback used to configure the spread + * spectrum modulation frequency, percentage, and method + * to reduce EMI by spreading the clock frequency over a + * wider range. + * Returns 0 on success, -EERROR otherwise. + * * @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy * is expressed in ppb (parts per billion). The parent accuracy is * an input parameter. @@ -249,6 +288,8 @@ struct clk_ops { int (*set_rate_and_parent)(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index); + int (*set_spread_spectrum)(struct clk_hw *hw, + const struct clk_spread_spectrum *ss_conf); unsigned long (*recalc_accuracy)(struct clk_hw *hw, unsigned long parent_accuracy); int (*get_phase)(struct clk_hw *hw); @@ -1463,11 +1504,14 @@ int clk_mux_determine_rate_flags(struct clk_hw *hw, unsigned long flags); int clk_hw_determine_rate_no_reparent(struct clk_hw *hw, struct clk_rate_request *req); +int clk_determine_rate_noop(struct clk_hw *hw, struct clk_rate_request *req); void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent); void clk_hw_get_rate_range(struct clk_hw *hw, unsigned long *min_rate, unsigned long *max_rate); void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate, unsigned long max_rate); +int clk_hw_set_spread_spectrum(struct clk_hw *hw, + const struct clk_spread_spectrum *ss_conf); static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src) { diff --git a/include/linux/clk.h b/include/linux/clk.h index 998ba3f261da..db9cf184fd99 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -502,6 +502,22 @@ int __must_check devm_clk_bulk_get(struct device *dev, int num_clks, */ int __must_check devm_clk_bulk_get_optional(struct device *dev, int num_clks, struct clk_bulk_data *clks); +/** + * devm_clk_bulk_get_enable - Get and enable bulk clocks (managed) + * @dev: device for clock "consumer" + * @num_clks: the number of clk_bulk_data + * @clks: pointer to the clk_bulk_data table of consumer + * + * Behaves the same as devm_clk_bulk_get() but also prepares and enables the + * clocks in one operation with management. The clks will automatically be + * disabled, unprepared and freed when the device is unbound. + * + * Return: 0 if all clocks specified in clk_bulk_data table are obtained and + * enabled successfully. Otherwise returns valid IS_ERR() condition containing + * errno. + */ +int __must_check devm_clk_bulk_get_enable(struct device *dev, int num_clks, + struct clk_bulk_data *clks); /** * devm_clk_bulk_get_optional_enable - Get and enable optional bulk clocks (managed) * @dev: device for clock "consumer" @@ -951,8 +967,8 @@ int clk_set_parent(struct clk *clk, struct clk *parent); * clk_get_parent - get the parent clock source for this clock * @clk: clock source * - * Returns struct clk corresponding to parent clock source, or - * valid IS_ERR() condition containing errno. + * Returns struct clk corresponding to parent clock source, or NULL + * if clk is NULL. */ struct clk *clk_get_parent(struct clk *clk); @@ -1052,6 +1068,13 @@ static inline int __must_check devm_clk_bulk_get_optional(struct device *dev, return 0; } +static inline int __must_check devm_clk_bulk_get_enable(struct device *dev, + int num_clks, + struct clk_bulk_data *clks) +{ + return 0; +} + static inline int __must_check devm_clk_bulk_get_optional_enable(struct device *dev, int num_clks, struct clk_bulk_data *clks)