mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-27 22:13:55 -04:00
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:
@@ -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>;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
};
|
||||
@@ -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>;
|
||||
};
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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
16
drivers/clk/cix/Kconfig
Normal 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
3
drivers/clk/cix/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_CLK_SKY1_AUDSS) += clk-sky1-audss.o
|
||||
1206
drivers/clk/cix/clk-sky1-audss.c
Normal file
1206
drivers/clk/cix/clk-sky1-audss.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
242
drivers/clk/mediatek/clk-mt8173-mfgtop.c
Normal file
242
drivers/clk/mediatek/clk-mt8173-mfgtop.c
Normal 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");
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -260,7 +260,7 @@ static int __clkgen_pll_enable(struct clk_hw *hw)
|
||||
if (pll->data->switch2pll_en)
|
||||
CLKGEN_WRITE(pll, switch2pll, 0);
|
||||
|
||||
pr_debug("%s:%s enabled\n", __clk_get_name(hw->clk), __func__);
|
||||
pr_debug("%s:%s enabled\n", clk_hw_get_name(hw), __func__);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -295,7 +295,7 @@ static void __clkgen_pll_disable(struct clk_hw *hw)
|
||||
|
||||
CLKGEN_WRITE(pll, pdn_ctrl, 1);
|
||||
|
||||
pr_debug("%s:%s disabled\n", __clk_get_name(hw->clk), __func__);
|
||||
pr_debug("%s:%s disabled\n", clk_hw_get_name(hw), __func__);
|
||||
}
|
||||
|
||||
static void clkgen_pll_disable(struct clk_hw *hw)
|
||||
@@ -405,14 +405,14 @@ static int stm_pll3200c32_determine_rate(struct clk_hw *hw,
|
||||
&req->rate);
|
||||
else {
|
||||
pr_debug("%s: %s rate %ld Invalid\n", __func__,
|
||||
__clk_get_name(hw->clk), req->rate);
|
||||
clk_hw_get_name(hw), req->rate);
|
||||
req->rate = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_debug("%s: %s new rate %ld [ndiv=%u] [idf=%u]\n",
|
||||
__func__, __clk_get_name(hw->clk),
|
||||
__func__, clk_hw_get_name(hw),
|
||||
req->rate, (unsigned int)params.ndiv,
|
||||
(unsigned int)params.idf);
|
||||
|
||||
@@ -434,7 +434,7 @@ static int set_rate_stm_pll3200c32(struct clk_hw *hw, unsigned long rate,
|
||||
clk_pll3200c32_get_rate(parent_rate, ¶ms, &hwrate);
|
||||
|
||||
pr_debug("%s: %s new rate %ld [ndiv=0x%x] [idf=0x%x]\n",
|
||||
__func__, __clk_get_name(hw->clk),
|
||||
__func__, clk_hw_get_name(hw),
|
||||
hwrate, (unsigned int)params.ndiv,
|
||||
(unsigned int)params.idf);
|
||||
|
||||
@@ -547,7 +547,7 @@ static unsigned long recalc_stm_pll4600c28(struct clk_hw *hw,
|
||||
|
||||
clk_pll4600c28_get_rate(parent_rate, ¶ms, &rate);
|
||||
|
||||
pr_debug("%s:%s rate %lu\n", __clk_get_name(hw->clk), __func__, rate);
|
||||
pr_debug("%s:%s rate %lu\n", clk_hw_get_name(hw), __func__, rate);
|
||||
|
||||
return rate;
|
||||
}
|
||||
@@ -562,14 +562,14 @@ static int stm_pll4600c28_determine_rate(struct clk_hw *hw,
|
||||
&req->rate);
|
||||
} else {
|
||||
pr_debug("%s: %s rate %ld Invalid\n", __func__,
|
||||
__clk_get_name(hw->clk), req->rate);
|
||||
clk_hw_get_name(hw), req->rate);
|
||||
req->rate = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_debug("%s: %s new rate %ld [ndiv=%u] [idf=%u]\n",
|
||||
__func__, __clk_get_name(hw->clk),
|
||||
__func__, clk_hw_get_name(hw),
|
||||
req->rate, (unsigned int)params.ndiv,
|
||||
(unsigned int)params.idf);
|
||||
|
||||
@@ -591,12 +591,12 @@ static int set_rate_stm_pll4600c28(struct clk_hw *hw, unsigned long rate,
|
||||
clk_pll4600c28_get_rate(parent_rate, ¶ms, &hwrate);
|
||||
} else {
|
||||
pr_debug("%s: %s rate %ld Invalid\n", __func__,
|
||||
__clk_get_name(hw->clk), rate);
|
||||
clk_hw_get_name(hw), rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_debug("%s: %s new rate %ld [ndiv=0x%x] [idf=0x%x]\n",
|
||||
__func__, __clk_get_name(hw->clk),
|
||||
__func__, clk_hw_get_name(hw),
|
||||
hwrate, (unsigned int)params.ndiv,
|
||||
(unsigned int)params.idf);
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
18
drivers/clk/ultrarisc/Kconfig
Normal file
18
drivers/clk/ultrarisc/Kconfig
Normal 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.
|
||||
4
drivers/clk/ultrarisc/Makefile
Normal file
4
drivers/clk/ultrarisc/Makefile
Normal 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
|
||||
154
drivers/clk/ultrarisc/clk-dp1000.c
Normal file
154
drivers/clk/ultrarisc/clk-dp1000.c
Normal 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");
|
||||
399
drivers/clk/ultrarisc/clk-ultrarisc.c
Normal file
399
drivers/clk/ultrarisc/clk-ultrarisc.c
Normal 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 = ÷r_clk->divider;
|
||||
int value;
|
||||
u32 val;
|
||||
|
||||
value = divider_get_val(rate, parent_rate, divider->table,
|
||||
divider->width, divider->flags);
|
||||
if (value < 0)
|
||||
return value;
|
||||
|
||||
scoped_guard(spinlock_irqsave, divider->lock) {
|
||||
val = readl_relaxed(divider->reg);
|
||||
val &= ~(clk_div_mask(divider->width) << divider->shift);
|
||||
val |= value << divider->shift;
|
||||
writel_relaxed(val, divider->reg);
|
||||
|
||||
if (divider_clk->load_mask) {
|
||||
/*
|
||||
* Program the new divider field, then write 1 to the
|
||||
* load bit to trigger the update. The load bit is
|
||||
* write-triggered and reads back as 0 on this hardware.
|
||||
*/
|
||||
writel_relaxed(val | divider_clk->load_mask, divider->reg);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops ultrarisc_divider_ops = {
|
||||
.recalc_rate = ultrarisc_divider_recalc_rate,
|
||||
.determine_rate = ultrarisc_divider_determine_rate,
|
||||
.set_rate = ultrarisc_divider_set_rate,
|
||||
};
|
||||
|
||||
static struct clk_hw *ultrarisc_clk_register_pll(struct device *dev,
|
||||
const struct ultrarisc_pll_desc *desc,
|
||||
const struct ultrarisc_pll_layout *layout,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct clk_parent_data pdata = { .index = 0 };
|
||||
struct ultrarisc_pll_clk *pll;
|
||||
struct clk_init_data init = {
|
||||
.name = desc->name,
|
||||
.ops = &ultrarisc_pll_ro_ops,
|
||||
.parent_data = &pdata,
|
||||
.num_parents = 1,
|
||||
.flags = CLK_GET_RATE_NOCACHE,
|
||||
};
|
||||
int ret;
|
||||
|
||||
pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pll->base = base;
|
||||
pll->layout = layout;
|
||||
pll->hw.init = &init;
|
||||
|
||||
ret = devm_clk_hw_register(dev, &pll->hw);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return &pll->hw;
|
||||
}
|
||||
|
||||
static struct clk_hw *
|
||||
ultrarisc_clk_register_divider(struct device *dev,
|
||||
const struct ultrarisc_divider_desc *desc,
|
||||
struct clk_hw *parent_hw, void __iomem *base,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
const struct clk_parent_data pdata = { .hw = parent_hw };
|
||||
void __iomem *reg = base + desc->offset;
|
||||
struct ultrarisc_divider_clk *divider;
|
||||
|
||||
if (!desc->div_width)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!lock)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL);
|
||||
if (!divider)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
divider->divider.reg = reg;
|
||||
divider->divider.shift = desc->div_shift;
|
||||
divider->divider.width = desc->div_width;
|
||||
divider->divider.flags = desc->divider_flags;
|
||||
divider->divider.lock = lock;
|
||||
divider->load_mask = desc->load_mask;
|
||||
divider->gate.reg = reg;
|
||||
divider->gate.bit_idx = desc->gate_bit;
|
||||
divider->gate.flags = desc->gate_flags;
|
||||
divider->gate.lock = lock;
|
||||
|
||||
return devm_clk_hw_register_composite_pdata(dev, desc->name,
|
||||
&pdata, 1, NULL, NULL,
|
||||
÷r->divider.hw,
|
||||
&ultrarisc_divider_ops,
|
||||
÷r->gate.hw,
|
||||
&clk_gate_ops, 0);
|
||||
}
|
||||
|
||||
static int ultrarisc_clk_register_fixed_factors(struct device *dev,
|
||||
struct clk_hw_onecell_data *clk_data,
|
||||
const struct ultrarisc_clk_soc_data *soc_data)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < soc_data->num_fixed_factors; i++) {
|
||||
const struct ultrarisc_fixed_factor_desc *desc;
|
||||
struct clk_hw *parent_hw;
|
||||
struct clk_hw *hw;
|
||||
|
||||
desc = &soc_data->fixed_factors[i];
|
||||
if (desc->id >= clk_data->num || desc->parent_id >= clk_data->num)
|
||||
return -EINVAL;
|
||||
|
||||
parent_hw = clk_data->hws[desc->parent_id];
|
||||
if (!parent_hw)
|
||||
return -EINVAL;
|
||||
|
||||
hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, desc->name,
|
||||
parent_hw, 0,
|
||||
desc->mult,
|
||||
desc->div);
|
||||
if (IS_ERR(hw))
|
||||
return PTR_ERR(hw);
|
||||
|
||||
clk_data->hws[desc->id] = hw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ultrarisc_clk_register_plls(struct platform_device *pdev,
|
||||
struct clk_hw_onecell_data *clk_data,
|
||||
const struct ultrarisc_clk_soc_data *soc_data,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < soc_data->num_plls; i++) {
|
||||
const struct ultrarisc_pll_desc *desc = &soc_data->plls[i];
|
||||
struct clk_hw *hw;
|
||||
|
||||
if (desc->id >= clk_data->num) {
|
||||
dev_err(dev, "%s invalid clock ID %u >= %u\n",
|
||||
desc->name, desc->id, clk_data->num);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hw = ultrarisc_clk_register_pll(dev, desc, soc_data->pll_layout, base);
|
||||
if (IS_ERR(hw))
|
||||
return PTR_ERR(hw);
|
||||
|
||||
clk_data->hws[desc->id] = hw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ultrarisc_clk_register_dividers(struct platform_device *pdev,
|
||||
struct clk_hw_onecell_data *clk_data,
|
||||
const struct ultrarisc_clk_soc_data *soc_data,
|
||||
void __iomem *base,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < soc_data->num_dividers; i++) {
|
||||
const struct ultrarisc_divider_desc *desc;
|
||||
struct clk_hw *parent_hw;
|
||||
struct clk_hw *hw;
|
||||
|
||||
desc = &soc_data->dividers[i];
|
||||
if (desc->id >= clk_data->num || desc->parent_id >= clk_data->num)
|
||||
return -EINVAL;
|
||||
|
||||
parent_hw = clk_data->hws[desc->parent_id];
|
||||
if (!parent_hw)
|
||||
return -EINVAL;
|
||||
|
||||
hw = ultrarisc_clk_register_divider(dev, desc, parent_hw, base,
|
||||
lock);
|
||||
if (IS_ERR(hw))
|
||||
return PTR_ERR(hw);
|
||||
|
||||
if (desc->max_rate)
|
||||
clk_hw_set_rate_range(hw, 0, desc->max_rate);
|
||||
|
||||
clk_data->hws[desc->id] = hw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ultrarisc_clk_register_gates(struct platform_device *pdev,
|
||||
struct clk_hw_onecell_data *clk_data,
|
||||
const struct ultrarisc_clk_soc_data *soc_data,
|
||||
void __iomem *base,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < soc_data->num_gates; i++) {
|
||||
const struct ultrarisc_gate_desc *desc;
|
||||
struct clk_hw *parent_hw;
|
||||
struct clk_hw *hw;
|
||||
|
||||
desc = &soc_data->gates[i];
|
||||
if (desc->id >= clk_data->num || desc->parent_id >= clk_data->num)
|
||||
return -EINVAL;
|
||||
|
||||
parent_hw = clk_data->hws[desc->parent_id];
|
||||
if (!parent_hw)
|
||||
return -EINVAL;
|
||||
|
||||
hw = devm_clk_hw_register_gate_parent_hw(dev, desc->name,
|
||||
parent_hw, 0,
|
||||
base + desc->offset,
|
||||
desc->gate_bit,
|
||||
desc->gate_flags,
|
||||
lock);
|
||||
if (IS_ERR(hw))
|
||||
return PTR_ERR(hw);
|
||||
|
||||
clk_data->hws[desc->id] = hw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ultrarisc_clk_probe(struct platform_device *pdev,
|
||||
const struct ultrarisc_clk_soc_data *soc_data)
|
||||
{
|
||||
struct clk_hw_onecell_data *clk_data;
|
||||
struct device *dev = &pdev->dev;
|
||||
void __iomem *base;
|
||||
spinlock_t *lock;
|
||||
int ret;
|
||||
|
||||
if (!soc_data)
|
||||
return -EINVAL;
|
||||
|
||||
lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
|
||||
if (!lock)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(lock);
|
||||
|
||||
clk_data = devm_kzalloc(dev, struct_size(clk_data, hws,
|
||||
soc_data->num_clks),
|
||||
GFP_KERNEL);
|
||||
if (!clk_data)
|
||||
return -ENOMEM;
|
||||
|
||||
clk_data->num = soc_data->num_clks;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
ret = ultrarisc_clk_register_plls(pdev, clk_data, soc_data, base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ultrarisc_clk_register_fixed_factors(dev, clk_data, soc_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ultrarisc_clk_register_dividers(pdev, clk_data, soc_data, base, lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ultrarisc_clk_register_gates(pdev, clk_data, soc_data, base, lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (int i = 0; i < clk_data->num; i++) {
|
||||
if (!clk_data->hws[i]) {
|
||||
dev_err(dev, "missing clock ID %u\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(ultrarisc_clk_probe, "CLK_ULTRARISC");
|
||||
|
||||
MODULE_DESCRIPTION("UltraRISC clock core driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
71
drivers/clk/ultrarisc/clk-ultrarisc.h
Normal file
71
drivers/clk/ultrarisc/clk-ultrarisc.h
Normal 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 */
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
127
drivers/reset/reset-sky1-audss.c
Normal file
127
drivers/reset/reset-sky1-audss.c
Normal 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");
|
||||
@@ -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
|
||||
|
||||
60
include/dt-bindings/clock/cix,sky1-audss-cru.h
Normal file
60
include/dt-bindings/clock/cix,sky1-audss-cru.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
27
include/dt-bindings/clock/ultrarisc,dp1000-clk.h
Normal file
27
include/dt-bindings/clock/ultrarisc,dp1000-clk.h
Normal 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 */
|
||||
@@ -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_ */
|
||||
|
||||
25
include/dt-bindings/reset/cix,sky1-audss-cru.h
Normal file
25
include/dt-bindings/reset/cix,sky1-audss-cru.h
Normal 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
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user