Commit 7ddb58cb authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux

Pull clk updates from Stephen Boyd:
 "The usual collection of clk driver updates and new driver additions.
  In terms of lines it's mainly Qualcomm and Mediatek code, supporting
  various SoCs and their multitude of clk controllers.

  New Drivers:
   - GCC and RPMcc support for Qualcomm QCM2290 SoCs
   - GCC support for Qualcomm MSM8994/MSM8992 SoCs
   - LPASSCC and CAMCC support for Qualcomm SC7280 SoCs
   - Support for Mediatek MT8195 SoCs
   - Initial clock driver for the Exynos850 SoC
   - Add i.MX8ULP clock driver and related bindings

  Updates:
   - Clock power management for new SAMA7G5 SoC
   - Updates to the master clock driver and sam9x60-pll to be able to
     use cpufreq-dt driver and avoid overclocking of CPU and MCK0
     domains while changing the frequency via DVFS
   - Use ARRAY_SIZE in qcom clk drivers
   - Remove some impractical fallback parent names in qcom clk drivers
   - Make Mediatek clk drivers tristate
   - Refactoring of the CPU clock code and conversion of Samsung
     Exynos5433 CPU clock driver to the platform driver
   - A few conversions to devm_platform_ioremap_resource()
   - Updates of the Samsung Kconfig help text
   - Update video path realted clocks for Amlogic meson8
   - Add SPI Multi I/O Bus and SDHI clocks and resets on Renesas RZ/G2L
   - Add SPI Multi I/O Bus (RPC) clocks on Renesas R-Car V3U
   - Add MediaLB clocks on Renesas R-Car H3, M3-W/W+, and M3-N
   - Remove unused helpers from i.MX specific clock header
   - Rework all i.MX clk based helpers to use clk_hw based ones
   - Rework i.MX gate/mux/divider wrappers
   - Rework imx_clk_hw_composite and imx_clk_hw_pll14xx wrappers
   - Update i.MX pllv4 and composite clocks to support i.MX8ULP
   - Disable i.MX7ULP composite clock during initialization
   - Add CLK_SET_RATE_NO_REPARENT flag to the i.MX7ULP composite
   - Disable the i.MX pfd when set pfdv2 clock rate
   - Add support for i.MX8ULP in pfdv2
   - Add the pcc reset controller support on i.MX8ULP
   - Fix the build break when clk-imx8ulp is built as module
   - Move csi_sel mux to correct base register in i.MX6UL clock drivr
   - Fix csi clk gate register in i.MX6UL clock driver
   - Fix build bug making CLK_IMX8ULP select MXC_CLK
   - Add TPU (PWM), and Z (Cortex-A76) clocks on Renesas R-Car V3U
   - Add Ethernet clocks on Renesas RZ/G2L
   - Move Rockchip to use module_platform_probe
   - Enable usage of Coresight related clocks on Rockchip rk3399"

* tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (170 commits)
  clk: use clk_core_get_rate_recalc() in clk_rate_get()
  clk: at91: sama7g5: set low limit for mck0 at 32KHz
  clk: at91: sama7g5: remove prescaler part of master clock
  clk: at91: clk-master: add notifier for divider
  clk: at91: clk-sam9x60-pll: add notifier for div part of PLL
  clk: at91: clk-master: fix prescaler logic
  clk: at91: clk-master: mask mckr against layout->mask
  clk: at91: clk-master: check if div or pres is zero
  clk: at91: sam9x60-pll: use DIV_ROUND_CLOSEST_ULL
  clk: at91: pmc: add sama7g5 to the list of available pmcs
  clk: at91: clk-master: improve readability by using local variables
  clk: at91: clk-master: add register definition for sama7g5's master clock
  clk: at91: sama7g5: add securam's peripheral clock
  clk: at91: pmc: execute suspend/resume only for backup mode
  clk: at91: re-factor clocks suspend/resume
  clk: ux500: Add driver for the reset portions of PRCC
  dt-bindings: clock: u8500: Rewrite in YAML and extend
  clk: composite: Use rate_ops.determine_rate when also a mux is available
  clk: samsung: describe drivers in Kconfig
  clk: samsung: exynos5433: update apollo and atlas clock probing
  ...
parents ce840177 e2ceaa86
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/arm/mediatek/mediatek,mt8195-clock.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: MediaTek Functional Clock Controller for MT8195
maintainers:
- Chun-Jie Chen <chun-jie.chen@mediatek.com>
description:
The clock architecture in Mediatek like below
PLLs -->
dividers -->
muxes
-->
clock gate
The devices except apusys_pll provide clock gate control in different IP blocks.
The apusys_pll provides Plls which generated from SoC 26m for AI Processing Unit.
properties:
compatible:
items:
- enum:
- mediatek,mt8195-scp_adsp
- mediatek,mt8195-imp_iic_wrap_s
- mediatek,mt8195-imp_iic_wrap_w
- mediatek,mt8195-mfgcfg
- mediatek,mt8195-vppsys0
- mediatek,mt8195-wpesys
- mediatek,mt8195-wpesys_vpp0
- mediatek,mt8195-wpesys_vpp1
- mediatek,mt8195-vppsys1
- mediatek,mt8195-imgsys
- mediatek,mt8195-imgsys1_dip_top
- mediatek,mt8195-imgsys1_dip_nr
- mediatek,mt8195-imgsys1_wpe
- mediatek,mt8195-ipesys
- mediatek,mt8195-camsys
- mediatek,mt8195-camsys_rawa
- mediatek,mt8195-camsys_yuva
- mediatek,mt8195-camsys_rawb
- mediatek,mt8195-camsys_yuvb
- mediatek,mt8195-camsys_mraw
- mediatek,mt8195-ccusys
- mediatek,mt8195-vdecsys_soc
- mediatek,mt8195-vdecsys
- mediatek,mt8195-vdecsys_core1
- mediatek,mt8195-vencsys
- mediatek,mt8195-vencsys_core1
- mediatek,mt8195-apusys_pll
reg:
maxItems: 1
'#clock-cells':
const: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
scp_adsp: clock-controller@10720000 {
compatible = "mediatek,mt8195-scp_adsp";
reg = <0x10720000 0x1000>;
#clock-cells = <1>;
};
- |
imp_iic_wrap_s: clock-controller@11d03000 {
compatible = "mediatek,mt8195-imp_iic_wrap_s";
reg = <0x11d03000 0x1000>;
#clock-cells = <1>;
};
- |
imp_iic_wrap_w: clock-controller@11e05000 {
compatible = "mediatek,mt8195-imp_iic_wrap_w";
reg = <0x11e05000 0x1000>;
#clock-cells = <1>;
};
- |
mfgcfg: clock-controller@13fbf000 {
compatible = "mediatek,mt8195-mfgcfg";
reg = <0x13fbf000 0x1000>;
#clock-cells = <1>;
};
- |
vppsys0: clock-controller@14000000 {
compatible = "mediatek,mt8195-vppsys0";
reg = <0x14000000 0x1000>;
#clock-cells = <1>;
};
- |
wpesys: clock-controller@14e00000 {
compatible = "mediatek,mt8195-wpesys";
reg = <0x14e00000 0x1000>;
#clock-cells = <1>;
};
- |
wpesys_vpp0: clock-controller@14e02000 {
compatible = "mediatek,mt8195-wpesys_vpp0";
reg = <0x14e02000 0x1000>;
#clock-cells = <1>;
};
- |
wpesys_vpp1: clock-controller@14e03000 {
compatible = "mediatek,mt8195-wpesys_vpp1";
reg = <0x14e03000 0x1000>;
#clock-cells = <1>;
};
- |
vppsys1: clock-controller@14f00000 {
compatible = "mediatek,mt8195-vppsys1";
reg = <0x14f00000 0x1000>;
#clock-cells = <1>;
};
- |
imgsys: clock-controller@15000000 {
compatible = "mediatek,mt8195-imgsys";
reg = <0x15000000 0x1000>;
#clock-cells = <1>;
};
- |
imgsys1_dip_top: clock-controller@15110000 {
compatible = "mediatek,mt8195-imgsys1_dip_top";
reg = <0x15110000 0x1000>;
#clock-cells = <1>;
};
- |
imgsys1_dip_nr: clock-controller@15130000 {
compatible = "mediatek,mt8195-imgsys1_dip_nr";
reg = <0x15130000 0x1000>;
#clock-cells = <1>;
};
- |
imgsys1_wpe: clock-controller@15220000 {
compatible = "mediatek,mt8195-imgsys1_wpe";
reg = <0x15220000 0x1000>;
#clock-cells = <1>;
};
- |
ipesys: clock-controller@15330000 {
compatible = "mediatek,mt8195-ipesys";
reg = <0x15330000 0x1000>;
#clock-cells = <1>;
};
- |
camsys: clock-controller@16000000 {
compatible = "mediatek,mt8195-camsys";
reg = <0x16000000 0x1000>;
#clock-cells = <1>;
};
- |
camsys_rawa: clock-controller@1604f000 {
compatible = "mediatek,mt8195-camsys_rawa";
reg = <0x1604f000 0x1000>;
#clock-cells = <1>;
};
- |
camsys_yuva: clock-controller@1606f000 {
compatible = "mediatek,mt8195-camsys_yuva";
reg = <0x1606f000 0x1000>;
#clock-cells = <1>;
};
- |
camsys_rawb: clock-controller@1608f000 {
compatible = "mediatek,mt8195-camsys_rawb";
reg = <0x1608f000 0x1000>;
#clock-cells = <1>;
};
- |
camsys_yuvb: clock-controller@160af000 {
compatible = "mediatek,mt8195-camsys_yuvb";
reg = <0x160af000 0x1000>;
#clock-cells = <1>;
};
- |
camsys_mraw: clock-controller@16140000 {
compatible = "mediatek,mt8195-camsys_mraw";
reg = <0x16140000 0x1000>;
#clock-cells = <1>;
};
- |
ccusys: clock-controller@17200000 {
compatible = "mediatek,mt8195-ccusys";
reg = <0x17200000 0x1000>;
#clock-cells = <1>;
};
- |
vdecsys_soc: clock-controller@1800f000 {
compatible = "mediatek,mt8195-vdecsys_soc";
reg = <0x1800f000 0x1000>;
#clock-cells = <1>;
};
- |
vdecsys: clock-controller@1802f000 {
compatible = "mediatek,mt8195-vdecsys";
reg = <0x1802f000 0x1000>;
#clock-cells = <1>;
};
- |
vdecsys_core1: clock-controller@1803f000 {
compatible = "mediatek,mt8195-vdecsys_core1";
reg = <0x1803f000 0x1000>;
#clock-cells = <1>;
};
- |
vencsys: clock-controller@1a000000 {
compatible = "mediatek,mt8195-vencsys";
reg = <0x1a000000 0x1000>;
#clock-cells = <1>;
};
- |
vencsys_core1: clock-controller@1b000000 {
compatible = "mediatek,mt8195-vencsys_core1";
reg = <0x1b000000 0x1000>;
#clock-cells = <1>;
};
- |
apusys_pll: clock-controller@190f3000 {
compatible = "mediatek,mt8195-apusys_pll";
reg = <0x190f3000 0x1000>;
#clock-cells = <1>;
};
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/arm/mediatek/mediatek,mt8195-sys-clock.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: MediaTek System Clock Controller for MT8195
maintainers:
- Chun-Jie Chen <chun-jie.chen@mediatek.com>
description:
The clock architecture in Mediatek like below
PLLs -->
dividers -->
muxes
-->
clock gate
The apmixedsys provides most of PLLs which generated from SoC 26m.
The topckgen provides dividers and muxes which provide the clock source to other IP blocks.
The infracfg_ao and pericfg_ao provides clock gate in peripheral and infrastructure IP blocks.
properties:
compatible:
items:
- enum:
- mediatek,mt8195-topckgen
- mediatek,mt8195-infracfg_ao
- mediatek,mt8195-apmixedsys
- mediatek,mt8195-pericfg_ao
- const: syscon
reg:
maxItems: 1
'#clock-cells':
const: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
topckgen: syscon@10000000 {
compatible = "mediatek,mt8195-topckgen", "syscon";
reg = <0x10000000 0x1000>;
#clock-cells = <1>;
};
- |
infracfg_ao: syscon@10001000 {
compatible = "mediatek,mt8195-infracfg_ao", "syscon";
reg = <0x10001000 0x1000>;
#clock-cells = <1>;
};
- |
apmixedsys: syscon@1000c000 {
compatible = "mediatek,mt8195-apmixedsys", "syscon";
reg = <0x1000c000 0x1000>;
#clock-cells = <1>;
};
- |
pericfg_ao: syscon@11003000 {
compatible = "mediatek,mt8195-pericfg_ao", "syscon";
reg = <0x11003000 0x1000>;
#clock-cells = <1>;
};
......@@ -24,7 +24,7 @@ properties:
- const: allwinner,sun8i-v3s-de2-clk
- const: allwinner,sun50i-a64-de2-clk
- const: allwinner,sun50i-h5-de2-clk
- const: allwinner,sun50i-h6-de2-clk
- const: allwinner,sun50i-h6-de3-clk
- items:
- const: allwinner,sun8i-r40-de2-clk
- const: allwinner,sun8i-h3-de2-clk
......
......@@ -69,6 +69,10 @@ properties:
- arm,impd1-vco1
- arm,impd1-vco2
reg:
maxItems: 1
description: The VCO register
clocks:
description: Parent clock for the ICST VCO
maxItems: 1
......@@ -83,6 +87,7 @@ properties:
vco-offset:
$ref: '/schemas/types.yaml#/definitions/uint32'
description: Offset to the VCO register for the oscillator
deprecated: true
required:
- "#clock-cells"
......
Binding for simple memory mapped io fixed-rate clock sources.
The driver reads a clock frequency value from a single 32-bit memory mapped
I/O register and registers it as a fixed rate clock.
It was designed for test systems, like FPGA, not for complete, finished SoCs.
This binding uses the common clock binding[1].
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
Required properties:
- compatible : shall be "fixed-mmio-clock".
- #clock-cells : from common clock binding; shall be set to 0.
- reg : Address and length of the clock value register set.
Optional properties:
- clock-output-names : From common clock binding.
Example:
sysclock: sysclock@fd020004 {
#clock-cells = <0>;
compatible = "fixed-mmio-clock";
reg = <0xfd020004 0x4>;
};
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/fixed-mmio-clock.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Binding for simple memory mapped IO fixed-rate clock sources
description:
This binding describes a fixed-rate clock for which the frequency can
be read from a single 32-bit memory mapped I/O register.
It was designed for test systems, like FPGA, not for complete,
finished SoCs.
maintainers:
- Jan Kotas <jank@cadence.com>
properties:
compatible:
const: fixed-mmio-clock
reg:
maxItems: 1
"#clock-cells":
const: 0
clock-output-names:
maxItems: 1
required:
- compatible
- reg
- "#clock-cells"
additionalProperties: false
examples:
- |
sysclock: sysclock@fd020004 {
compatible = "fixed-mmio-clock";
#clock-cells = <0>;
reg = <0xfd020004 0x4>;
clock-output-names = "sysclk";
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/imx8ulp-cgc-clock.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP i.MX8ULP Clock Generation & Control(CGC) Module Binding
maintainers:
- Jacky Bai <ping.bai@nxp.com>
description: |
On i.MX8ULP, The clock sources generation, distribution and management is
under the control of several CGCs & PCCs modules. The CGC modules generate
and distribute clocks on the device.
properties:
compatible:
enum:
- fsl,imx8ulp-cgc1
- fsl,imx8ulp-cgc2
reg:
maxItems: 1
'#clock-cells':
const: 1
required:
- compatible
- reg
- '#clock-cells'
additionalProperties: false
examples:
# Clock Generation & Control Module node:
- |
clock-controller@292c0000 {
compatible = "fsl,imx8ulp-cgc1";
reg = <0x292c0000 0x10000>;
#clock-cells = <1>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/imx8ulp-pcc-clock.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP i.MX8ULP Peripheral Clock Controller(PCC) Module Binding
maintainers:
- Jacky Bai <ping.bai@nxp.com>
description: |
On i.MX8ULP, The clock sources generation, distribution and management is
under the control of several CGCs & PCCs modules. The PCC modules control
software reset, clock selection, optional division and clock gating mode
for peripherals.
properties:
compatible:
enum:
- fsl,imx8ulp-pcc3
- fsl,imx8ulp-pcc4
- fsl,imx8ulp-pcc5
reg:
maxItems: 1
'#clock-cells':
const: 1
'#reset-cells':
const: 1
required:
- compatible
- reg
- '#clock-cells'
- '#reset-cells'
additionalProperties: false
examples:
# Peripheral Clock Control Module node:
- |
clock-controller@292d0000 {
compatible = "fsl,imx8ulp-pcc3";
reg = <0x292d0000 0x10000>;
#clock-cells = <1>;
#reset-cells = <1>;
};
......@@ -56,6 +56,16 @@ properties:
reg:
maxItems: 1
power-domains:
description:
A phandle and PM domain specifier for the MMCX power domain.
maxItems: 1
required-opps:
description:
A phandle to an OPP node describing required MMCX performance point.
maxItems: 1
required:
- compatible
- reg
......@@ -70,6 +80,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,rpmh.h>
#include <dt-bindings/power/qcom-rpmpd.h>
clock-controller@af00000 {
compatible = "qcom,sm8250-dispcc";
reg = <0x0af00000 0x10000>;
......@@ -90,5 +101,7 @@ examples:
#clock-cells = <1>;
#reset-cells = <1>;
#power-domain-cells = <1>;
power-domains = <&rpmhpd SM8250_MMCX>;
required-opps = <&rpmhpd_opp_low_svs>;
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/qcom,gcc-msm8994.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Global Clock & Reset Controller Binding for MSM8994
maintainers:
- Konrad Dybcio <konrad.dybcio@somainline.org>
description: |
Qualcomm global clock control module which supports the clocks, resets and
power domains on MSM8994 and MSM8992.
See also:
- dt-bindings/clock/qcom,gcc-msm8994.h
properties:
compatible:
enum:
- qcom,gcc-msm8992
- qcom,gcc-msm8994
clocks:
items:
- description: Board XO source
- description: Sleep clock source
clock-names:
items:
- const: xo
- const: sleep
'#clock-cells':
const: 1
'#reset-cells':
const: 1
'#power-domain-cells':
const: 1
reg:
maxItems: 1
required:
- compatible
- clocks
- clock-names
- reg
- '#clock-cells'
- '#reset-cells'
- '#power-domain-cells'
additionalProperties: false
examples:
- |
clock-controller@300000 {
compatible = "qcom,gcc-msm8994";
reg = <0x00300000 0x90000>;
clocks = <&xo_board>, <&sleep_clk>;
clock-names = "xo", "sleep";
#clock-cells = <1>;
#reset-cells = <1>;
#power-domain-cells = <1>;
};
...
......@@ -25,21 +25,17 @@ properties:
items:
- description: Board XO source
- description: Sleep clock source
- description: USB 3.0 phy pipe clock
- description: UFS phy rx symbol clock for pipe 0
- description: UFS phy rx symbol clock for pipe 1
- description: UFS phy tx symbol clock
- description: PCIE phy pipe clock
- description: Audio reference clock (Optional clock)
- description: PLL test clock source (Optional clock)
minItems: 2
clock-names:
items:
- const: xo
- const: sleep_clk
- const: usb3_pipe
- const: ufs_rx_symbol0
- const: ufs_rx_symbol1
- const: ufs_tx_symbol0
- const: pcie0_pipe
- const: aud_ref_clk # Optional clock
- const: core_bi_pll_test_se # Optional clock
minItems: 2
'#clock-cells':
const: 1
......@@ -80,16 +76,10 @@ examples:
clocks = <&rpmcc RPM_SMD_XO_CLK_SRC>,
<&sleep>,
<0>,
<0>,
<0>,
<0>,
<0>;
clock-names = "xo",
"sleep_clk",
"usb3_pipe",
"ufs_rx_symbol0",
"ufs_rx_symbol1",
"ufs_tx_symbol0",
"pcie0_pipe";
"aud_ref_clk",
"core_bi_pll_test_se";
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/qcom,gcc-qcm2290.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Global Clock & Reset Controller Binding for QCM2290
maintainers:
- Shawn Guo <shawn.guo@linaro.org>
description: |
Qualcomm global clock control module which supports the clocks, resets
and power domains on QCM2290.
See also:
- dt-bindings/clock/qcom,gcc-qcm2290.h
properties:
compatible:
const: qcom,gcc-qcm2290
clocks:
items:
- description: Board XO source
- description: Sleep clock source
clock-names:
items:
- const: bi_tcxo
- const: sleep_clk
'#clock-cells':
const: 1
'#reset-cells':
const: 1
'#power-domain-cells':
const: 1
reg:
maxItems: 1
protected-clocks:
description:
Protected clock specifier list as per common clock binding.
required:
- compatible
- clocks
- clock-names
- reg
- '#clock-cells'
- '#reset-cells'
- '#power-domain-cells'
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,rpmcc.h>
clock-controller@1400000 {
compatible = "qcom,gcc-qcm2290";
reg = <0x01400000 0x1f0000>;
#clock-cells = <1>;
#reset-cells = <1>;
#power-domain-cells = <1>;
clock-names = "bi_tcxo", "sleep_clk";
clocks = <&rpmcc RPM_SMD_XO_CLK_SRC>, <&sleep_clk>;
};
...
......@@ -29,7 +29,6 @@ description: |
- dt-bindings/reset/qcom,gcc-msm8660.h
- dt-bindings/clock/qcom,gcc-msm8974.h (qcom,gcc-msm8226 and qcom,gcc-msm8974)
- dt-bindings/reset/qcom,gcc-msm8974.h (qcom,gcc-msm8226 and qcom,gcc-msm8974)
- dt-bindings/clock/qcom,gcc-msm8994.h
- dt-bindings/clock/qcom,gcc-mdm9607.h
- dt-bindings/clock/qcom,gcc-mdm9615.h
- dt-bindings/reset/qcom,gcc-mdm9615.h
......@@ -52,7 +51,6 @@ properties:
- qcom,gcc-msm8974
- qcom,gcc-msm8974pro
- qcom,gcc-msm8974pro-ac
- qcom,gcc-msm8994
- qcom,gcc-mdm9615
- qcom,gcc-sdm630
- qcom,gcc-sdm660
......
......@@ -25,6 +25,7 @@ Required properties :
"qcom,rpmcc-msm8994",·"qcom,rpmcc"
"qcom,rpmcc-msm8996", "qcom,rpmcc"
"qcom,rpmcc-msm8998", "qcom,rpmcc"
"qcom,rpmcc-qcm2290", "qcom,rpmcc"
"qcom,rpmcc-qcs404", "qcom,rpmcc"
"qcom,rpmcc-sdm660", "qcom,rpmcc"
"qcom,rpmcc-sm6115", "qcom,rpmcc"
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/qcom,sc7280-camcc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Camera Clock & Reset Controller Binding for SC7280
maintainers:
- Taniya Das <tdas@codeaurora.org>
description: |
Qualcomm camera clock control module which supports the clocks, resets and
power domains on SC7280.
See also dt-bindings/clock/qcom,camcc-sc7280.h
properties:
compatible:
const: qcom,sc7280-camcc
clocks:
items:
- description: Board XO source
- description: Board XO active source
- description: Sleep clock source
clock-names:
items:
- const: bi_tcxo
- const: bi_tcxo_ao
- const: sleep_clk
'#clock-cells':
const: 1
'#reset-cells':
const: 1
'#power-domain-cells':
const: 1
reg:
maxItems: 1
required:
- compatible
- reg
- clocks
- clock-names
- '#clock-cells'
- '#reset-cells'
- '#power-domain-cells'
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,rpmh.h>
clock-controller@ad00000 {
compatible = "qcom,sc7280-camcc";
reg = <0x0ad00000 0x10000>;
clocks = <&rpmhcc RPMH_CXO_CLK>,
<&rpmhcc RPMH_CXO_CLK_A>,
<&sleep_clk>;
clock-names = "bi_tcxo", "bi_tcxo_ao", "sleep_clk";
#clock-cells = <1>;
#reset-cells = <1>;
#power-domain-cells = <1>;
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/qcom,sc7280-lpasscc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm LPASS Core Clock Controller Binding for SC7280
maintainers:
- Taniya Das <tdas@codeaurora.org>
description: |
Qualcomm LPASS core clock control module which supports the clocks and
power domains on SC7280.
See also:
- dt-bindings/clock/qcom,lpass-sc7280.h
properties:
compatible:
enum:
- qcom,sc7280-lpasscc
clocks:
items:
- description: gcc_cfg_noc_lpass_clk from GCC
clock-names:
items:
- const: iface
'#clock-cells':
const: 1
reg:
items:
- description: LPASS qdsp6ss register
- description: LPASS top-cc register
- description: LPASS cc register
reg-names:
items:
- const: qdsp6ss
- const: top_cc
- const: cc
required:
- compatible
- reg
- clocks
- clock-names
- '#clock-cells'
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,gcc-sc7280.h>
#include <dt-bindings/clock/qcom,lpass-sc7280.h>
clock-controller@3000000 {
compatible = "qcom,sc7280-lpasscc";
reg = <0x03000000 0x40>, <0x03c04000 0x4>, <0x03389000 0x24>;
reg-names = "qdsp6ss", "top_cc", "cc";
clocks = <&gcc GCC_CFG_NOC_LPASS_CLK>;
clock-names = "iface";
#clock-cells = <1>;
};
...
......@@ -49,6 +49,16 @@ properties:
reg:
maxItems: 1
power-domains:
description:
A phandle and PM domain specifier for the MMCX power domain.
maxItems: 1
required-opps:
description:
A phandle to an OPP node describing required MMCX performance point.
maxItems: 1
required:
- compatible
- reg
......@@ -63,6 +73,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,rpmh.h>
#include <dt-bindings/power/qcom-rpmpd.h>
clock-controller@ab00000 {
compatible = "qcom,sdm845-videocc";
reg = <0x0ab00000 0x10000>;
......@@ -71,5 +82,7 @@ examples:
#clock-cells = <1>;
#reset-cells = <1>;
#power-domain-cells = <1>;
power-domains = <&rpmhpd SM8250_MMCX>;
required-opps = <&rpmhpd_opp_low_svs>;
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/samsung,exynos850-clock.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung Exynos850 SoC clock controller
maintainers:
- Sam Protsenko <semen.protsenko@linaro.org>
- Chanwoo Choi <cw00.choi@samsung.com>
- Krzysztof Kozlowski <krzysztof.kozlowski@canonical.com>
- Sylwester Nawrocki <s.nawrocki@samsung.com>
- Tomasz Figa <tomasz.figa@gmail.com>
description: |
Exynos850 clock controller is comprised of several CMU units, generating
clocks for different domains. Those CMU units are modeled as separate device
tree nodes, and might depend on each other. Root clocks in that clock tree are
two external clocks:: OSCCLK (26 MHz) and RTCCLK (32768 Hz). Those external
clocks must be defined as fixed-rate clocks in dts.
CMU_TOP is a top-level CMU, where all base clocks are prepared using PLLs and
dividers; all other leaf clocks (other CMUs) are usually derived from CMU_TOP.
Each clock is assigned an identifier and client nodes can use this identifier
to specify the clock which they consume. All clocks available for usage
in clock consumer nodes are defined as preprocessor macros in
'dt-bindings/clock/exynos850.h' header.
properties:
compatible:
enum:
- samsung,exynos850-cmu-top
- samsung,exynos850-cmu-core
- samsung,exynos850-cmu-dpu
- samsung,exynos850-cmu-hsi
- samsung,exynos850-cmu-peri
clocks:
minItems: 1
maxItems: 5
clock-names:
minItems: 1
maxItems: 5
"#clock-cells":
const: 1
reg:
maxItems: 1
allOf:
- if:
properties:
compatible:
contains:
const: samsung,exynos850-cmu-top
then:
properties:
clocks:
items:
- description: External reference clock (26 MHz)
clock-names:
items:
- const: oscclk
- if:
properties:
compatible:
contains:
const: samsung,exynos850-cmu-core
then:
properties:
clocks:
items:
- description: External reference clock (26 MHz)
- description: CMU_CORE bus clock (from CMU_TOP)
- description: CCI clock (from CMU_TOP)
- description: eMMC clock (from CMU_TOP)
- description: SSS clock (from CMU_TOP)
clock-names:
items:
- const: oscclk
- const: dout_core_bus
- const: dout_core_cci
- const: dout_core_mmc_embd
- const: dout_core_sss
- if:
properties:
compatible:
contains:
const: samsung,exynos850-cmu-dpu
then:
properties:
clocks:
items:
- description: External reference clock (26 MHz)
- description: DPU clock (from CMU_TOP)
clock-names:
items:
- const: oscclk
- const: dout_dpu
- if:
properties:
compatible:
contains:
const: samsung,exynos850-cmu-hsi
then:
properties:
clocks:
items:
- description: External reference clock (26 MHz)
- description: External RTC clock (32768 Hz)
- description: CMU_HSI bus clock (from CMU_TOP)
- description: SD card clock (from CMU_TOP)
- description: "USB 2.0 DRD clock (from CMU_TOP)"
clock-names:
items:
- const: oscclk
- const: rtcclk
- const: dout_hsi_bus
- const: dout_hsi_mmc_card
- const: dout_hsi_usb20drd
- if:
properties:
compatible:
contains:
const: samsung,exynos850-cmu-peri
then:
properties:
clocks:
items:
- description: External reference clock (26 MHz)
- description: CMU_PERI bus clock (from CMU_TOP)
- description: UART clock (from CMU_TOP)
- description: Parent clock for HSI2C and SPI (from CMU_TOP)
clock-names:
items:
- const: oscclk
- const: dout_peri_bus
- const: dout_peri_uart
- const: dout_peri_ip
required:
- compatible
- "#clock-cells"
- clocks
- clock-names
- reg
additionalProperties: false
examples:
# Clock controller node for CMU_PERI
- |
#include <dt-bindings/clock/exynos850.h>
cmu_peri: clock-controller@10030000 {
compatible = "samsung,exynos850-cmu-peri";
reg = <0x10030000 0x8000>;
#clock-cells = <1>;
clocks = <&oscclk>, <&cmu_top CLK_DOUT_PERI_BUS>,
<&cmu_top CLK_DOUT_PERI_UART>,
<&cmu_top CLK_DOUT_PERI_IP>;
clock-names = "oscclk", "dout_peri_bus",
"dout_peri_uart", "dout_peri_ip";
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/stericsson,u8500-clks.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ST-Ericsson DB8500 (U8500) clocks
maintainers:
- Ulf Hansson <ulf.hansson@linaro.org>
- Linus Walleij <linus.walleij@linaro.org>
description: While named "U8500 clocks" these clocks are inside the
DB8500 digital baseband system-on-chip and its siblings such as
DB8520. These bindings consider the clocks present in the SoC
itself, not off-chip clocks. There are four different on-chip
clocks - RTC (32 kHz), CPU clock (SMP TWD), PRCMU (power reset and
control management unit) clocks and PRCC (peripheral reset and
clock controller) clocks. For some reason PRCC 4 does not exist so
the itemization can be a bit unintuitive.
properties:
compatible:
enum:
- stericsson,u8500-clks
- stericsson,u8540-clks
- stericsson,u9540-clks
reg:
items:
- description: PRCC 1 register area
- description: PRCC 2 register area
- description: PRCC 3 register area
- description: PRCC 5 register area
- description: PRCC 6 register area
prcmu-clock:
description: A subnode with one clock cell for PRCMU (power, reset, control
management unit) clocks. The cell indicates which PRCMU clock in the
prcmu-clock node the consumer wants to use.
type: object
properties:
'#clock-cells':
const: 1
additionalProperties: false
prcc-periph-clock:
description: A subnode with two clock cells for PRCC (peripheral
reset and clock controller) peripheral clocks. The first cell indicates
which PRCC block the consumer wants to use, possible values are 1, 2, 3,
5, 6. The second cell indicates which clock inside the PRCC block it
wants, possible values are 0 thru 31.
type: object
properties:
'#clock-cells':
const: 2
additionalProperties: false
prcc-kernel-clock:
description: A subnode with two clock cells for PRCC (peripheral reset
and clock controller) kernel clocks. The first cell indicates which PRCC
block the consumer wants to use, possible values are 1, 2, 3, 5, 6. The
second cell indicates which clock inside the PRCC block it wants, possible
values are 0 thru 31.
type: object
properties:
'#clock-cells':
const: 2
additionalProperties: false
prcc-reset-controller:
description: A subnode with two reset cells for the reset portions of the
PRCC (peripheral reset and clock controller). The first cell indicates
which PRCC block the consumer wants to use, possible values are 1, 2, 3
5 and 6. The second cell indicates which reset line inside the PRCC block
it wants to control, possible values are 0 thru 31.
type: object
properties:
'#reset-cells':
const: 2
additionalProperties: false
rtc32k-clock:
description: A subnode with zero clock cells for the 32kHz RTC clock.
type: object
properties:
'#clock-cells':
const: 0
additionalProperties: false
smp-twd-clock:
description: A subnode for the ARM SMP Timer Watchdog cluster with zero
clock cells.
type: object
properties:
'#clock-cells':
const: 0
additionalProperties: false
required:
- compatible
- reg
- prcmu-clock
- prcc-periph-clock
- prcc-kernel-clock
- rtc32k-clock
- smp-twd-clock
additionalProperties: false
Clock bindings for ST-Ericsson Ux500 clocks
Required properties :
- compatible : shall contain only one of the following:
"stericsson,u8500-clks"
"stericsson,u8540-clks"
"stericsson,u9540-clks"
- reg : shall contain base register location and length for
CLKRST1, 2, 3, 5, and 6 in an array. Note the absence of
CLKRST4, which does not exist.
Required subnodes:
- prcmu-clock: a subnode with one clock cell for PRCMU (power,
reset, control unit) clocks. The cell indicates which PRCMU
clock in the prcmu-clock node the consumer wants to use.
- prcc-periph-clock: a subnode with two clock cells for
PRCC (programmable reset- and clock controller) peripheral clocks.
The first cell indicates which PRCC block the consumer
wants to use, possible values are 1, 2, 3, 5, 6. The second
cell indicates which clock inside the PRCC block it wants,
possible values are 0 thru 31.
- prcc-kernel-clock: a subnode with two clock cells for
PRCC (programmable reset- and clock controller) kernel clocks
The first cell indicates which PRCC block the consumer
wants to use, possible values are 1, 2, 3, 5, 6. The second
cell indicates which clock inside the PRCC block it wants,
possible values are 0 thru 31.
- rtc32k-clock: a subnode with zero clock cells for the 32kHz
RTC clock.
- smp-twd-clock: a subnode for the ARM SMP Timer Watchdog cluster
with zero clock cells.
Example:
clocks {
compatible = "stericsson,u8500-clks";
/*
* Registers for the CLKRST block on peripheral
* groups 1, 2, 3, 5, 6,
*/
reg = <0x8012f000 0x1000>, <0x8011f000 0x1000>,
<0x8000f000 0x1000>, <0xa03ff000 0x1000>,
<0xa03cf000 0x1000>;
prcmu_clk: prcmu-clock {
#clock-cells = <1>;
};
prcc_pclk: prcc-periph-clock {
#clock-cells = <2>;
};
prcc_kclk: prcc-kernel-clock {
#clock-cells = <2>;
};
rtc_clk: rtc32k-clock {
#clock-cells = <0>;
};
smp_twd_clk: smp-twd-clock {
#clock-cells = <0>;
};
};
......@@ -6,7 +6,7 @@ menuconfig ARCH_INTEGRATOR
select CMA
select DMA_CMA
select HAVE_TCM
select ICST
select CLK_ICST
select MFD_SYSCON
select PLAT_VERSATILE
select POWER_RESET
......
......@@ -11,7 +11,7 @@ menuconfig ARCH_REALVIEW
select HAVE_ARM_TWD if SMP
select HAVE_PATA_PLATFORM
select HAVE_TCM
select ICST
select CLK_ICST
select MACH_REALVIEW_EB if ARCH_MULTI_V5
select MFD_SYSCON
select PLAT_VERSATILE
......
......@@ -29,6 +29,7 @@ menuconfig ARCH_U8500
select REGULATOR_DB8500_PRCMU
select REGULATOR_FIXED_VOLTAGE
select SOC_BUS
select RESET_CONTROLLER
help
Support for ST-Ericsson's Ux500 architecture
......
......@@ -7,7 +7,7 @@ config ARCH_VERSATILE
select ARM_VIC
select CLKSRC_VERSATILE
select CPU_ARM926T
select ICST
select CLK_ICST
select MFD_SYSCON
select PLAT_VERSATILE
select POWER_RESET
......
......@@ -11,7 +11,7 @@ menuconfig ARCH_VEXPRESS
select HAVE_ARM_SCU if SMP
select HAVE_ARM_TWD if SMP
select HAVE_PATA_PLATFORM
select ICST
select CLK_ICST
select NO_IOPORT_MAP
select PLAT_VERSATILE
select POWER_RESET
......
......@@ -152,7 +152,7 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
"masterck_pres",
&at91rm9200_master_layout,
&rm9200_mck_characteristics,
&rm9200_mck_lock, CLK_SET_RATE_GATE);
&rm9200_mck_lock, CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
goto err_free;
......
......@@ -429,7 +429,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
&at91rm9200_master_layout,
data->mck_characteristics,
&at91sam9260_mck_lock,
CLK_SET_RATE_GATE);
CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
goto err_free;
......
......@@ -164,7 +164,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
&at91rm9200_master_layout,
&mck_characteristics,
&at91sam9g45_mck_lock,
CLK_SET_RATE_GATE);
CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
goto err_free;
......
......@@ -191,7 +191,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
&at91sam9x5_master_layout,
&mck_characteristics,
&at91sam9n12_mck_lock,
CLK_SET_RATE_GATE);
CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
goto err_free;
......
......@@ -132,7 +132,7 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
"masterck_pres",
&at91rm9200_master_layout,
&sam9rl_mck_characteristics,
&sam9rl_mck_lock, CLK_SET_RATE_GATE);
&sam9rl_mck_lock, CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
goto err_free;
......
......@@ -210,7 +210,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
"masterck_pres",
&at91sam9x5_master_layout,
&mck_characteristics, &mck_lock,
CLK_SET_RATE_GATE);
CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
goto err_free;
......
......@@ -27,6 +27,7 @@ struct clk_generated {
u32 id;
u32 gckdiv;
const struct clk_pcr_layout *layout;
struct at91_clk_pms pms;
u8 parent_id;
int chg_pid;
};
......@@ -34,25 +35,35 @@ struct clk_generated {
#define to_clk_generated(hw) \
container_of(hw, struct clk_generated, hw)
static int clk_generated_enable(struct clk_hw *hw)
static int clk_generated_set(struct clk_generated *gck, int status)
{
struct clk_generated *gck = to_clk_generated(hw);
unsigned long flags;
pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
__func__, gck->gckdiv, gck->parent_id);
unsigned int enable = status ? AT91_PMC_PCR_GCKEN : 0;
spin_lock_irqsave(gck->lock, flags);
regmap_write(gck->regmap, gck->layout->offset,
(gck->id & gck->layout->pid_mask));
regmap_update_bits(gck->regmap, gck->layout->offset,
AT91_PMC_PCR_GCKDIV_MASK | gck->layout->gckcss_mask |
gck->layout->cmd | AT91_PMC_PCR_GCKEN,
gck->layout->cmd | enable,
field_prep(gck->layout->gckcss_mask, gck->parent_id) |
gck->layout->cmd |
FIELD_PREP(AT91_PMC_PCR_GCKDIV_MASK, gck->gckdiv) |
AT91_PMC_PCR_GCKEN);
enable);
spin_unlock_irqrestore(gck->lock, flags);
return 0;
}
static int clk_generated_enable(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
__func__, gck->gckdiv, gck->parent_id);
clk_generated_set(gck, 1);
return 0;
}
......@@ -245,6 +256,23 @@ static int clk_generated_set_rate(struct clk_hw *hw,
return 0;
}
static int clk_generated_save_context(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
gck->pms.status = clk_generated_is_enabled(&gck->hw);
return 0;
}
static void clk_generated_restore_context(struct clk_hw *hw)
{
struct clk_generated *gck = to_clk_generated(hw);
if (gck->pms.status)
clk_generated_set(gck, gck->pms.status);
}
static const struct clk_ops generated_ops = {
.enable = clk_generated_enable,
.disable = clk_generated_disable,
......@@ -254,6 +282,8 @@ static const struct clk_ops generated_ops = {
.get_parent = clk_generated_get_parent,
.set_parent = clk_generated_set_parent,
.set_rate = clk_generated_set_rate,
.save_context = clk_generated_save_context,
.restore_context = clk_generated_restore_context,
};
/**
......@@ -320,8 +350,6 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
if (ret) {
kfree(gck);
hw = ERR_PTR(ret);
} else {
pmc_register_id(id);
}
return hw;
......
......@@ -28,6 +28,7 @@
struct clk_main_osc {
struct clk_hw hw;
struct regmap *regmap;
struct at91_clk_pms pms;
};
#define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw)
......@@ -37,6 +38,7 @@ struct clk_main_rc_osc {
struct regmap *regmap;
unsigned long frequency;
unsigned long accuracy;
struct at91_clk_pms pms;
};
#define to_clk_main_rc_osc(hw) container_of(hw, struct clk_main_rc_osc, hw)
......@@ -51,6 +53,7 @@ struct clk_rm9200_main {
struct clk_sam9x5_main {
struct clk_hw hw;
struct regmap *regmap;
struct at91_clk_pms pms;
u8 parent;
};
......@@ -120,10 +123,29 @@ static int clk_main_osc_is_prepared(struct clk_hw *hw)
return (status & AT91_PMC_MOSCS) && clk_main_parent_select(tmp);
}
static int clk_main_osc_save_context(struct clk_hw *hw)
{
struct clk_main_osc *osc = to_clk_main_osc(hw);
osc->pms.status = clk_main_osc_is_prepared(hw);
return 0;
}
static void clk_main_osc_restore_context(struct clk_hw *hw)
{
struct clk_main_osc *osc = to_clk_main_osc(hw);
if (osc->pms.status)
clk_main_osc_prepare(hw);
}
static const struct clk_ops main_osc_ops = {
.prepare = clk_main_osc_prepare,
.unprepare = clk_main_osc_unprepare,
.is_prepared = clk_main_osc_is_prepared,
.save_context = clk_main_osc_save_context,
.restore_context = clk_main_osc_restore_context,
};
struct clk_hw * __init
......@@ -240,12 +262,31 @@ static unsigned long clk_main_rc_osc_recalc_accuracy(struct clk_hw *hw,
return osc->accuracy;
}
static int clk_main_rc_osc_save_context(struct clk_hw *hw)
{
struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
osc->pms.status = clk_main_rc_osc_is_prepared(hw);
return 0;
}
static void clk_main_rc_osc_restore_context(struct clk_hw *hw)
{
struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
if (osc->pms.status)
clk_main_rc_osc_prepare(hw);
}
static const struct clk_ops main_rc_osc_ops = {
.prepare = clk_main_rc_osc_prepare,
.unprepare = clk_main_rc_osc_unprepare,
.is_prepared = clk_main_rc_osc_is_prepared,
.recalc_rate = clk_main_rc_osc_recalc_rate,
.recalc_accuracy = clk_main_rc_osc_recalc_accuracy,
.save_context = clk_main_rc_osc_save_context,
.restore_context = clk_main_rc_osc_restore_context,
};
struct clk_hw * __init
......@@ -465,12 +506,37 @@ static u8 clk_sam9x5_main_get_parent(struct clk_hw *hw)
return clk_main_parent_select(status);
}
static int clk_sam9x5_main_save_context(struct clk_hw *hw)
{
struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
clkmain->pms.status = clk_main_rc_osc_is_prepared(&clkmain->hw);
clkmain->pms.parent = clk_sam9x5_main_get_parent(&clkmain->hw);
return 0;
}
static void clk_sam9x5_main_restore_context(struct clk_hw *hw)
{
struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
int ret;
ret = clk_sam9x5_main_set_parent(hw, clkmain->pms.parent);
if (ret)
return;
if (clkmain->pms.status)
clk_sam9x5_main_prepare(hw);
}
static const struct clk_ops sam9x5_main_ops = {
.prepare = clk_sam9x5_main_prepare,
.is_prepared = clk_sam9x5_main_is_prepared,
.recalc_rate = clk_sam9x5_main_recalc_rate,
.set_parent = clk_sam9x5_main_set_parent,
.get_parent = clk_sam9x5_main_get_parent,
.save_context = clk_sam9x5_main_save_context,
.restore_context = clk_sam9x5_main_restore_context,
};
struct clk_hw * __init
......
This diff is collapsed.
......@@ -37,6 +37,7 @@ struct clk_sam9x5_peripheral {
u32 id;
u32 div;
const struct clk_pcr_layout *layout;
struct at91_clk_pms pms;
bool auto_div;
int chg_pid;
};
......@@ -155,10 +156,11 @@ static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
periph->div = shift;
}
static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
static int clk_sam9x5_peripheral_set(struct clk_sam9x5_peripheral *periph,
unsigned int status)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
unsigned long flags;
unsigned int enable = status ? AT91_PMC_PCR_EN : 0;
if (periph->id < PERIPHERAL_ID_MIN)
return 0;
......@@ -168,15 +170,21 @@ static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
(periph->id & periph->layout->pid_mask));
regmap_update_bits(periph->regmap, periph->layout->offset,
periph->layout->div_mask | periph->layout->cmd |
AT91_PMC_PCR_EN,
enable,
field_prep(periph->layout->div_mask, periph->div) |
periph->layout->cmd |
AT91_PMC_PCR_EN);
periph->layout->cmd | enable);
spin_unlock_irqrestore(periph->lock, flags);
return 0;
}
static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
return clk_sam9x5_peripheral_set(periph, 1);
}
static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
......@@ -393,6 +401,23 @@ static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
return -EINVAL;
}
static int clk_sam9x5_peripheral_save_context(struct clk_hw *hw)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
periph->pms.status = clk_sam9x5_peripheral_is_enabled(hw);
return 0;
}
static void clk_sam9x5_peripheral_restore_context(struct clk_hw *hw)
{
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
if (periph->pms.status)
clk_sam9x5_peripheral_set(periph, periph->pms.status);
}
static const struct clk_ops sam9x5_peripheral_ops = {
.enable = clk_sam9x5_peripheral_enable,
.disable = clk_sam9x5_peripheral_disable,
......@@ -400,6 +425,8 @@ static const struct clk_ops sam9x5_peripheral_ops = {
.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
.round_rate = clk_sam9x5_peripheral_round_rate,
.set_rate = clk_sam9x5_peripheral_set_rate,
.save_context = clk_sam9x5_peripheral_save_context,
.restore_context = clk_sam9x5_peripheral_restore_context,
};
static const struct clk_ops sam9x5_peripheral_chg_ops = {
......@@ -409,6 +436,8 @@ static const struct clk_ops sam9x5_peripheral_chg_ops = {
.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
.determine_rate = clk_sam9x5_peripheral_determine_rate,
.set_rate = clk_sam9x5_peripheral_set_rate,
.save_context = clk_sam9x5_peripheral_save_context,
.restore_context = clk_sam9x5_peripheral_restore_context,
};
struct clk_hw * __init
......@@ -460,7 +489,6 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
hw = ERR_PTR(ret);
} else {
clk_sam9x5_peripheral_autodiv(periph);
pmc_register_id(id);
}
return hw;
......
......@@ -40,6 +40,7 @@ struct clk_pll {
u16 mul;
const struct clk_pll_layout *layout;
const struct clk_pll_characteristics *characteristics;
struct at91_clk_pms pms;
};
static inline bool clk_pll_ready(struct regmap *regmap, int id)
......@@ -260,6 +261,42 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
static int clk_pll_save_context(struct clk_hw *hw)
{
struct clk_pll *pll = to_clk_pll(hw);
struct clk_hw *parent_hw = clk_hw_get_parent(hw);
pll->pms.parent_rate = clk_hw_get_rate(parent_hw);
pll->pms.rate = clk_pll_recalc_rate(&pll->hw, pll->pms.parent_rate);
pll->pms.status = clk_pll_ready(pll->regmap, PLL_REG(pll->id));
return 0;
}
static void clk_pll_restore_context(struct clk_hw *hw)
{
struct clk_pll *pll = to_clk_pll(hw);
unsigned long calc_rate;
unsigned int pllr, pllr_out, pllr_count;
u8 out = 0;
if (pll->characteristics->out)
out = pll->characteristics->out[pll->range];
regmap_read(pll->regmap, PLL_REG(pll->id), &pllr);
calc_rate = (pll->pms.parent_rate / PLL_DIV(pllr)) *
(PLL_MUL(pllr, pll->layout) + 1);
pllr_count = (pllr >> PLL_COUNT_SHIFT) & PLL_MAX_COUNT;
pllr_out = (pllr >> PLL_OUT_SHIFT) & out;
if (pll->pms.rate != calc_rate ||
pll->pms.status != clk_pll_ready(pll->regmap, PLL_REG(pll->id)) ||
pllr_count != PLL_MAX_COUNT ||
(out && pllr_out != out))
pr_warn("PLLAR was not configured properly by firmware\n");
}
static const struct clk_ops pll_ops = {
.prepare = clk_pll_prepare,
.unprepare = clk_pll_unprepare,
......@@ -267,6 +304,8 @@ static const struct clk_ops pll_ops = {
.recalc_rate = clk_pll_recalc_rate,
.round_rate = clk_pll_round_rate,
.set_rate = clk_pll_set_rate,
.save_context = clk_pll_save_context,
.restore_context = clk_pll_restore_context,
};
struct clk_hw * __init
......
......@@ -24,6 +24,7 @@ struct clk_programmable {
u32 *mux_table;
u8 id;
const struct clk_programmable_layout *layout;
struct at91_clk_pms pms;
};
#define to_clk_programmable(hw) container_of(hw, struct clk_programmable, hw)
......@@ -177,12 +178,38 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
static int clk_programmable_save_context(struct clk_hw *hw)
{
struct clk_programmable *prog = to_clk_programmable(hw);
struct clk_hw *parent_hw = clk_hw_get_parent(hw);
prog->pms.parent = clk_programmable_get_parent(hw);
prog->pms.parent_rate = clk_hw_get_rate(parent_hw);
prog->pms.rate = clk_programmable_recalc_rate(hw, prog->pms.parent_rate);
return 0;
}
static void clk_programmable_restore_context(struct clk_hw *hw)
{
struct clk_programmable *prog = to_clk_programmable(hw);
int ret;
ret = clk_programmable_set_parent(hw, prog->pms.parent);
if (ret)
return;
clk_programmable_set_rate(hw, prog->pms.rate, prog->pms.parent_rate);
}
static const struct clk_ops programmable_ops = {
.recalc_rate = clk_programmable_recalc_rate,
.determine_rate = clk_programmable_determine_rate,
.get_parent = clk_programmable_get_parent,
.set_parent = clk_programmable_set_parent,
.set_rate = clk_programmable_set_rate,
.save_context = clk_programmable_save_context,
.restore_context = clk_programmable_restore_context,
};
struct clk_hw * __init
......@@ -221,8 +248,6 @@ at91_clk_register_programmable(struct regmap *regmap,
if (ret) {
kfree(prog);
hw = ERR_PTR(ret);
} else {
pmc_register_pck(id);
}
return hw;
......
......@@ -5,6 +5,7 @@
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
......@@ -38,19 +39,24 @@ struct sam9x60_pll_core {
struct sam9x60_frac {
struct sam9x60_pll_core core;
struct at91_clk_pms pms;
u32 frac;
u16 mul;
};
struct sam9x60_div {
struct sam9x60_pll_core core;
struct at91_clk_pms pms;
u8 div;
u8 safe_div;
};
#define to_sam9x60_pll_core(hw) container_of(hw, struct sam9x60_pll_core, hw)
#define to_sam9x60_frac(core) container_of(core, struct sam9x60_frac, core)
#define to_sam9x60_div(core) container_of(core, struct sam9x60_div, core)
static struct sam9x60_div *notifier_div;
static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
{
unsigned int status;
......@@ -71,13 +77,12 @@ static unsigned long sam9x60_frac_pll_recalc_rate(struct clk_hw *hw,
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_frac *frac = to_sam9x60_frac(core);
return (parent_rate * (frac->mul + 1) +
((u64)parent_rate * frac->frac >> 22));
return parent_rate * (frac->mul + 1) +
DIV_ROUND_CLOSEST_ULL((u64)parent_rate * frac->frac, (1 << 22));
}
static int sam9x60_frac_pll_prepare(struct clk_hw *hw)
static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_frac *frac = to_sam9x60_frac(core);
struct regmap *regmap = core->regmap;
unsigned int val, cfrac, cmul;
......@@ -141,6 +146,13 @@ static int sam9x60_frac_pll_prepare(struct clk_hw *hw)
return 0;
}
static int sam9x60_frac_pll_prepare(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
return sam9x60_frac_pll_set(core);
}
static void sam9x60_frac_pll_unprepare(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
......@@ -280,6 +292,25 @@ static int sam9x60_frac_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
return ret;
}
static int sam9x60_frac_pll_save_context(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_frac *frac = to_sam9x60_frac(core);
frac->pms.status = sam9x60_pll_ready(core->regmap, core->id);
return 0;
}
static void sam9x60_frac_pll_restore_context(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_frac *frac = to_sam9x60_frac(core);
if (frac->pms.status)
sam9x60_frac_pll_set(core);
}
static const struct clk_ops sam9x60_frac_pll_ops = {
.prepare = sam9x60_frac_pll_prepare,
.unprepare = sam9x60_frac_pll_unprepare,
......@@ -287,6 +318,8 @@ static const struct clk_ops sam9x60_frac_pll_ops = {
.recalc_rate = sam9x60_frac_pll_recalc_rate,
.round_rate = sam9x60_frac_pll_round_rate,
.set_rate = sam9x60_frac_pll_set_rate,
.save_context = sam9x60_frac_pll_save_context,
.restore_context = sam9x60_frac_pll_restore_context,
};
static const struct clk_ops sam9x60_frac_pll_ops_chg = {
......@@ -296,11 +329,32 @@ static const struct clk_ops sam9x60_frac_pll_ops_chg = {
.recalc_rate = sam9x60_frac_pll_recalc_rate,
.round_rate = sam9x60_frac_pll_round_rate,
.set_rate = sam9x60_frac_pll_set_rate_chg,
.save_context = sam9x60_frac_pll_save_context,
.restore_context = sam9x60_frac_pll_restore_context,
};
static int sam9x60_div_pll_prepare(struct clk_hw *hw)
/* This function should be called with spinlock acquired. */
static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, u32 div,
bool enable)
{
struct regmap *regmap = core->regmap;
u32 ena_msk = enable ? core->layout->endiv_mask : 0;
u32 ena_val = enable ? (1 << core->layout->endiv_shift) : 0;
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
core->layout->div_mask | ena_msk,
(div << core->layout->div_shift) | ena_val);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | core->id);
while (!sam9x60_pll_ready(regmap, core->id))
cpu_relax();
}
static int sam9x60_div_pll_set(struct sam9x60_pll_core *core)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_div *div = to_sam9x60_div(core);
struct regmap *regmap = core->regmap;
unsigned long flags;
......@@ -316,17 +370,7 @@ static int sam9x60_div_pll_prepare(struct clk_hw *hw)
if (!!(val & core->layout->endiv_mask) && cdiv == div->div)
goto unlock;
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
core->layout->div_mask | core->layout->endiv_mask,
(div->div << core->layout->div_shift) |
(1 << core->layout->endiv_shift));
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | core->id);
while (!sam9x60_pll_ready(regmap, core->id))
cpu_relax();
sam9x60_div_pll_set_div(core, div->div, 1);
unlock:
spin_unlock_irqrestore(core->lock, flags);
......@@ -334,6 +378,13 @@ static int sam9x60_div_pll_prepare(struct clk_hw *hw)
return 0;
}
static int sam9x60_div_pll_prepare(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
return sam9x60_div_pll_set(core);
}
static void sam9x60_div_pll_unprepare(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
......@@ -465,16 +516,7 @@ static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
if (cdiv == div->div)
goto unlock;
regmap_update_bits(regmap, AT91_PMC_PLL_CTRL0,
core->layout->div_mask,
(div->div << core->layout->div_shift));
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT,
AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
AT91_PMC_PLL_UPDT_UPDATE | core->id);
while (!sam9x60_pll_ready(regmap, core->id))
cpu_relax();
sam9x60_div_pll_set_div(core, div->div, 0);
unlock:
spin_unlock_irqrestore(core->lock, irqflags);
......@@ -482,6 +524,67 @@ static int sam9x60_div_pll_set_rate_chg(struct clk_hw *hw, unsigned long rate,
return 0;
}
static int sam9x60_div_pll_save_context(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_div *div = to_sam9x60_div(core);
div->pms.status = sam9x60_div_pll_is_prepared(hw);
return 0;
}
static void sam9x60_div_pll_restore_context(struct clk_hw *hw)
{
struct sam9x60_pll_core *core = to_sam9x60_pll_core(hw);
struct sam9x60_div *div = to_sam9x60_div(core);
if (div->pms.status)
sam9x60_div_pll_set(core);
}
static int sam9x60_div_pll_notifier_fn(struct notifier_block *notifier,
unsigned long code, void *data)
{
struct sam9x60_div *div = notifier_div;
struct sam9x60_pll_core core = div->core;
struct regmap *regmap = core.regmap;
unsigned long irqflags;
u32 val, cdiv;
int ret = NOTIFY_DONE;
if (code != PRE_RATE_CHANGE)
return ret;
/*
* We switch to safe divider to avoid overclocking of other domains
* feed by us while the frac PLL (our parent) is changed.
*/
div->div = div->safe_div;
spin_lock_irqsave(core.lock, irqflags);
regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
core.id);
regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val);
cdiv = (val & core.layout->div_mask) >> core.layout->div_shift;
/* Stop if nothing changed. */
if (cdiv == div->safe_div)
goto unlock;
sam9x60_div_pll_set_div(&core, div->div, 0);
ret = NOTIFY_OK;
unlock:
spin_unlock_irqrestore(core.lock, irqflags);
return ret;
}
static struct notifier_block sam9x60_div_pll_notifier = {
.notifier_call = sam9x60_div_pll_notifier_fn,
};
static const struct clk_ops sam9x60_div_pll_ops = {
.prepare = sam9x60_div_pll_prepare,
.unprepare = sam9x60_div_pll_unprepare,
......@@ -489,6 +592,8 @@ static const struct clk_ops sam9x60_div_pll_ops = {
.recalc_rate = sam9x60_div_pll_recalc_rate,
.round_rate = sam9x60_div_pll_round_rate,
.set_rate = sam9x60_div_pll_set_rate,
.save_context = sam9x60_div_pll_save_context,
.restore_context = sam9x60_div_pll_restore_context,
};
static const struct clk_ops sam9x60_div_pll_ops_chg = {
......@@ -498,6 +603,8 @@ static const struct clk_ops sam9x60_div_pll_ops_chg = {
.recalc_rate = sam9x60_div_pll_recalc_rate,
.round_rate = sam9x60_div_pll_round_rate,
.set_rate = sam9x60_div_pll_set_rate_chg,
.save_context = sam9x60_div_pll_save_context,
.restore_context = sam9x60_div_pll_restore_context,
};
struct clk_hw * __init
......@@ -587,7 +694,8 @@ struct clk_hw * __init
sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
const char *name, const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, u32 flags)
const struct clk_pll_layout *layout, u32 flags,
u32 safe_div)
{
struct sam9x60_div *div;
struct clk_hw *hw;
......@@ -596,9 +704,13 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
unsigned int val;
int ret;
if (id > PLL_MAX_ID || !lock)
/* We only support one changeable PLL. */
if (id > PLL_MAX_ID || !lock || (safe_div && notifier_div))
return ERR_PTR(-EINVAL);
if (safe_div >= PLL_DIV_MAX)
safe_div = PLL_DIV_MAX - 1;
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
return ERR_PTR(-ENOMEM);
......@@ -618,6 +730,7 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
div->core.layout = layout;
div->core.regmap = regmap;
div->core.lock = lock;
div->safe_div = safe_div;
spin_lock_irqsave(div->core.lock, irqflags);
......@@ -633,6 +746,9 @@ sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
if (ret) {
kfree(div);
hw = ERR_PTR(ret);
} else if (div->safe_div) {
notifier_div = div;
clk_notifier_register(hw->clk, &sam9x60_div_pll_notifier);
}
return hw;
......
......@@ -20,6 +20,7 @@
struct clk_system {
struct clk_hw hw;
struct regmap *regmap;
struct at91_clk_pms pms;
u8 id;
};
......@@ -77,10 +78,29 @@ static int clk_system_is_prepared(struct clk_hw *hw)
return !!(status & (1 << sys->id));
}
static int clk_system_save_context(struct clk_hw *hw)
{
struct clk_system *sys = to_clk_system(hw);
sys->pms.status = clk_system_is_prepared(hw);
return 0;
}
static void clk_system_restore_context(struct clk_hw *hw)
{
struct clk_system *sys = to_clk_system(hw);
if (sys->pms.status)
clk_system_prepare(&sys->hw);
}
static const struct clk_ops system_ops = {
.prepare = clk_system_prepare,
.unprepare = clk_system_unprepare,
.is_prepared = clk_system_is_prepared,
.save_context = clk_system_save_context,
.restore_context = clk_system_restore_context,
};
struct clk_hw * __init
......
......@@ -24,6 +24,7 @@
struct at91sam9x5_clk_usb {
struct clk_hw hw;
struct regmap *regmap;
struct at91_clk_pms pms;
u32 usbs_mask;
u8 num_parents;
};
......@@ -148,12 +149,38 @@ static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
return 0;
}
static int at91sam9x5_usb_save_context(struct clk_hw *hw)
{
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
struct clk_hw *parent_hw = clk_hw_get_parent(hw);
usb->pms.parent = at91sam9x5_clk_usb_get_parent(hw);
usb->pms.parent_rate = clk_hw_get_rate(parent_hw);
usb->pms.rate = at91sam9x5_clk_usb_recalc_rate(hw, usb->pms.parent_rate);
return 0;
}
static void at91sam9x5_usb_restore_context(struct clk_hw *hw)
{
struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(hw);
int ret;
ret = at91sam9x5_clk_usb_set_parent(hw, usb->pms.parent);
if (ret)
return;
at91sam9x5_clk_usb_set_rate(hw, usb->pms.rate, usb->pms.parent_rate);
}
static const struct clk_ops at91sam9x5_usb_ops = {
.recalc_rate = at91sam9x5_clk_usb_recalc_rate,
.determine_rate = at91sam9x5_clk_usb_determine_rate,
.get_parent = at91sam9x5_clk_usb_get_parent,
.set_parent = at91sam9x5_clk_usb_set_parent,
.set_rate = at91sam9x5_clk_usb_set_rate,
.save_context = at91sam9x5_usb_save_context,
.restore_context = at91sam9x5_usb_restore_context,
};
static int at91sam9n12_clk_usb_enable(struct clk_hw *hw)
......
......@@ -23,6 +23,7 @@ struct clk_utmi {
struct clk_hw hw;
struct regmap *regmap_pmc;
struct regmap *regmap_sfr;
struct at91_clk_pms pms;
};
#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw)
......@@ -113,11 +114,30 @@ static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw,
return UTMI_RATE;
}
static int clk_utmi_save_context(struct clk_hw *hw)
{
struct clk_utmi *utmi = to_clk_utmi(hw);
utmi->pms.status = clk_utmi_is_prepared(hw);
return 0;
}
static void clk_utmi_restore_context(struct clk_hw *hw)
{
struct clk_utmi *utmi = to_clk_utmi(hw);
if (utmi->pms.status)
clk_utmi_prepare(hw);
}
static const struct clk_ops utmi_ops = {
.prepare = clk_utmi_prepare,
.unprepare = clk_utmi_unprepare,
.is_prepared = clk_utmi_is_prepared,
.recalc_rate = clk_utmi_recalc_rate,
.save_context = clk_utmi_save_context,
.restore_context = clk_utmi_restore_context,
};
static struct clk_hw * __init
......@@ -232,10 +252,29 @@ static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw)
return 0;
}
static int clk_utmi_sama7g5_save_context(struct clk_hw *hw)
{
struct clk_utmi *utmi = to_clk_utmi(hw);
utmi->pms.status = clk_utmi_sama7g5_is_prepared(hw);
return 0;
}
static void clk_utmi_sama7g5_restore_context(struct clk_hw *hw)
{
struct clk_utmi *utmi = to_clk_utmi(hw);
if (utmi->pms.status)
clk_utmi_sama7g5_prepare(hw);
}
static const struct clk_ops sama7g5_utmi_ops = {
.prepare = clk_utmi_sama7g5_prepare,
.is_prepared = clk_utmi_sama7g5_is_prepared,
.recalc_rate = clk_utmi_recalc_rate,
.save_context = clk_utmi_sama7g5_save_context,
.restore_context = clk_utmi_sama7g5_restore_context,
};
struct clk_hw * __init
......
......@@ -399,7 +399,7 @@ of_at91_clk_master_setup(struct device_node *np,
hw = at91_clk_register_master_div(regmap, name, "masterck_pres",
layout, characteristics,
&mck_lock, CLK_SET_RATE_GATE);
&mck_lock, CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
goto out_free_characteristics;
......
......@@ -3,10 +3,12 @@
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/clk/at91_pmc.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
......@@ -14,8 +16,6 @@
#include <asm/proc-fns.h>
#include <dt-bindings/clock/at91.h>
#include "pmc.h"
#define PMC_MAX_IDS 128
......@@ -111,151 +111,46 @@ struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
}
#ifdef CONFIG_PM
static struct regmap *pmcreg;
static u8 registered_ids[PMC_MAX_IDS];
static u8 registered_pcks[PMC_MAX_PCKS];
static struct
{
u32 scsr;
u32 pcsr0;
u32 uckr;
u32 mor;
u32 mcfr;
u32 pllar;
u32 mckr;
u32 usb;
u32 imr;
u32 pcsr1;
u32 pcr[PMC_MAX_IDS];
u32 audio_pll0;
u32 audio_pll1;
u32 pckr[PMC_MAX_PCKS];
} pmc_cache;
/* Address in SECURAM that say if we suspend to backup mode. */
static void __iomem *at91_pmc_backup_suspend;
/*
* As Peripheral ID 0 is invalid on AT91 chips, the identifier is stored
* without alteration in the table, and 0 is for unused clocks.
*/
void pmc_register_id(u8 id)
static int at91_pmc_suspend(void)
{
int i;
for (i = 0; i < PMC_MAX_IDS; i++) {
if (registered_ids[i] == 0) {
registered_ids[i] = id;
break;
}
if (registered_ids[i] == id)
break;
}
}
/*
* As Programmable Clock 0 is valid on AT91 chips, there is an offset
* of 1 between the stored value and the real clock ID.
*/
void pmc_register_pck(u8 pck)
{
int i;
for (i = 0; i < PMC_MAX_PCKS; i++) {
if (registered_pcks[i] == 0) {
registered_pcks[i] = pck + 1;
break;
}
if (registered_pcks[i] == (pck + 1))
break;
}
}
static int pmc_suspend(void)
{
int i;
u8 num;
regmap_read(pmcreg, AT91_PMC_SCSR, &pmc_cache.scsr);
regmap_read(pmcreg, AT91_PMC_PCSR, &pmc_cache.pcsr0);
regmap_read(pmcreg, AT91_CKGR_UCKR, &pmc_cache.uckr);
regmap_read(pmcreg, AT91_CKGR_MOR, &pmc_cache.mor);
regmap_read(pmcreg, AT91_CKGR_MCFR, &pmc_cache.mcfr);
regmap_read(pmcreg, AT91_CKGR_PLLAR, &pmc_cache.pllar);
regmap_read(pmcreg, AT91_PMC_MCKR, &pmc_cache.mckr);
regmap_read(pmcreg, AT91_PMC_USB, &pmc_cache.usb);
regmap_read(pmcreg, AT91_PMC_IMR, &pmc_cache.imr);
regmap_read(pmcreg, AT91_PMC_PCSR1, &pmc_cache.pcsr1);
for (i = 0; registered_ids[i]; i++) {
regmap_write(pmcreg, AT91_PMC_PCR,
(registered_ids[i] & AT91_PMC_PCR_PID_MASK));
regmap_read(pmcreg, AT91_PMC_PCR,
&pmc_cache.pcr[registered_ids[i]]);
}
for (i = 0; registered_pcks[i]; i++) {
num = registered_pcks[i] - 1;
regmap_read(pmcreg, AT91_PMC_PCKR(num), &pmc_cache.pckr[num]);
}
unsigned int backup;
if (!at91_pmc_backup_suspend)
return 0;
}
static bool pmc_ready(unsigned int mask)
{
unsigned int status;
regmap_read(pmcreg, AT91_PMC_SR, &status);
backup = readl_relaxed(at91_pmc_backup_suspend);
if (!backup)
return 0;
return ((status & mask) == mask) ? 1 : 0;
return clk_save_context();
}
static void pmc_resume(void)
static void at91_pmc_resume(void)
{
int i;
u8 num;
u32 tmp;
u32 mask = AT91_PMC_MCKRDY | AT91_PMC_LOCKA;
regmap_read(pmcreg, AT91_PMC_MCKR, &tmp);
if (pmc_cache.mckr != tmp)
pr_warn("MCKR was not configured properly by the firmware\n");
regmap_read(pmcreg, AT91_CKGR_PLLAR, &tmp);
if (pmc_cache.pllar != tmp)
pr_warn("PLLAR was not configured properly by the firmware\n");
regmap_write(pmcreg, AT91_PMC_SCER, pmc_cache.scsr);
regmap_write(pmcreg, AT91_PMC_PCER, pmc_cache.pcsr0);
regmap_write(pmcreg, AT91_CKGR_UCKR, pmc_cache.uckr);
regmap_write(pmcreg, AT91_CKGR_MOR, pmc_cache.mor);
regmap_write(pmcreg, AT91_CKGR_MCFR, pmc_cache.mcfr);
regmap_write(pmcreg, AT91_PMC_USB, pmc_cache.usb);
regmap_write(pmcreg, AT91_PMC_IMR, pmc_cache.imr);
regmap_write(pmcreg, AT91_PMC_PCER1, pmc_cache.pcsr1);
for (i = 0; registered_ids[i]; i++) {
regmap_write(pmcreg, AT91_PMC_PCR,
pmc_cache.pcr[registered_ids[i]] |
AT91_PMC_PCR_CMD);
}
for (i = 0; registered_pcks[i]; i++) {
num = registered_pcks[i] - 1;
regmap_write(pmcreg, AT91_PMC_PCKR(num), pmc_cache.pckr[num]);
}
unsigned int backup;
if (!at91_pmc_backup_suspend)
return;
if (pmc_cache.uckr & AT91_PMC_UPLLEN)
mask |= AT91_PMC_LOCKU;
backup = readl_relaxed(at91_pmc_backup_suspend);
if (!backup)
return;
while (!pmc_ready(mask))
cpu_relax();
clk_restore_context();
}
static struct syscore_ops pmc_syscore_ops = {
.suspend = pmc_suspend,
.resume = pmc_resume,
.suspend = at91_pmc_suspend,
.resume = at91_pmc_resume,
};
static const struct of_device_id sama5d2_pmc_dt_ids[] = {
static const struct of_device_id pmc_dt_ids[] = {
{ .compatible = "atmel,sama5d2-pmc" },
{ .compatible = "microchip,sama7g5-pmc", },
{ /* sentinel */ }
};
......@@ -263,14 +158,31 @@ static int __init pmc_register_ops(void)
{
struct device_node *np;
np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids);
np = of_find_matching_node(NULL, pmc_dt_ids);
if (!np)
return -ENODEV;
if (!of_device_is_available(np)) {
of_node_put(np);
return -ENODEV;
}
of_node_put(np);
np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam");
if (!np)
return -ENODEV;
pmcreg = device_node_to_regmap(np);
if (!of_device_is_available(np)) {
of_node_put(np);
if (IS_ERR(pmcreg))
return PTR_ERR(pmcreg);
return -ENODEV;
}
of_node_put(np);
at91_pmc_backup_suspend = of_iomap(np, 0);
if (!at91_pmc_backup_suspend) {
pr_warn("%s(): unable to map securam\n", __func__);
return -ENOMEM;
}
register_syscore_ops(&pmc_syscore_ops);
......
......@@ -13,6 +13,8 @@
#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <dt-bindings/clock/at91.h>
extern spinlock_t pmc_pcr_lock;
struct pmc_data {
......@@ -98,6 +100,20 @@ struct clk_pcr_layout {
u32 pid_mask;
};
/**
* struct at91_clk_pms - Power management state for AT91 clock
* @rate: clock rate
* @parent_rate: clock parent rate
* @status: clock status (enabled or disabled)
* @parent: clock parent index
*/
struct at91_clk_pms {
unsigned long rate;
unsigned long parent_rate;
unsigned int status;
unsigned int parent;
};
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
......@@ -166,7 +182,7 @@ at91_clk_register_master_div(struct regmap *regmap, const char *name,
const char *parent_names,
const struct clk_master_layout *layout,
const struct clk_master_characteristics *characteristics,
spinlock_t *lock, u32 flags);
spinlock_t *lock, u32 flags, u32 safe_div);
struct clk_hw * __init
at91_clk_sama7g5_register_master(struct regmap *regmap,
......@@ -198,7 +214,8 @@ struct clk_hw * __init
sam9x60_clk_register_div_pll(struct regmap *regmap, spinlock_t *lock,
const char *name, const char *parent_name, u8 id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, u32 flags);
const struct clk_pll_layout *layout, u32 flags,
u32 safe_div);
struct clk_hw * __init
sam9x60_clk_register_frac_pll(struct regmap *regmap, spinlock_t *lock,
......@@ -248,12 +265,4 @@ struct clk_hw * __init
at91_clk_sama7g5_register_utmi(struct regmap *regmap, const char *name,
const char *parent_name);
#ifdef CONFIG_PM
void pmc_register_id(u8 id);
void pmc_register_pck(u8 pck);
#else
static inline void pmc_register_id(u8 id) {}
static inline void pmc_register_pck(u8 pck) {}
#endif
#endif /* __PMC_H_ */
......@@ -242,7 +242,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
* This feeds CPU. It should not
* be disabled.
*/
CLK_IS_CRITICAL | CLK_SET_RATE_GATE);
CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
goto err_free;
......@@ -260,7 +260,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
&pll_div_layout,
CLK_SET_RATE_GATE |
CLK_SET_PARENT_GATE |
CLK_SET_RATE_PARENT);
CLK_SET_RATE_PARENT, 0);
if (IS_ERR(hw))
goto err_free;
......@@ -279,7 +279,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
hw = at91_clk_register_master_div(regmap, "masterck_div",
"masterck_pres", &sam9x60_master_layout,
&mck_characteristics, &mck_lock,
CLK_SET_RATE_GATE);
CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
goto err_free;
......
......@@ -249,7 +249,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
"masterck_pres",
&at91sam9x5_master_layout,
&mck_characteristics, &mck_lock,
CLK_SET_RATE_GATE);
CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
goto err_free;
......
......@@ -184,7 +184,7 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
"masterck_pres",
&at91sam9x5_master_layout,
&mck_characteristics, &mck_lock,
CLK_SET_RATE_GATE);
CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
goto err_free;
......
......@@ -199,7 +199,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
"masterck_pres",
&at91sam9x5_master_layout,
&mck_characteristics, &mck_lock,
CLK_SET_RATE_GATE);
CLK_SET_RATE_GATE, 0);
if (IS_ERR(hw))
goto err_free;
......
......@@ -127,6 +127,8 @@ static const struct clk_pll_characteristics pll_characteristics = {
* @t: clock type
* @f: clock flags
* @eid: export index in sama7g5->chws[] array
* @safe_div: intermediate divider need to be set on PRE_RATE_CHANGE
* notification
*/
static const struct {
const char *n;
......@@ -136,6 +138,7 @@ static const struct {
unsigned long f;
u8 t;
u8 eid;
u8 safe_div;
} sama7g5_plls[][PLL_ID_MAX] = {
[PLL_ID_CPU] = {
{ .n = "cpupll_fracck",
......@@ -156,7 +159,12 @@ static const struct {
.t = PLL_TYPE_DIV,
/* This feeds CPU. It should not be disabled. */
.f = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT,
.eid = PMC_CPUPLL, },
.eid = PMC_CPUPLL,
/*
* Safe div=15 should be safe even for switching b/w 1GHz and
* 90MHz (frac pll might go up to 1.2GHz).
*/
.safe_div = 15, },
},
[PLL_ID_SYS] = {
......@@ -377,6 +385,7 @@ static const struct {
u8 id;
} sama7g5_periphck[] = {
{ .n = "pioA_clk", .p = "mck0", .id = 11, },
{ .n = "securam_clk", .p = "mck0", .id = 18, },
{ .n = "sfr_clk", .p = "mck1", .id = 19, },
{ .n = "hsmc_clk", .p = "mck1", .id = 21, },
{ .n = "xdmac0_clk", .p = "mck1", .id = 22, },
......@@ -841,7 +850,7 @@ static const struct {
/* MCK0 characteristics. */
static const struct clk_master_characteristics mck0_characteristics = {
.output = { .min = 50000000, .max = 200000000 },
.output = { .min = 32768, .max = 200000000 },
.divisors = { 1, 2, 4, 3, 5 },
.have_div3_pres = 1,
};
......@@ -966,7 +975,8 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
sama7g5_plls[i][j].p, i,
sama7g5_plls[i][j].c,
sama7g5_plls[i][j].l,
sama7g5_plls[i][j].f);
sama7g5_plls[i][j].f,
sama7g5_plls[i][j].safe_div);
break;
default:
......@@ -982,18 +992,9 @@ static void __init sama7g5_pmc_setup(struct device_node *np)
}
parent_names[0] = "cpupll_divpmcck";
hw = at91_clk_register_master_pres(regmap, "cpuck", 1, parent_names,
&mck0_layout, &mck0_characteristics,
&pmc_mck0_lock,
CLK_SET_RATE_PARENT, 0);
if (IS_ERR(hw))
goto err_free;
sama7g5_pmc->chws[PMC_CPU] = hw;
hw = at91_clk_register_master_div(regmap, "mck0", "cpuck",
hw = at91_clk_register_master_div(regmap, "mck0", "cpupll_divpmcck",
&mck0_layout, &mck0_characteristics,
&pmc_mck0_lock, 0);
&pmc_mck0_lock, CLK_GET_RATE_NOCACHE, 5);
if (IS_ERR(hw))
goto err_free;
......
......@@ -42,6 +42,29 @@ static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
return rate_ops->recalc_rate(rate_hw, parent_rate);
}
static int clk_composite_determine_rate_for_parent(struct clk_hw *rate_hw,
struct clk_rate_request *req,
struct clk_hw *parent_hw,
const struct clk_ops *rate_ops)
{
long rate;
req->best_parent_hw = parent_hw;
req->best_parent_rate = clk_hw_get_rate(parent_hw);
if (rate_ops->determine_rate)
return rate_ops->determine_rate(rate_hw, req);
rate = rate_ops->round_rate(rate_hw, req->rate,
&req->best_parent_rate);
if (rate < 0)
return rate;
req->rate = rate;
return 0;
}
static int clk_composite_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
......@@ -51,51 +74,56 @@ static int clk_composite_determine_rate(struct clk_hw *hw,
struct clk_hw *rate_hw = composite->rate_hw;
struct clk_hw *mux_hw = composite->mux_hw;
struct clk_hw *parent;
unsigned long parent_rate;
long tmp_rate, best_rate = 0;
unsigned long rate_diff;
unsigned long best_rate_diff = ULONG_MAX;
long rate;
int i;
unsigned long best_rate = 0;
int i, ret;
if (rate_hw && rate_ops && rate_ops->round_rate &&
if (rate_hw && rate_ops &&
(rate_ops->determine_rate || rate_ops->round_rate) &&
mux_hw && mux_ops && mux_ops->set_parent) {
req->best_parent_hw = NULL;
if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) {
struct clk_rate_request tmp_req = *req;
parent = clk_hw_get_parent(mux_hw);
req->best_parent_hw = parent;
req->best_parent_rate = clk_hw_get_rate(parent);
rate = rate_ops->round_rate(rate_hw, req->rate,
&req->best_parent_rate);
if (rate < 0)
return rate;
ret = clk_composite_determine_rate_for_parent(rate_hw,
&tmp_req,
parent,
rate_ops);
if (ret)
return ret;
req->rate = tmp_req.rate;
req->best_parent_rate = tmp_req.best_parent_rate;
req->rate = rate;
return 0;
}
for (i = 0; i < clk_hw_get_num_parents(mux_hw); i++) {
struct clk_rate_request tmp_req = *req;
parent = clk_hw_get_parent_by_index(mux_hw, i);
if (!parent)
continue;
parent_rate = clk_hw_get_rate(parent);
tmp_rate = rate_ops->round_rate(rate_hw, req->rate,
&parent_rate);
if (tmp_rate < 0)
ret = clk_composite_determine_rate_for_parent(rate_hw,
&tmp_req,
parent,
rate_ops);
if (ret)
continue;
rate_diff = abs(req->rate - tmp_rate);
rate_diff = abs(req->rate - tmp_req.rate);
if (!rate_diff || !req->best_parent_hw
|| best_rate_diff > rate_diff) {
req->best_parent_hw = parent;
req->best_parent_rate = parent_rate;
req->best_parent_rate = tmp_req.best_parent_rate;
best_rate_diff = rate_diff;
best_rate = tmp_rate;
best_rate = tmp_req.rate;
}
if (!rate_diff)
......@@ -362,6 +390,7 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
return ERR_CAST(hw);
return hw->clk;
}
EXPORT_SYMBOL_GPL(clk_register_composite);
struct clk *clk_register_composite_pdata(struct device *dev, const char *name,
const struct clk_parent_data *parent_data,
......
......@@ -3108,7 +3108,10 @@ static int clk_rate_get(void *data, u64 *val)
{
struct clk_core *core = data;
*val = core->rate;
clk_prepare_lock();
*val = clk_core_get_rate_recalc(core);
clk_prepare_unlock();
return 0;
}
......
......@@ -98,3 +98,10 @@ config CLK_IMX8QXP
select MXC_CLK_SCU
help
Build the driver for IMX8QXP SCU based clocks.
config CLK_IMX8ULP
tristate "IMX8ULP CCM Clock Driver"
depends on ARCH_MXC || COMPILE_TEST
select MXC_CLK
help
Build the driver for i.MX8ULP CCM Clock Driver
......@@ -31,6 +31,8 @@ clk-imx-scu-$(CONFIG_CLK_IMX8QXP) += clk-scu.o clk-imx8qxp.o \
clk-imx8qxp-rsrc.o clk-imx8qm-rsrc.o
clk-imx-lpcg-scu-$(CONFIG_CLK_IMX8QXP) += clk-lpcg-scu.o clk-imx8qxp-lpcg.o
obj-$(CONFIG_CLK_IMX8ULP) += clk-imx8ulp.o
obj-$(CONFIG_CLK_IMX1) += clk-imx1.o
obj-$(CONFIG_CLK_IMX25) += clk-imx25.o
obj-$(CONFIG_CLK_IMX27) += clk-imx27.o
......
......@@ -8,6 +8,7 @@
#include <linux/bits.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/slab.h>
#include "../clk-fractional-divider.h"
......@@ -23,17 +24,61 @@
#define PCG_PCD_WIDTH 3
#define PCG_PCD_MASK 0x7
struct clk_hw *imx7ulp_clk_hw_composite(const char *name,
#define SW_RST BIT(28)
static int pcc_gate_enable(struct clk_hw *hw)
{
struct clk_gate *gate = to_clk_gate(hw);
unsigned long flags;
u32 val;
int ret;
ret = clk_gate_ops.enable(hw);
if (ret)
return ret;
spin_lock_irqsave(gate->lock, flags);
/*
* release the sw reset for peripherals associated with
* with this pcc clock.
*/
val = readl(gate->reg);
val |= SW_RST;
writel(val, gate->reg);
spin_unlock_irqrestore(gate->lock, flags);
return 0;
}
static void pcc_gate_disable(struct clk_hw *hw)
{
clk_gate_ops.disable(hw);
}
static int pcc_gate_is_enabled(struct clk_hw *hw)
{
return clk_gate_ops.is_enabled(hw);
}
static const struct clk_ops pcc_gate_ops = {
.enable = pcc_gate_enable,
.disable = pcc_gate_disable,
.is_enabled = pcc_gate_is_enabled,
};
static struct clk_hw *imx_ulp_clk_hw_composite(const char *name,
const char * const *parent_names,
int num_parents, bool mux_present,
bool rate_present, bool gate_present,
void __iomem *reg)
void __iomem *reg, bool has_swrst)
{
struct clk_hw *mux_hw = NULL, *fd_hw = NULL, *gate_hw = NULL;
struct clk_fractional_divider *fd = NULL;
struct clk_gate *gate = NULL;
struct clk_mux *mux = NULL;
struct clk_hw *hw;
u32 val;
if (mux_present) {
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
......@@ -43,6 +88,8 @@ struct clk_hw *imx7ulp_clk_hw_composite(const char *name,
mux->reg = reg;
mux->shift = PCG_PCS_SHIFT;
mux->mask = PCG_PCS_MASK;
if (has_swrst)
mux->lock = &imx_ccm_lock;
}
if (rate_present) {
......@@ -60,6 +107,8 @@ struct clk_hw *imx7ulp_clk_hw_composite(const char *name,
fd->nwidth = PCG_PCD_WIDTH;
fd->nmask = PCG_PCD_MASK;
fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED;
if (has_swrst)
fd->lock = &imx_ccm_lock;
}
if (gate_present) {
......@@ -72,13 +121,27 @@ struct clk_hw *imx7ulp_clk_hw_composite(const char *name,
gate_hw = &gate->hw;
gate->reg = reg;
gate->bit_idx = PCG_CGC_SHIFT;
if (has_swrst)
gate->lock = &imx_ccm_lock;
/*
* make sure clock is gated during clock tree initialization,
* the HW ONLY allow clock parent/rate changed with clock gated,
* during clock tree initialization, clocks could be enabled
* by bootloader, so the HW status will mismatch with clock tree
* prepare count, then clock core driver will allow parent/rate
* change since the prepare count is zero, but HW actually
* prevent the parent/rate change due to the clock is enabled.
*/
val = readl_relaxed(reg);
val &= ~(1 << PCG_CGC_SHIFT);
writel_relaxed(val, reg);
}
hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
mux_hw, &clk_mux_ops, fd_hw,
&clk_fractional_divider_ops, gate_hw,
&clk_gate_ops, CLK_SET_RATE_GATE |
CLK_SET_PARENT_GATE);
has_swrst ? &pcc_gate_ops : &clk_gate_ops, CLK_SET_RATE_GATE |
CLK_SET_PARENT_GATE | CLK_SET_RATE_NO_REPARENT);
if (IS_ERR(hw)) {
kfree(mux);
kfree(fd);
......@@ -87,3 +150,20 @@ struct clk_hw *imx7ulp_clk_hw_composite(const char *name,
return hw;
}
struct clk_hw *imx7ulp_clk_hw_composite(const char *name, const char * const *parent_names,
int num_parents, bool mux_present, bool rate_present,
bool gate_present, void __iomem *reg)
{
return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present,
gate_present, reg, false);
}
struct clk_hw *imx8ulp_clk_hw_composite(const char *name, const char * const *parent_names,
int num_parents, bool mux_present, bool rate_present,
bool gate_present, void __iomem *reg, bool has_swrst)
{
return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present,
gate_present, reg, has_swrst);
}
EXPORT_SYMBOL_GPL(imx8ulp_clk_hw_composite);
......@@ -171,7 +171,7 @@ static const struct clk_ops imx8m_clk_composite_mux_ops = {
.determine_rate = imx8m_clk_composite_mux_determine_rate,
};
struct clk_hw *imx8m_clk_hw_composite_flags(const char *name,
struct clk_hw *__imx8m_clk_hw_composite(const char *name,
const char * const *parent_names,
int num_parents, void __iomem *reg,
u32 composite_flags,
......@@ -246,4 +246,4 @@ struct clk_hw *imx8m_clk_hw_composite_flags(const char *name,
kfree(mux);
return ERR_CAST(hw);
}
EXPORT_SYMBOL_GPL(imx8m_clk_hw_composite_flags);
EXPORT_SYMBOL_GPL(__imx8m_clk_hw_composite);
......@@ -161,7 +161,6 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
hws[IMX6UL_PLL5_BYPASS] = imx_clk_hw_mux_flags("pll5_bypass", base + 0xa0, 16, 1, pll5_bypass_sels, ARRAY_SIZE(pll5_bypass_sels), CLK_SET_RATE_PARENT);
hws[IMX6UL_PLL6_BYPASS] = imx_clk_hw_mux_flags("pll6_bypass", base + 0xe0, 16, 1, pll6_bypass_sels, ARRAY_SIZE(pll6_bypass_sels), CLK_SET_RATE_PARENT);
hws[IMX6UL_PLL7_BYPASS] = imx_clk_hw_mux_flags("pll7_bypass", base + 0x20, 16, 1, pll7_bypass_sels, ARRAY_SIZE(pll7_bypass_sels), CLK_SET_RATE_PARENT);
hws[IMX6UL_CLK_CSI_SEL] = imx_clk_hw_mux_flags("csi_sel", base + 0x3c, 9, 2, csi_sels, ARRAY_SIZE(csi_sels), CLK_SET_RATE_PARENT);
/* Do not bypass PLLs initially */
clk_set_parent(hws[IMX6UL_PLL1_BYPASS]->clk, hws[IMX6UL_CLK_PLL1]->clk);
......@@ -270,6 +269,7 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
hws[IMX6UL_CLK_ECSPI_SEL] = imx_clk_hw_mux("ecspi_sel", base + 0x38, 18, 1, ecspi_sels, ARRAY_SIZE(ecspi_sels));
hws[IMX6UL_CLK_LCDIF_PRE_SEL] = imx_clk_hw_mux_flags("lcdif_pre_sel", base + 0x38, 15, 3, lcdif_pre_sels, ARRAY_SIZE(lcdif_pre_sels), CLK_SET_RATE_PARENT);
hws[IMX6UL_CLK_LCDIF_SEL] = imx_clk_hw_mux("lcdif_sel", base + 0x38, 9, 3, lcdif_sels, ARRAY_SIZE(lcdif_sels));
hws[IMX6UL_CLK_CSI_SEL] = imx_clk_hw_mux("csi_sel", base + 0x3c, 9, 2, csi_sels, ARRAY_SIZE(csi_sels));
hws[IMX6UL_CLK_LDB_DI0_DIV_SEL] = imx_clk_hw_mux("ldb_di0", base + 0x20, 10, 1, ldb_di0_div_sels, ARRAY_SIZE(ldb_di0_div_sels));
hws[IMX6UL_CLK_LDB_DI1_DIV_SEL] = imx_clk_hw_mux("ldb_di1", base + 0x20, 11, 1, ldb_di1_div_sels, ARRAY_SIZE(ldb_di1_div_sels));
......@@ -380,7 +380,6 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
hws[IMX6ULL_CLK_ESAI_IPG] = imx_clk_hw_gate2_shared("esai_ipg", "ahb", base + 0x70, 0, &share_count_esai);
hws[IMX6ULL_CLK_ESAI_MEM] = imx_clk_hw_gate2_shared("esai_mem", "ahb", base + 0x70, 0, &share_count_esai);
}
hws[IMX6UL_CLK_CSI] = imx_clk_hw_gate2("csi", "csi_podf", base + 0x70, 2);
hws[IMX6UL_CLK_I2C1] = imx_clk_hw_gate2("i2c1", "perclk", base + 0x70, 6);
hws[IMX6UL_CLK_I2C2] = imx_clk_hw_gate2("i2c2", "perclk", base + 0x70, 8);
hws[IMX6UL_CLK_I2C3] = imx_clk_hw_gate2("i2c3", "perclk", base + 0x70, 10);
......@@ -391,6 +390,12 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
hws[IMX6UL_CLK_PXP] = imx_clk_hw_gate2("pxp", "axi", base + 0x70, 30);
/* CCGR3 */
/*
* Although the imx6ull reference manual lists CCGR2 as the csi clk
* gate register, tests have shown that it is actually the CCGR3
* register bit 0/1, same as for the imx6ul.
*/
hws[IMX6UL_CLK_CSI] = imx_clk_hw_gate2("csi", "csi_podf", base + 0x74, 0);
hws[IMX6UL_CLK_UART5_IPG] = imx_clk_hw_gate2("uart5_ipg", "ipg", base + 0x74, 2);
hws[IMX6UL_CLK_UART5_SERIAL] = imx_clk_hw_gate2("uart5_serial", "uart_podf", base + 0x74, 2);
if (clk_on_imx6ul()) {
......
......@@ -78,20 +78,20 @@ static void __init imx7ulp_clk_scg1_init(struct device_node *np)
hws[IMX7ULP_CLK_SPLL_PRE_DIV] = imx_clk_hw_divider_flags("spll_pre_div", "spll_pre_sel", base + 0x608, 8, 3, CLK_SET_RATE_GATE);
/* name parent_name base */
hws[IMX7ULP_CLK_APLL] = imx_clk_hw_pllv4("apll", "apll_pre_div", base + 0x500);
hws[IMX7ULP_CLK_SPLL] = imx_clk_hw_pllv4("spll", "spll_pre_div", base + 0x600);
hws[IMX7ULP_CLK_APLL] = imx_clk_hw_pllv4(IMX_PLLV4_IMX7ULP, "apll", "apll_pre_div", base + 0x500);
hws[IMX7ULP_CLK_SPLL] = imx_clk_hw_pllv4(IMX_PLLV4_IMX7ULP, "spll", "spll_pre_div", base + 0x600);
/* APLL PFDs */
hws[IMX7ULP_CLK_APLL_PFD0] = imx_clk_hw_pfdv2("apll_pfd0", "apll", base + 0x50c, 0);
hws[IMX7ULP_CLK_APLL_PFD1] = imx_clk_hw_pfdv2("apll_pfd1", "apll", base + 0x50c, 1);
hws[IMX7ULP_CLK_APLL_PFD2] = imx_clk_hw_pfdv2("apll_pfd2", "apll", base + 0x50c, 2);
hws[IMX7ULP_CLK_APLL_PFD3] = imx_clk_hw_pfdv2("apll_pfd3", "apll", base + 0x50c, 3);
hws[IMX7ULP_CLK_APLL_PFD0] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "apll_pfd0", "apll", base + 0x50c, 0);
hws[IMX7ULP_CLK_APLL_PFD1] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "apll_pfd1", "apll", base + 0x50c, 1);
hws[IMX7ULP_CLK_APLL_PFD2] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "apll_pfd2", "apll", base + 0x50c, 2);
hws[IMX7ULP_CLK_APLL_PFD3] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "apll_pfd3", "apll", base + 0x50c, 3);
/* SPLL PFDs */
hws[IMX7ULP_CLK_SPLL_PFD0] = imx_clk_hw_pfdv2("spll_pfd0", "spll", base + 0x60C, 0);
hws[IMX7ULP_CLK_SPLL_PFD1] = imx_clk_hw_pfdv2("spll_pfd1", "spll", base + 0x60C, 1);
hws[IMX7ULP_CLK_SPLL_PFD2] = imx_clk_hw_pfdv2("spll_pfd2", "spll", base + 0x60C, 2);
hws[IMX7ULP_CLK_SPLL_PFD3] = imx_clk_hw_pfdv2("spll_pfd3", "spll", base + 0x60C, 3);
hws[IMX7ULP_CLK_SPLL_PFD0] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "spll_pfd0", "spll", base + 0x60C, 0);
hws[IMX7ULP_CLK_SPLL_PFD1] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "spll_pfd1", "spll", base + 0x60C, 1);
hws[IMX7ULP_CLK_SPLL_PFD2] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "spll_pfd2", "spll", base + 0x60C, 2);
hws[IMX7ULP_CLK_SPLL_PFD3] = imx_clk_hw_pfdv2(IMX_PFDV2_IMX7ULP, "spll_pfd3", "spll", base + 0x60C, 3);
/* PLL Mux */
hws[IMX7ULP_CLK_APLL_PFD_SEL] = imx_clk_hw_mux_flags("apll_pfd_sel", base + 0x508, 14, 2, apll_pfd_sels, ARRAY_SIZE(apll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE);
......
This diff is collapsed.
......@@ -161,8 +161,17 @@ static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate,
if (!rate)
return -EINVAL;
/* PFD can NOT change rate without gating */
WARN_ON(clk_pfdv2_is_enabled(hw));
/*
* PFD can NOT change rate without gating.
* as the PFDs may enabled in HW by default but no
* consumer used it, the enable count is '0', so the
* 'SET_RATE_GATE' can NOT help on blocking the set_rate
* ops especially for 'assigned-clock-xxx'. In order
* to simplify the case, just disable the PFD if it is
* enabled in HW but not in SW.
*/
if (clk_pfdv2_is_enabled(hw))
clk_pfdv2_disable(hw);
tmp = tmp * 18 + rate / 2;
do_div(tmp, rate);
......@@ -191,8 +200,8 @@ static const struct clk_ops clk_pfdv2_ops = {
.is_enabled = clk_pfdv2_is_enabled,
};
struct clk_hw *imx_clk_hw_pfdv2(const char *name, const char *parent_name,
void __iomem *reg, u8 idx)
struct clk_hw *imx_clk_hw_pfdv2(enum imx_pfdv2_type type, const char *name,
const char *parent_name, void __iomem *reg, u8 idx)
{
struct clk_init_data init;
struct clk_pfdv2 *pfd;
......@@ -214,7 +223,10 @@ struct clk_hw *imx_clk_hw_pfdv2(const char *name, const char *parent_name,
init.ops = &clk_pfdv2_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
if (type == IMX_PFDV2_IMX7ULP)
init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT;
else
init.flags = CLK_SET_RATE_GATE;
pfd->hw.init = &init;
......@@ -227,3 +239,4 @@ struct clk_hw *imx_clk_hw_pfdv2(const char *name, const char *parent_name,
return hw;
}
EXPORT_SYMBOL_GPL(imx_clk_hw_pfdv2);
......@@ -23,14 +23,17 @@
/* PLL Configuration Register (xPLLCFG) */
#define PLL_CFG_OFFSET 0x08
#define IMX8ULP_PLL_CFG_OFFSET 0x10
#define BP_PLL_MULT 16
#define BM_PLL_MULT (0x7f << 16)
/* PLL Numerator Register (xPLLNUM) */
#define PLL_NUM_OFFSET 0x10
#define IMX8ULP_PLL_NUM_OFFSET 0x1c
/* PLL Denominator Register (xPLLDENOM) */
#define PLL_DENOM_OFFSET 0x14
#define IMX8ULP_PLL_DENOM_OFFSET 0x18
#define MAX_MFD 0x3fffffff
#define DEFAULT_MFD 1000000
......@@ -38,6 +41,9 @@
struct clk_pllv4 {
struct clk_hw hw;
void __iomem *base;
u32 cfg_offset;
u32 num_offset;
u32 denom_offset;
};
/* Valid PLL MULT Table */
......@@ -72,12 +78,12 @@ static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
u32 mult, mfn, mfd;
u64 temp64;
mult = readl_relaxed(pll->base + PLL_CFG_OFFSET);
mult = readl_relaxed(pll->base + pll->cfg_offset);
mult &= BM_PLL_MULT;
mult >>= BP_PLL_MULT;
mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET);
mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET);
mfn = readl_relaxed(pll->base + pll->num_offset);
mfd = readl_relaxed(pll->base + pll->denom_offset);
temp64 = parent_rate;
temp64 *= mfn;
do_div(temp64, mfd);
......@@ -165,13 +171,13 @@ static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
do_div(temp64, parent_rate);
mfn = temp64;
val = readl_relaxed(pll->base + PLL_CFG_OFFSET);
val = readl_relaxed(pll->base + pll->cfg_offset);
val &= ~BM_PLL_MULT;
val |= mult << BP_PLL_MULT;
writel_relaxed(val, pll->base + PLL_CFG_OFFSET);
writel_relaxed(val, pll->base + pll->cfg_offset);
writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
writel_relaxed(mfn, pll->base + pll->num_offset);
writel_relaxed(mfd, pll->base + pll->denom_offset);
return 0;
}
......@@ -207,8 +213,8 @@ static const struct clk_ops clk_pllv4_ops = {
.is_prepared = clk_pllv4_is_prepared,
};
struct clk_hw *imx_clk_hw_pllv4(const char *name, const char *parent_name,
void __iomem *base)
struct clk_hw *imx_clk_hw_pllv4(enum imx_pllv4_type type, const char *name,
const char *parent_name, void __iomem *base)
{
struct clk_pllv4 *pll;
struct clk_hw *hw;
......@@ -221,6 +227,16 @@ struct clk_hw *imx_clk_hw_pllv4(const char *name, const char *parent_name,
pll->base = base;
if (type == IMX_PLLV4_IMX8ULP) {
pll->cfg_offset = IMX8ULP_PLL_CFG_OFFSET;
pll->num_offset = IMX8ULP_PLL_NUM_OFFSET;
pll->denom_offset = IMX8ULP_PLL_DENOM_OFFSET;
} else {
pll->cfg_offset = PLL_CFG_OFFSET;
pll->num_offset = PLL_NUM_OFFSET;
pll->denom_offset = PLL_DENOM_OFFSET;
}
init.name = name;
init.ops = &clk_pllv4_ops;
init.parent_names = &parent_name;
......@@ -238,3 +254,4 @@ struct clk_hw *imx_clk_hw_pllv4(const char *name, const char *parent_name,
return hw;
}
EXPORT_SYMBOL_GPL(imx_clk_hw_pllv4);
This diff is collapsed.
This diff is collapsed.
......@@ -80,5 +80,13 @@ obj-$(CONFIG_COMMON_CLK_MT8192_MSDC) += clk-mt8192-msdc.o
obj-$(CONFIG_COMMON_CLK_MT8192_SCP_ADSP) += clk-mt8192-scp_adsp.o
obj-$(CONFIG_COMMON_CLK_MT8192_VDECSYS) += clk-mt8192-vdec.o
obj-$(CONFIG_COMMON_CLK_MT8192_VENCSYS) += clk-mt8192-venc.o
obj-$(CONFIG_COMMON_CLK_MT8195) += clk-mt8195-apmixedsys.o clk-mt8195-topckgen.o \
clk-mt8195-peri_ao.o clk-mt8195-infra_ao.o \
clk-mt8195-cam.o clk-mt8195-ccu.o clk-mt8195-img.o \
clk-mt8195-ipe.o clk-mt8195-mfg.o clk-mt8195-scp_adsp.o \
clk-mt8195-vdec.o clk-mt8195-vdo0.o clk-mt8195-vdo1.o \
clk-mt8195-venc.o clk-mt8195-vpp0.o clk-mt8195-vpp1.o \
clk-mt8195-wpe.o clk-mt8195-imp_iic_wrap.o \
clk-mt8195-apusys_pll.o
obj-$(CONFIG_COMMON_CLK_MT8516) += clk-mt8516.o
obj-$(CONFIG_COMMON_CLK_MT8516_AUDSYS) += clk-mt8516-aud.o
......@@ -5,6 +5,7 @@
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/slab.h>
......@@ -97,3 +98,5 @@ struct clk * __init mtk_clk_register_ref2usb_tx(const char *name,
return clk;
}
MODULE_LICENSE("GPL");
......@@ -6,6 +6,7 @@
#include <linux/clk-provider.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "clk-mtk.h"
......@@ -106,3 +107,5 @@ int mtk_clk_register_cpumuxes(struct device_node *node,
return 0;
}
MODULE_LICENSE("GPL");
......@@ -11,6 +11,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/clkdev.h>
#include <linux/module.h>
#include "clk-mtk.h"
#include "clk-gate.h"
......@@ -122,24 +123,28 @@ const struct clk_ops mtk_clk_gate_ops_setclr = {
.enable = mtk_cg_enable,
.disable = mtk_cg_disable,
};
EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr);
const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
.is_enabled = mtk_cg_bit_is_set,
.enable = mtk_cg_enable_inv,
.disable = mtk_cg_disable_inv,
};
EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr_inv);
const struct clk_ops mtk_clk_gate_ops_no_setclr = {
.is_enabled = mtk_cg_bit_is_cleared,
.enable = mtk_cg_enable_no_setclr,
.disable = mtk_cg_disable_no_setclr,
};
EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr);
const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = {
.is_enabled = mtk_cg_bit_is_set,
.enable = mtk_cg_enable_inv_no_setclr,
.disable = mtk_cg_disable_inv_no_setclr,
};
EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr_inv);
struct clk *mtk_clk_register_gate(
const char *name,
......@@ -181,3 +186,6 @@ struct clk *mtk_clk_register_gate(
return clk;
}
EXPORT_SYMBOL_GPL(mtk_clk_register_gate);
MODULE_LICENSE("GPL");
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -202,6 +202,7 @@ void mtk_clk_register_dividers(const struct mtk_clk_divider *mcds,
struct clk_onecell_data *clk_data);
struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num);
void mtk_free_clk_data(struct clk_onecell_data *clk_data);
#define HAVE_RST_BAR BIT(0)
#define PLL_AO BIT(1)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment