summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Boyd <sboyd@kernel.org>2026-08-13 17:42:25 -0700
committerStephen Boyd <sboyd@kernel.org>2026-08-17 09:28:08 -0700
commitdf822f2e36cf2c2864d166d90cf002df4d8689e5 (patch)
tree7245d5de17dca83a30f98ff79a83879700abfbb7
parent804b10deb0e8872716416b38af0e4e60a038f0fd (diff)
parentcf561283254dd9f55805800237e6f82f08d91497 (diff)
downloadlinux-df822f2e36cf2c2864d166d90cf002df4d8689e5.tar.gz
linux-df822f2e36cf2c2864d166d90cf002df4d8689e5.zip
Merge tag 'clk-misc-round-two-for-v7.3' of ssh://github.com/masneyb/linux into clk-pile
Pull clk patches from Brian Masney: - New clock controller drivers for the Cix Sky1 audio subsystem (AUDSS), UltraRISC DP1000, and MediaTek MT8173 MFG_TOP, along with their devicetree bindings. Si549 support was added to the existing si544 driver. - New clock and reset support for the Aspeed AST2700 PECI controller and Airoha EN7523 PCIe PERSTOUT reset lines. - The clk core gains devm_clk_bulk_get_enable() as the mandatory counterpart to the existing optional variant, and exports devm_clk_hw_register_composite_pdata() for modular drivers. - Tegra gets a proper EMC clock implementation for Tegra114, 48MHz pll_p_out1 support needed for UEFI on Surface2, and the Xilinx clocking-wizard gains PLL charge pump/lock parameter programming during dynamic reconfiguration. - Bug fixes for a NULL pointer dereference from uninitialized clk_init_data in the eswin driver, an IO remap leak in the MediaTek pllfh error path, inverted gate control for MT8135 devapc_ck, a missing OF node put in tegra124-emc on registration failure, a prepare reference leak in the palmas driver, unregistered PLLs on MT6735 probe failure, a missing kasprintf NULL check in pmc_atom, PCIe warm boot instability in the Airoha EN7523 driver, and a clocking-wizard clock difference detection bug. - Various cleanups across tegra, st, and mvebu providers to stop misusing the consumer clock API, along with other minor fixes and improvements. Signed-off-by: Brian Masney <bmasney@redhat.com> * tag 'clk-misc-round-two-for-v7.3' of ssh://github.com/masneyb/linux: (45 commits) clk: tegra: set up proper EMC clock implementation for Tegra114 clk: clocking-wizard: remove 20kHz restriction clk: clocking-wizard: optimize clock search clk: clocking-wizard: fix clock difference detection clk: mediatek: mt8135: Fix inverted gate control for devapc_ck clk: clocking-wizard: Program PLL CP/RES and lock parameters on reconfig clk: tegra: support 48MHz clock for pll_p_out1 clk: aspeed: add AST2700 PECI clock dt-bindings: clock: ast2700: add PECI clock clk: mediatek: Add mt8173-mfgtop driver dt-bindings: clock: mediatek: Add mt8173 mfgtop clk: tegra: clean-up simple provider misuse of the consumer API clk: st: clean-up simple provider misuse of the consumer API clk: mvebu: clean-up simple provider misuse of the consumer API clk: remove conditional return with no effect clk: Add devm_clk_bulk_get_enable() clk: en7523: add support for dedicated PCIe PERSTOUT reset dt-bindings: clock: airoha: Add additional reset for PCIe PERSTOUT arm64: dts: cix: sky1: add audss cru reset: cix: add sky1 audss auxiliary reset driver ...
-rw-r--r--Documentation/devicetree/bindings/clock/mediatek,mt8173-mfgtop.yaml70
-rw-r--r--Documentation/devicetree/bindings/clock/silabs,si544.yaml10
-rw-r--r--Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml60
-rw-r--r--Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml92
-rw-r--r--Documentation/driver-api/driver-model/devres.rst1
-rw-r--r--MAINTAINERS8
-rw-r--r--arch/arm64/boot/dts/cix/sky1.dtsi18
-rw-r--r--drivers/clk/Kconfig10
-rw-r--r--drivers/clk/Makefile4
-rw-r--r--drivers/clk/aspeed/clk-ast2700.c10
-rw-r--r--drivers/clk/cix/Kconfig16
-rw-r--r--drivers/clk/cix/Makefile3
-rw-r--r--drivers/clk/cix/clk-sky1-audss.c1206
-rw-r--r--drivers/clk/clk-composite.c1
-rw-r--r--drivers/clk/clk-cs2000-cp.c6
-rw-r--r--drivers/clk/clk-devres.c7
-rw-r--r--drivers/clk/clk-en7523.c43
-rw-r--r--drivers/clk/clk-lmk04832.c12
-rw-r--r--drivers/clk/clk-palmas.c24
-rw-r--r--drivers/clk/clk-si544.c82
-rw-r--r--drivers/clk/eswin/clk.c4
-rw-r--r--drivers/clk/hisilicon/clk-hi3660-stub.c10
-rw-r--r--drivers/clk/mediatek/Kconfig10
-rw-r--r--drivers/clk/mediatek/Makefile1
-rw-r--r--drivers/clk/mediatek/clk-mt6735-apmixedsys.c5
-rw-r--r--drivers/clk/mediatek/clk-mt6735-vdecsys.c1
-rw-r--r--drivers/clk/mediatek/clk-mt6735-vencsys.c1
-rw-r--r--drivers/clk/mediatek/clk-mt8135.c5
-rw-r--r--drivers/clk/mediatek/clk-mt8173-mfgtop.c242
-rw-r--r--drivers/clk/mediatek/clk-mt8196-vlpckgen.c1
-rw-r--r--drivers/clk/mediatek/clk-pllfh.c98
-rw-r--r--drivers/clk/mmp/clk-pxa1908-apbc.c2
-rw-r--r--drivers/clk/mmp/clk-pxa1908-apbcp.c2
-rw-r--r--drivers/clk/mvebu/clk-cpu.c2
-rw-r--r--drivers/clk/st/clkgen-pll.c20
-rw-r--r--drivers/clk/tegra/clk-pll.c1
-rw-r--r--drivers/clk/tegra/clk-tegra114.c39
-rw-r--r--drivers/clk/tegra/clk-tegra124-emc.c1
-rw-r--r--drivers/clk/tegra/clk-tegra210.c2
-rw-r--r--drivers/clk/ultrarisc/Kconfig18
-rw-r--r--drivers/clk/ultrarisc/Makefile4
-rw-r--r--drivers/clk/ultrarisc/clk-dp1000.c154
-rw-r--r--drivers/clk/ultrarisc/clk-ultrarisc.c399
-rw-r--r--drivers/clk/ultrarisc/clk-ultrarisc.h71
-rw-r--r--drivers/clk/x86/clk-pmc-atom.c3
-rw-r--r--drivers/clk/xilinx/clk-xlnx-clock-wizard.c142
-rw-r--r--drivers/reset/Kconfig13
-rw-r--r--drivers/reset/Makefile1
-rw-r--r--drivers/reset/reset-sky1-audss.c127
-rw-r--r--include/dt-bindings/clock/aspeed,ast2700-scu.h2
-rw-r--r--include/dt-bindings/clock/cix,sky1-audss-cru.h60
-rw-r--r--include/dt-bindings/clock/mt8173-clk.h7
-rw-r--r--include/dt-bindings/clock/ultrarisc,dp1000-clk.h27
-rw-r--r--include/dt-bindings/reset/airoha,en7581-reset.h4
-rw-r--r--include/dt-bindings/reset/cix,sky1-audss-cru.h25
-rw-r--r--include/linux/clk.h27
56 files changed, 3063 insertions, 151 deletions
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 <angelogioacchino.delregno@collabora.com>
+
+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 <dt-bindings/clock/mt8173-clk.h>
+ #include <dt-bindings/power/mt8173-power.h>
+
+ 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/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 <mike.looijmans@topic.nl>
@@ -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/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 <wangjia@ultrarisc.com>
+
+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 <dt-bindings/clock/ultrarisc,dp1000-clk.h>
+
+ 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/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 <joakim.zhang@cixtech.com>
+
+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/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 6fd042984253..d15f20dbc92d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27671,6 +27671,14 @@ S: Maintained
F: drivers/usb/common/ulpi.c
F: include/linux/ulpi/
+ULTRARISC DP1000 CLOCK DRIVER
+M: Jia Wang <wangjia@ultrarisc.com>
+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 <wangjia@ultrarisc.com>
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 <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/cix,sky1.h>
+#include <dt-bindings/clock/cix,sky1-audss-cru.h>
+#include <dt-bindings/reset/cix,sky1-system-control.h>
+#include <dt-bindings/reset/cix,sky1-s5-system-control.h>
+#include <dt-bindings/reset/cix,sky1-audss-cru.h>
#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/Kconfig b/drivers/clk/Kconfig
index 3d94c7611356..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"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f9f669dff8a0..d61078cda465 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -131,6 +131,7 @@ 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_COMMON_CLK_HISI) += hisilicon/
@@ -163,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/
@@ -171,6 +172,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-y += tenstorrent/
obj-$(CONFIG_ARCH_THEAD) += 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/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 <linux/auxiliary_bus.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <dt-bindings/clock/cix,sky1-audss-cru.h>
+
+#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);
+ 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, &reg);
+
+ 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, &reg);
+
+ /* 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 <joakim.zhang@cixtech.com>");
+MODULE_DESCRIPTION("Cixtech Sky1 Audio Subsystem Clock Controller Driver");
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-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c
index fd277dcefad7..e415bdff4d97 100644
--- a/drivers/clk/clk-cs2000-cp.c
+++ b/drivers/clk/clk-cs2000-cp.c
@@ -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..49d4782991eb 100644
--- a/drivers/clk/clk-devres.c
+++ b/drivers/clk/clk-devres.c
@@ -222,6 +222,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-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;
+
+ /* For PCIC reset logic is inverted, 0:assert 1:deassert */
+ if (offset == REG_NP_SCU_PCIC)
+ inverted = true;
- return !!(readl(addr) & BIT(id % RST_NR_PER_BANK));
+ 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-lmk04832.c b/drivers/clk/clk-lmk04832.c
index c644d31b223e..f9a0684d2628 100644
--- a/drivers/clk/clk-lmk04832.c
+++ b/drivers/clk/clk-lmk04832.c
@@ -815,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,
@@ -836,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)
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-si544.c b/drivers/clk/clk-si544.c
index f45f3f73aa82..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 <mike.looijmans@topic.nl>
*/
@@ -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[] = {
- { .name = "si544a", .driver_data = 1500000000 },
- { .name = "si544b", .driver_data = 800000000 },
- { .name = "si544c", .driver_data = 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/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/clk-hi3660-stub.c b/drivers/clk/hisilicon/clk-hi3660-stub.c
index 0cb1c0538eae..d28118f4cfb8 100644
--- a/drivers/clk/hisilicon/clk-hi3660-stub.c
+++ b/drivers/clk/hisilicon/clk-hi3660-stub.c
@@ -114,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;
@@ -129,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/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index fd8440122ec2..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
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 <wenst@chromium.org>
+ *
+ * Based on driver in downstream ChromeOS v5.15 kernel.
+ *
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Chiawen Lee <chiawen.lee@mediatek.com>
+ */
+
+#include <dt-bindings/clock/mt8173-clk.h>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#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/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/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, &params, &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, &params, &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, &params, &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/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/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 <linux/module.h>
+
+#include <dt-bindings/clock/ultrarisc,dp1000-clk.h>
+
+#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 <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#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 = &divider_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,
+ &divider->divider.hw,
+ &ultrarisc_divider_ops,
+ &divider->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 <linux/clk-provider.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+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/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 dbef983eb425..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);
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index f63e89ed6a4e..3168ab1eea88 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -311,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 a75af831ef58..45ad17062462 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -40,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-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 <dt-bindings/reset/cix,sky1-audss-cru.h>
+
+#include <linux/auxiliary_bus.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#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 <joakim.zhang@cixtech.com>");
+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/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/linux/clk.h b/include/linux/clk.h
index 998ba3f261da..db9cf184fd99 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -503,6 +503,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"
* @num_clks: the number of clk_bulk_data
@@ -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)