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
  ...
This commit is contained in:
Stephen Boyd
2026-08-13 17:42:25 -07:00
56 changed files with 3063 additions and 151 deletions

View File

@@ -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>;
};

View File

@@ -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

View File

@@ -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>;
};
};

View File

@@ -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>;
};

View File

@@ -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()

View File

@@ -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

View File

@@ -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 @@ mbox_pm2ap: mailbox@65a0080 {
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>;

View File

@@ -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"

View File

@@ -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/

View File

@@ -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),

16
drivers/clk/cix/Kconfig Normal file
View File

@@ -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

3
drivers/clk/cix/Makefile Normal file
View File

@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CLK_SKY1_AUDSS) += clk-sky1-audss.o

File diff suppressed because it is too large Load Diff

View File

@@ -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);

View File

@@ -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)

View File

@@ -222,6 +222,13 @@ static int __devm_clk_bulk_get_enable(struct device *dev, int num_clks,
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)
{

View File

@@ -57,6 +57,7 @@
#define REG_RST_CTRL2 0x830
#define REG_RST_CTRL1 0x834
#define REG_PCIE_HB_RST BIT(29)
#define EN751221_REG_RST_DMT 0x84
#define EN751221_REG_RST_USB 0xec
@@ -338,6 +339,7 @@ static const struct en_clk_desc en7581_base_clks[] = {
static const u16 en7581_rst_ofs[] = {
REG_RST_CTRL2,
REG_RST_CTRL1,
REG_NP_SCU_PCIC,
};
static const u16 en751221_rst_ofs[] = {
@@ -450,6 +452,11 @@ static const u16 en7581_rst_map[] = {
[EN7581_CPU_TIMER_RST] = RST_NR_PER_BANK + 28,
[EN7581_PCIE_HB_RST] = RST_NR_PER_BANK + 29,
[EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31,
/* RST_PCIC */
[EN7581_PCIC_PERSTOUT0_RST] = 2 * RST_NR_PER_BANK + 29,
[EN7581_PCIC_PERSTOUT1_RST] = 2 * RST_NR_PER_BANK + 26,
[EN7581_PCIC_PERSTOUT2_RST] = 2 * RST_NR_PER_BANK + 16,
};
static const u16 en751221_rst_map[] = {
@@ -635,9 +642,7 @@ static int en7581_pci_enable(struct clk_hw *hw)
void __iomem *np_base = cg->base;
u32 val, mask;
mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 |
REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 |
REG_PCI_CONTROL_PERSTOUT;
mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
val = readl(np_base + REG_PCI_CONTROL);
writel(val | mask, np_base + REG_PCI_CONTROL);
@@ -650,9 +655,7 @@ static void en7581_pci_disable(struct clk_hw *hw)
void __iomem *np_base = cg->base;
u32 val, mask;
mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1 |
REG_PCI_CONTROL_PERSTOUT1 | REG_PCI_CONTROL_PERSTOUT2 |
REG_PCI_CONTROL_PERSTOUT;
mask = REG_PCI_CONTROL_REFCLK_EN0 | REG_PCI_CONTROL_REFCLK_EN1;
val = readl(np_base + REG_PCI_CONTROL);
writel(val & ~mask, np_base + REG_PCI_CONTROL);
usleep_range(1000, 2000);
@@ -754,11 +757,17 @@ static int en7523_reset_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev);
void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK];
u32 offset = rst_data->bank_ofs[id / RST_NR_PER_BANK];
void __iomem *addr = rst_data->base + offset;
bool inverted = false;
u32 val;
/* For PCIC reset logic is inverted, 0:assert 1:deassert */
if (offset == REG_NP_SCU_PCIC)
inverted = true;
val = readl(addr);
if (assert)
if (assert ^ inverted)
val |= BIT(id % RST_NR_PER_BANK);
else
val &= ~BIT(id % RST_NR_PER_BANK);
@@ -783,9 +792,17 @@ static int en7523_reset_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct en_rst_data *rst_data = container_of(rcdev, struct en_rst_data, rcdev);
void __iomem *addr = rst_data->base + rst_data->bank_ofs[id / RST_NR_PER_BANK];
u32 offset = rst_data->bank_ofs[id / RST_NR_PER_BANK];
void __iomem *addr = rst_data->base + offset;
bool inverted = false;
u32 val;
return !!(readl(addr) & BIT(id % RST_NR_PER_BANK));
/* For PCIC reset logic is inverted, 0:assert 1:deassert */
if (offset == REG_NP_SCU_PCIC)
inverted = true;
val = readl(addr) & BIT(id % RST_NR_PER_BANK);
return inverted ? !val : !!val;
}
static int en7523_reset_xlate(struct reset_controller_dev *rcdev,
@@ -853,6 +870,12 @@ static int en7581_clk_hw_init(struct platform_device *pdev,
val = readl(base + REG_NP_SCU_PCIC);
writel(val | 3, base + REG_NP_SCU_PCIC);
val = readl(base + REG_RST_CTRL1);
val |= REG_PCIE_HB_RST;
writel(val, base + REG_RST_CTRL1);
val &= ~REG_PCIE_HB_RST;
writel(val, base + REG_RST_CTRL1);
return en7581_reset_register(&pdev->dev, base, en7581_rst_map,
ARRAY_SIZE(en7581_rst_map),
en7581_rst_ofs);

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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),

View File

@@ -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");

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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
*/

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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.

View File

@@ -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

View File

@@ -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");

View File

@@ -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");

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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");

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 */

View File

@@ -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_ */

View File

@@ -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

View File

@@ -502,6 +502,22 @@ int __must_check devm_clk_bulk_get(struct device *dev, int num_clks,
*/
int __must_check devm_clk_bulk_get_optional(struct device *dev, int num_clks,
struct clk_bulk_data *clks);
/**
* devm_clk_bulk_get_enable - Get and enable bulk clocks (managed)
* @dev: device for clock "consumer"
* @num_clks: the number of clk_bulk_data
* @clks: pointer to the clk_bulk_data table of consumer
*
* Behaves the same as devm_clk_bulk_get() but also prepares and enables the
* clocks in one operation with management. The clks will automatically be
* disabled, unprepared and freed when the device is unbound.
*
* Return: 0 if all clocks specified in clk_bulk_data table are obtained and
* enabled successfully. Otherwise returns valid IS_ERR() condition containing
* errno.
*/
int __must_check devm_clk_bulk_get_enable(struct device *dev, int num_clks,
struct clk_bulk_data *clks);
/**
* devm_clk_bulk_get_optional_enable - Get and enable optional bulk clocks (managed)
* @dev: device for clock "consumer"
@@ -951,8 +967,8 @@ int clk_set_parent(struct clk *clk, struct clk *parent);
* clk_get_parent - get the parent clock source for this clock
* @clk: clock source
*
* Returns struct clk corresponding to parent clock source, or
* valid IS_ERR() condition containing errno.
* Returns struct clk corresponding to parent clock source, or NULL
* if clk is NULL.
*/
struct clk *clk_get_parent(struct clk *clk);
@@ -1052,6 +1068,13 @@ static inline int __must_check devm_clk_bulk_get_optional(struct device *dev,
return 0;
}
static inline int __must_check devm_clk_bulk_get_enable(struct device *dev,
int num_clks,
struct clk_bulk_data *clks)
{
return 0;
}
static inline int __must_check devm_clk_bulk_get_optional_enable(struct device *dev,
int num_clks,
struct clk_bulk_data *clks)