This time around we have 4 lines of diff in the core framework, removing a
function that isn't used anymore. Otherwise the main new thing for the common clk framework is that it is selectable in the Kconfig language now. Hopefully this will let clk drivers and clk consumers be testable on more than the architectures that support the clk framework. The goal is to introduce some Kunit tests for the framework. Outside of the core framework we have the usual set of various driver updates and non-critical fixes. The dirstat shows that the new Baikal-T1 driver is the largest addition this time around in terms of lines of code. After that the x86 (Intel), Qualcomm, and Mediatek drivers introduce many lines to support new or upcoming SoCs. After that the dirstat shows the usual suspects working on their SoC support by fixing minor bugs, correcting data and converting some of their DT bindings to YAML. Core: - Allow the COMMON_CLK config to be selectable New Drivers: - Clk driver for Baikal-T1 SoCs - Mediatek MT6765 clock support - Support for Intel Agilex clks - Add support for X1830 and X1000 Ingenic SoC clk controllers - Add support for the new Renesas RZ/G1H (R8A7742) SoC - Add support for Qualcomm's MSM8939 Generic Clock Controller Updates: - Support IDT VersaClock 5P49V5925 - Bunch of updates for HSDK clock generation unit (CGU) driver - Start making audio and GPU clks work on Marvell MMP2/MMP3 SoCs - Add some GPU, NPU, and UFS clks to Qualcomm SM8150 driver - Enable supply regulators for GPU gdscs on Qualcomm SoCs - Add support for Si5342, Si5344 and Si5345 chips - Support custom flags in Xilinx zynq firmware - Various small fixes to the Xilinx clk driver - A single minor rounding fix for the legacy Allwinner clock support - A few patches from Abel Vesa as preparation of adding audiomix clock support on i.MX - A couple of cleanups from Anson Huang for i.MX clk-sscg-pll and clk-pllv3 drivers - Drop dependency on ARM64 for i.MX8M clock driver, to support aarch32 mode on aarch64 hardware - A series from Peng Fan to improve i.MX8M clock drivers, using composite clock for core and bus clk slice - Set a better parent clock for flexcan on i.MX6UL to support CiA102 defined bit rates - A couple changes for EMC frequency scaling on Tegra210 - Support for CPU frequency scaling on Tegra20/Tegra30 - New clk gate for CSI test pattern generator on Tegra210 - Regression fixes for Samsung exynos542x and exynos5433 SoCs - Use of fallthrough; attribute for Samsung s3c24xx - Updates and fixup HDMI and video clocks on Meson8b - Fixup reset polarity on Meson8b - Fix GPU glitch free mux switch on Meson gx and g12 - A minor fix for the currently unused suspend/resume handling on Renesas RZ/A1 and RZ/A2 - Two more conversions of Renesas DT bindings to json-schema - Add support for the USB 2.0 clock selector on Renesas R-Car M3-W+ -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEE9L57QeeUxqYDyoaDrQKIl8bklSUFAl7gEUgRHHNib3lkQGtl cm5lbC5vcmcACgkQrQKIl8bklSUemxAAlQKzx0yMS3yx5twJ4RSFUvf3hf4OqyPp O46soqADk+l69Z4SUUBsMjt8el5Sqmm4d1j1Gpfmgp3ZlumHCQK+qGYp48IXbwRP Jlo5sKNlNL6yhCd+ixPn4j7W/HbpGs4cciWOXkGQtYEGjhHm3Wllhd9MqpL2YjLx gZW60NqWtOe1XeB4ILyYQGisNwAGDi5XuBeNvxG12H/LaGC1mwtBX9yoNAehr9bF peJ2XnO02zFo73OCyzIOkw1uY4u7ZtwPdHGhymoGeVlcBWO6KwKesNkHnji/Grlv wMbsGLoRV/i3PL3q5kZIDigo8sqZ9RUG+9piRAoiLM5AgkSypw3/q9T+ujTfZp8t kgvFha6bLZz31UFmr4lBJPTT5Q/hAoe1W6RB6HZkx7XNqUpsAS04SwkQztAqkJqZ 9zlYJrXgLlP5qcNllJ6zvUWkMqtmIKW4ZkjYe4u84yk5Co7bX8DCYa+QOKCz+pV4 IbjRT62OrX2ZlXJYwkLb4m1nhZ7tBzhzIRP1umL0ukhxdomK6ofSNPzbBF9+t1eR /ai2/Ch6L6WIwDINEp+chO67/dJaj5W3WNqGMCmVt37myW1kBjH3eg0YG4cp7NYZ /jSjdWczQy/8BgY5V1009MRXI4uyazQxBw+apDcIGezamOKBmuwjBcvkf1D0mL2x Y6OclK5ljsw= =nuG5 -----END PGP SIGNATURE----- Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux Pull clk updates from Stephen Boyd: "This time around we have four lines of diff in the core framework, removing a function that isn't used anymore. Otherwise the main new thing for the common clk framework is that it is selectable in the Kconfig language now. Hopefully this will let clk drivers and clk consumers be testable on more than the architectures that support the clk framework. The goal is to introduce some Kunit tests for the framework. Outside of the core framework we have the usual set of various driver updates and non-critical fixes. The dirstat shows that the new Baikal-T1 driver is the largest addition this time around in terms of lines of code. After that the x86 (Intel), Qualcomm, and Mediatek drivers introduce many lines to support new or upcoming SoCs. After that the dirstat shows the usual suspects working on their SoC support by fixing minor bugs, correcting data and converting some of their DT bindings to YAML. Core: - Allow the COMMON_CLK config to be selectable New Drivers: - Clk driver for Baikal-T1 SoCs - Mediatek MT6765 clock support - Support for Intel Agilex clks - Add support for X1830 and X1000 Ingenic SoC clk controllers - Add support for the new Renesas RZ/G1H (R8A7742) SoC - Add support for Qualcomm's MSM8939 Generic Clock Controller Updates: - Support IDT VersaClock 5P49V5925 - Bunch of updates for HSDK clock generation unit (CGU) driver - Start making audio and GPU clks work on Marvell MMP2/MMP3 SoCs - Add some GPU, NPU, and UFS clks to Qualcomm SM8150 driver - Enable supply regulators for GPU gdscs on Qualcomm SoCs - Add support for Si5342, Si5344 and Si5345 chips - Support custom flags in Xilinx zynq firmware - Various small fixes to the Xilinx clk driver - A single minor rounding fix for the legacy Allwinner clock support - A few patches from Abel Vesa as preparation of adding audiomix clock support on i.MX - A couple of cleanups from Anson Huang for i.MX clk-sscg-pll and clk-pllv3 drivers - Drop dependency on ARM64 for i.MX8M clock driver, to support aarch32 mode on aarch64 hardware - A series from Peng Fan to improve i.MX8M clock drivers, using composite clock for core and bus clk slice - Set a better parent clock for flexcan on i.MX6UL to support CiA102 defined bit rates - A couple changes for EMC frequency scaling on Tegra210 - Support for CPU frequency scaling on Tegra20/Tegra30 - New clk gate for CSI test pattern generator on Tegra210 - Regression fixes for Samsung exynos542x and exynos5433 SoCs - Use of fallthrough; attribute for Samsung s3c24xx - Updates and fixup HDMI and video clocks on Meson8b - Fixup reset polarity on Meson8b - Fix GPU glitch free mux switch on Meson gx and g12 - A minor fix for the currently unused suspend/resume handling on Renesas RZ/A1 and RZ/A2 - Two more conversions of Renesas DT bindings to json-schema - Add support for the USB 2.0 clock selector on Renesas R-Car M3-W+" * tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (155 commits) clk: mediatek: Remove ifr{0,1}_cfg_regs structures clk: baikal-t1: remove redundant assignment to variable 'divider' clk: baikal-t1: fix spelling mistake "Uncompatible" -> "Incompatible" dt-bindings: clock: Add a missing include to MMP Audio Clock binding dt: Add bindings for IDT VersaClock 5P49V5925 clk: vc5: Add support for IDT VersaClock 5P49V6965 clk: Add Baikal-T1 CCU Dividers driver clk: Add Baikal-T1 CCU PLLs driver dt-bindings: clk: Add Baikal-T1 CCU Dividers binding dt-bindings: clk: Add Baikal-T1 CCU PLLs binding clk: mediatek: assign the initial value to clk_init_data of mtk_mux clk: mediatek: Add MT6765 clock support clk: mediatek: add mt6765 clock IDs dt-bindings: clock: mediatek: document clk bindings vcodecsys for Mediatek MT6765 SoC dt-bindings: clock: mediatek: document clk bindings mipi0a for Mediatek MT6765 SoC dt-bindings: clock: mediatek: document clk bindings for Mediatek MT6765 SoC CLK: HSDK: CGU: add support for 148.5MHz clock CLK: HSDK: CGU: support PLL bypassing CLK: HSDK: CGU: check if PLL is bypassed first clk: clk-si5341: Add support for the Si5345 series ...
This commit is contained in:
commit
6f630784cc
@ -8,6 +8,7 @@ Required Properties:
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt2701-apmixedsys"
|
||||
- "mediatek,mt2712-apmixedsys", "syscon"
|
||||
- "mediatek,mt6765-apmixedsys", "syscon"
|
||||
- "mediatek,mt6779-apmixedsys", "syscon"
|
||||
- "mediatek,mt6797-apmixedsys"
|
||||
- "mediatek,mt7622-apmixedsys"
|
||||
|
@ -7,6 +7,7 @@ Required Properties:
|
||||
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt2701-audsys", "syscon"
|
||||
- "mediatek,mt6765-audsys", "syscon"
|
||||
- "mediatek,mt6779-audio", "syscon"
|
||||
- "mediatek,mt7622-audsys", "syscon"
|
||||
- "mediatek,mt7623-audsys", "mediatek,mt2701-audsys", "syscon"
|
||||
|
@ -6,6 +6,7 @@ The MediaTek camsys controller provides various clocks to the system.
|
||||
Required Properties:
|
||||
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt6765-camsys", "syscon"
|
||||
- "mediatek,mt6779-camsys", "syscon"
|
||||
- "mediatek,mt8183-camsys", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
@ -8,6 +8,7 @@ Required Properties:
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt2701-imgsys", "syscon"
|
||||
- "mediatek,mt2712-imgsys", "syscon"
|
||||
- "mediatek,mt6765-imgsys", "syscon"
|
||||
- "mediatek,mt6779-imgsys", "syscon"
|
||||
- "mediatek,mt6797-imgsys", "syscon"
|
||||
- "mediatek,mt7623-imgsys", "mediatek,mt2701-imgsys", "syscon"
|
||||
|
@ -9,6 +9,7 @@ Required Properties:
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt2701-infracfg", "syscon"
|
||||
- "mediatek,mt2712-infracfg", "syscon"
|
||||
- "mediatek,mt6765-infracfg", "syscon"
|
||||
- "mediatek,mt6779-infracfg_ao", "syscon"
|
||||
- "mediatek,mt6797-infracfg", "syscon"
|
||||
- "mediatek,mt7622-infracfg", "syscon"
|
||||
|
@ -0,0 +1,28 @@
|
||||
Mediatek mipi0a (mipi_rx_ana_csi0a) controller
|
||||
============================
|
||||
|
||||
The Mediatek mipi0a controller provides various clocks
|
||||
to the system.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt6765-mipi0a", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The mipi0a controller uses the common clk binding from
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
The available clocks are defined in dt-bindings/clock/mt*-clk.h.
|
||||
|
||||
The mipi0a controller also uses the common power domain from
|
||||
Documentation/devicetree/bindings/soc/mediatek/scpsys.txt
|
||||
The available power doamins are defined in dt-bindings/power/mt*-power.h.
|
||||
|
||||
Example:
|
||||
|
||||
mipi0a: clock-controller@11c10000 {
|
||||
compatible = "mediatek,mt6765-mipi0a", "syscon";
|
||||
reg = <0 0x11c10000 0 0x1000>;
|
||||
power-domains = <&scpsys MT6765_POWER_DOMAIN_CAM>;
|
||||
#clock-cells = <1>;
|
||||
};
|
@ -9,6 +9,7 @@ Required Properties:
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt2701-mmsys", "syscon"
|
||||
- "mediatek,mt2712-mmsys", "syscon"
|
||||
- "mediatek,mt6765-mmsys", "syscon"
|
||||
- "mediatek,mt6779-mmsys", "syscon"
|
||||
- "mediatek,mt6797-mmsys", "syscon"
|
||||
- "mediatek,mt7623-mmsys", "mediatek,mt2701-mmsys", "syscon"
|
||||
|
@ -20,6 +20,7 @@ properties:
|
||||
- enum:
|
||||
- mediatek,mt2701-pericfg
|
||||
- mediatek,mt2712-pericfg
|
||||
- mediatek,mt6765-pericfg
|
||||
- mediatek,mt7622-pericfg
|
||||
- mediatek,mt7629-pericfg
|
||||
- mediatek,mt8135-pericfg
|
||||
|
@ -8,6 +8,7 @@ Required Properties:
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt2701-topckgen"
|
||||
- "mediatek,mt2712-topckgen", "syscon"
|
||||
- "mediatek,mt6765-topckgen", "syscon"
|
||||
- "mediatek,mt6779-topckgen", "syscon"
|
||||
- "mediatek,mt6797-topckgen"
|
||||
- "mediatek,mt7622-topckgen"
|
||||
|
@ -0,0 +1,27 @@
|
||||
Mediatek vcodecsys controller
|
||||
============================
|
||||
|
||||
The Mediatek vcodecsys controller provides various clocks to the system.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt6765-vcodecsys", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The vcodecsys controller uses the common clk binding from
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
The available clocks are defined in dt-bindings/clock/mt*-clk.h.
|
||||
|
||||
The vcodecsys controller also uses the common power domain from
|
||||
Documentation/devicetree/bindings/soc/mediatek/scpsys.txt
|
||||
The available power doamins are defined in dt-bindings/power/mt*-power.h.
|
||||
|
||||
Example:
|
||||
|
||||
venc_gcon: clock-controller@17000000 {
|
||||
compatible = "mediatek,mt6765-vcodecsys", "syscon";
|
||||
reg = <0 0x17000000 0 0x10000>;
|
||||
power-domains = <&scpsys MT6765_POWER_DOMAIN_VCODEC>;
|
||||
#clock-cells = <1>;
|
||||
};
|
188
Documentation/devicetree/bindings/clock/baikal,bt1-ccu-div.yaml
Normal file
188
Documentation/devicetree/bindings/clock/baikal,bt1-ccu-div.yaml
Normal file
@ -0,0 +1,188 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/baikal,bt1-ccu-div.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Baikal-T1 Clock Control Unit Dividers
|
||||
|
||||
maintainers:
|
||||
- Serge Semin <fancer.lancer@gmail.com>
|
||||
|
||||
description: |
|
||||
Clocks Control Unit is the core of Baikal-T1 SoC System Controller
|
||||
responsible for the chip subsystems clocking and resetting. The CCU is
|
||||
connected with an external fixed rate oscillator, which signal is transformed
|
||||
into clocks of various frequencies and then propagated to either individual
|
||||
IP-blocks or to groups of blocks (clock domains). The transformation is done
|
||||
by means of an embedded into CCU PLLs and gateable/non-gateable dividers. The
|
||||
later ones are described in this binding. Each clock domain can be also
|
||||
individually reset by using the domain clocks divider configuration
|
||||
registers. Baikal-T1 CCU is logically divided into the next components:
|
||||
1) External oscillator (normally XTAL's 25 MHz crystal oscillator, but
|
||||
in general can provide any frequency supported by the CCU PLLs).
|
||||
2) PLLs clocks generators (PLLs).
|
||||
3) AXI-bus clock dividers (AXI) - described in this binding file.
|
||||
4) System devices reference clock dividers (SYS) - described in this binding
|
||||
file.
|
||||
which are connected with each other as shown on the next figure:
|
||||
|
||||
+---------------+
|
||||
| Baikal-T1 CCU |
|
||||
| +----+------|- MIPS P5600 cores
|
||||
| +-|PLLs|------|- DDR controller
|
||||
| | +----+ |
|
||||
+----+ | | | | |
|
||||
|XTAL|--|-+ | | +---+-|
|
||||
+----+ | | | +-|AXI|-|- AXI-bus
|
||||
| | | +---+-|
|
||||
| | | |
|
||||
| | +----+---+-|- APB-bus
|
||||
| +-------|SYS|-|- Low-speed Devices
|
||||
| +---+-|- High-speed Devices
|
||||
+---------------+
|
||||
|
||||
Each sub-block is represented as a separate DT node and has an individual
|
||||
driver to be bound with.
|
||||
|
||||
In order to create signals of wide range frequencies the external oscillator
|
||||
output is primarily connected to a set of CCU PLLs. Some of PLLs CLKOUT are
|
||||
then passed over CCU dividers to create signals required for the target clock
|
||||
domain (like AXI-bus or System Device consumers). The dividers have the
|
||||
following structure:
|
||||
|
||||
+--------------+
|
||||
CLKIN --|->+----+ 1|\ |
|
||||
SETCLK--|--|/DIV|->| | |
|
||||
CLKDIV--|--| | | |-|->CLKLOUT
|
||||
LOCK----|--+----+ | | |
|
||||
| |/ |
|
||||
| | |
|
||||
EN------|-----------+ |
|
||||
RST-----|--------------|->RSTOUT
|
||||
+--------------+
|
||||
|
||||
where CLKIN is the reference clock coming either from CCU PLLs or from an
|
||||
external clock oscillator, SETCLK - a command to update the output clock in
|
||||
accordance with a set divider, CLKDIV - clocks divider, LOCK - a signal of
|
||||
the output clock stabilization, EN - enable/disable the divider block,
|
||||
RST/RSTOUT - reset clocks domain signal. Depending on the consumer IP-core
|
||||
peculiarities the dividers may lack of some functionality depicted on the
|
||||
figure above (like EN, CLKDIV/LOCK/SETCLK). In this case the corresponding
|
||||
clock provider just doesn't expose either switching functions, or the rate
|
||||
configuration, or both of them.
|
||||
|
||||
The clock dividers, which output clock is then consumed by the SoC individual
|
||||
devices, are united into a single clocks provider called System Devices CCU.
|
||||
Similarly the dividers with output clocks utilized as AXI-bus reference clocks
|
||||
are called AXI-bus CCU. Both of them use the common clock bindings with no
|
||||
custom properties. The list of exported clocks and reset signals can be found
|
||||
in the files: 'include/dt-bindings/clock/bt1-ccu.h' and
|
||||
'include/dt-bindings/reset/bt1-ccu.h'. Since System Devices and AXI-bus CCU
|
||||
are a part of the Baikal-T1 SoC System Controller their DT nodes are supposed
|
||||
to be a children of later one.
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: baikal,bt1-ccu-axi
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: CCU SATA PLL output clock
|
||||
- description: CCU PCIe PLL output clock
|
||||
- description: CCU Ethernet PLL output clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: sata_clk
|
||||
- const: pcie_clk
|
||||
- const: eth_clk
|
||||
|
||||
else:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: External reference clock
|
||||
- description: CCU SATA PLL output clock
|
||||
- description: CCU PCIe PLL output clock
|
||||
- description: CCU Ethernet PLL output clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ref_clk
|
||||
- const: sata_clk
|
||||
- const: pcie_clk
|
||||
- const: eth_clk
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- baikal,bt1-ccu-axi
|
||||
- baikal,bt1-ccu-sys
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#clock-cells":
|
||||
const: 1
|
||||
|
||||
"#reset-cells":
|
||||
const: 1
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- "#clock-cells"
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
examples:
|
||||
# AXI-bus Clock Control Unit node:
|
||||
- |
|
||||
#include <dt-bindings/clock/bt1-ccu.h>
|
||||
|
||||
clock-controller@1f04d030 {
|
||||
compatible = "baikal,bt1-ccu-axi";
|
||||
reg = <0x1f04d030 0x030>;
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
|
||||
clocks = <&ccu_pll CCU_SATA_PLL>,
|
||||
<&ccu_pll CCU_PCIE_PLL>,
|
||||
<&ccu_pll CCU_ETH_PLL>;
|
||||
clock-names = "sata_clk", "pcie_clk", "eth_clk";
|
||||
};
|
||||
# System Devices Clock Control Unit node:
|
||||
- |
|
||||
#include <dt-bindings/clock/bt1-ccu.h>
|
||||
|
||||
clock-controller@1f04d060 {
|
||||
compatible = "baikal,bt1-ccu-sys";
|
||||
reg = <0x1f04d060 0x0a0>;
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
|
||||
clocks = <&clk25m>,
|
||||
<&ccu_pll CCU_SATA_PLL>,
|
||||
<&ccu_pll CCU_PCIE_PLL>,
|
||||
<&ccu_pll CCU_ETH_PLL>;
|
||||
clock-names = "ref_clk", "sata_clk", "pcie_clk",
|
||||
"eth_clk";
|
||||
};
|
||||
# Required Clock Control Unit PLL node:
|
||||
- |
|
||||
ccu_pll: clock-controller@1f04d000 {
|
||||
compatible = "baikal,bt1-ccu-pll";
|
||||
reg = <0x1f04d000 0x028>;
|
||||
#clock-cells = <1>;
|
||||
|
||||
clocks = <&clk25m>;
|
||||
clock-names = "ref_clk";
|
||||
};
|
||||
...
|
131
Documentation/devicetree/bindings/clock/baikal,bt1-ccu-pll.yaml
Normal file
131
Documentation/devicetree/bindings/clock/baikal,bt1-ccu-pll.yaml
Normal file
@ -0,0 +1,131 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/baikal,bt1-ccu-pll.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Baikal-T1 Clock Control Unit PLL
|
||||
|
||||
maintainers:
|
||||
- Serge Semin <fancer.lancer@gmail.com>
|
||||
|
||||
description: |
|
||||
Clocks Control Unit is the core of Baikal-T1 SoC System Controller
|
||||
responsible for the chip subsystems clocking and resetting. The CCU is
|
||||
connected with an external fixed rate oscillator, which signal is transformed
|
||||
into clocks of various frequencies and then propagated to either individual
|
||||
IP-blocks or to groups of blocks (clock domains). The transformation is done
|
||||
by means of PLLs and gateable/non-gateable dividers embedded into the CCU.
|
||||
It's logically divided into the next components:
|
||||
1) External oscillator (normally XTAL's 25 MHz crystal oscillator, but
|
||||
in general can provide any frequency supported by the CCU PLLs).
|
||||
2) PLLs clocks generators (PLLs) - described in this binding file.
|
||||
3) AXI-bus clock dividers (AXI).
|
||||
4) System devices reference clock dividers (SYS).
|
||||
which are connected with each other as shown on the next figure:
|
||||
|
||||
+---------------+
|
||||
| Baikal-T1 CCU |
|
||||
| +----+------|- MIPS P5600 cores
|
||||
| +-|PLLs|------|- DDR controller
|
||||
| | +----+ |
|
||||
+----+ | | | | |
|
||||
|XTAL|--|-+ | | +---+-|
|
||||
+----+ | | | +-|AXI|-|- AXI-bus
|
||||
| | | +---+-|
|
||||
| | | |
|
||||
| | +----+---+-|- APB-bus
|
||||
| +-------|SYS|-|- Low-speed Devices
|
||||
| +---+-|- High-speed Devices
|
||||
+---------------+
|
||||
|
||||
Each CCU sub-block is represented as a separate dts-node and has an
|
||||
individual driver to be bound with.
|
||||
|
||||
In order to create signals of wide range frequencies the external oscillator
|
||||
output is primarily connected to a set of CCU PLLs. There are five PLLs
|
||||
to create a clock for the MIPS P5600 cores, the embedded DDR controller,
|
||||
SATA, Ethernet and PCIe domains. The last three domains though named by the
|
||||
biggest system interfaces in fact include nearly all of the rest SoC
|
||||
peripherals. Each of the PLLs is based on True Circuits TSMC CLN28HPM core
|
||||
with an interface wrapper (so called safe PLL' clocks switcher) to simplify
|
||||
the PLL configuration procedure. The PLLs work as depicted on the next
|
||||
diagram:
|
||||
|
||||
+--------------------------+
|
||||
| |
|
||||
+-->+---+ +---+ +---+ | +---+ 0|\
|
||||
CLKF--->|/NF|--->|PFD|...|VCO|-+->|/OD|--->| |
|
||||
+---+ +->+---+ +---+ /->+---+ | |--->CLKOUT
|
||||
CLKOD---------C----------------+ 1| |
|
||||
+--------C--------------------------->|/
|
||||
| | ^
|
||||
Rclk-+->+---+ | |
|
||||
CLKR--->|/NR|-+ |
|
||||
+---+ |
|
||||
BYPASS--------------------------------------+
|
||||
BWADJ--->
|
||||
|
||||
where Rclk is the reference clock coming from XTAL, NR - reference clock
|
||||
divider, NF - PLL clock multiplier, OD - VCO output clock divider, CLKOUT -
|
||||
output clock, BWADJ is the PLL bandwidth adjustment parameter. At this moment
|
||||
the binding supports the PLL dividers configuration in accordance with a
|
||||
requested rate, while bypassing and bandwidth adjustment settings can be
|
||||
added in future if it gets to be necessary.
|
||||
|
||||
The PLLs CLKOUT is then either directly connected with the corresponding
|
||||
clocks consumer (like P5600 cores or DDR controller) or passed over a CCU
|
||||
divider to create a signal required for the clock domain.
|
||||
|
||||
The CCU PLL dts-node uses the common clock bindings with no custom
|
||||
parameters. The list of exported clocks can be found in
|
||||
'include/dt-bindings/clock/bt1-ccu.h'. Since CCU PLL is a part of the
|
||||
Baikal-T1 SoC System Controller its DT node is supposed to be a child of
|
||||
later one.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: baikal,bt1-ccu-pll
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#clock-cells":
|
||||
const: 1
|
||||
|
||||
clocks:
|
||||
description: External reference clock
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: ref_clk
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- "#clock-cells"
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
examples:
|
||||
# Clock Control Unit PLL node:
|
||||
- |
|
||||
clock-controller@1f04d000 {
|
||||
compatible = "baikal,bt1-ccu-pll";
|
||||
reg = <0x1f04d000 0x028>;
|
||||
#clock-cells = <1>;
|
||||
|
||||
clocks = <&clk25m>;
|
||||
clock-names = "ref_clk";
|
||||
};
|
||||
# Required external oscillator:
|
||||
- |
|
||||
clk25m: clock-oscillator-25m {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <25000000>;
|
||||
clock-output-names = "clk25m";
|
||||
};
|
||||
...
|
@ -12,6 +12,7 @@ Required properties:
|
||||
"idt,5p49v5933"
|
||||
"idt,5p49v5935"
|
||||
"idt,5p49v6901"
|
||||
"idt,5p49v6965"
|
||||
- reg: i2c device address, shall be 0x68 or 0x6a.
|
||||
- #clock-cells: from common clock binding; shall be set to 1.
|
||||
- clocks: from common clock binding; list of parent clock handles,
|
||||
|
46
Documentation/devicetree/bindings/clock/intel,agilex.yaml
Normal file
46
Documentation/devicetree/bindings/clock/intel,agilex.yaml
Normal file
@ -0,0 +1,46 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/intel,agilex.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Intel SoCFPGA Agilex platform clock controller binding
|
||||
|
||||
maintainers:
|
||||
- Dinh Nguyen <dinguyen@kernel.org>
|
||||
|
||||
description:
|
||||
The Intel Agilex Clock controller is an integrated clock controller, which
|
||||
generates and supplies to all modules.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: intel,agilex-clkmgr
|
||||
|
||||
'#clock-cells':
|
||||
const: 1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- '#clock-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
# Clock controller node:
|
||||
- |
|
||||
clkmgr: clock-controller@ffd10000 {
|
||||
compatible = "intel,agilex-clkmgr";
|
||||
reg = <0xffd10000 0x1000>;
|
||||
clocks = <&osc1>;
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
...
|
44
Documentation/devicetree/bindings/clock/intel,cgu-lgm.yaml
Normal file
44
Documentation/devicetree/bindings/clock/intel,cgu-lgm.yaml
Normal file
@ -0,0 +1,44 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/intel,cgu-lgm.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Intel Lightning Mountain SoC's Clock Controller(CGU) Binding
|
||||
|
||||
maintainers:
|
||||
- Rahul Tanwar <rahul.tanwar@linux.intel.com>
|
||||
|
||||
description: |
|
||||
Lightning Mountain(LGM) SoC's Clock Generation Unit(CGU) driver provides
|
||||
all means to access the CGU hardware module in order to generate a series
|
||||
of clocks for the whole system and individual peripherals.
|
||||
|
||||
Please refer to include/dt-bindings/clock/intel,lgm-clk.h header file, it
|
||||
defines all available clocks as macros. These macros can be used in device
|
||||
tree sources.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: intel,cgu-lgm
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
'#clock-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- '#clock-cells'
|
||||
|
||||
examples:
|
||||
- |
|
||||
cgu: clock-controller@e0200000 {
|
||||
compatible = "intel,cgu-lgm";
|
||||
reg = <0xe0200000 0x33c>;
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
|
||||
...
|
@ -0,0 +1,75 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0+ OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/marvell,mmp2-audio-clock.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Marvell MMP2 Audio Clock Controller
|
||||
|
||||
maintainers:
|
||||
- Lubomir Rintel <lkundrak@v3.sk>
|
||||
|
||||
description: |
|
||||
The audio clock controller generates and supplies the clocks to the audio
|
||||
codec.
|
||||
|
||||
Each clock is assigned an identifier and client nodes use this identifier
|
||||
to specify the clock which they consume.
|
||||
|
||||
All these identifiers could be found in
|
||||
<dt-bindings/clock/marvell,mmp2-audio.h>.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- marvell,mmp2-audio-clock
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Audio subsystem clock
|
||||
- description: The crystal oscillator clock
|
||||
- description: First I2S clock
|
||||
- description: Second I2S clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: audio
|
||||
- const: vctcxo
|
||||
- const: i2s0
|
||||
- const: i2s1
|
||||
|
||||
'#clock-cells':
|
||||
const: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- '#clock-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/marvell,mmp2-audio.h>
|
||||
#include <dt-bindings/clock/marvell,mmp2.h>
|
||||
#include <dt-bindings/power/marvell,mmp2.h>
|
||||
|
||||
clock-controller@d42a0c30 {
|
||||
compatible = "marvell,mmp2-audio-clock";
|
||||
reg = <0xd42a0c30 0x10>;
|
||||
clock-names = "audio", "vctcxo", "i2s0", "i2s1";
|
||||
clocks = <&soc_clocks MMP2_CLK_AUDIO>,
|
||||
<&soc_clocks MMP2_CLK_VCTCXO>,
|
||||
<&soc_clocks MMP2_CLK_I2S0>,
|
||||
<&soc_clocks MMP2_CLK_I2S1>;
|
||||
power-domains = <&soc_clocks MMP2_POWER_DOMAIN_AUDIO>;
|
||||
#clock-cells = <1>;
|
||||
};
|
@ -42,12 +42,16 @@ properties:
|
||||
'#reset-cells':
|
||||
const: 1
|
||||
|
||||
'#power-domain-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- '#clock-cells'
|
||||
- '#reset-cells'
|
||||
- '#power-domain-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
@ -61,4 +65,5 @@ examples:
|
||||
reg-names = "mpmu", "apmu", "apbc";
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
#power-domain-cells = <1>;
|
||||
};
|
||||
|
@ -1,22 +0,0 @@
|
||||
Qualcomm MSM8916 A53 PLL Binding
|
||||
--------------------------------
|
||||
The A53 PLL on MSM8916 platforms is the main CPU PLL used used for frequencies
|
||||
above 1GHz.
|
||||
|
||||
Required properties :
|
||||
- compatible : Shall contain only one of the following:
|
||||
|
||||
"qcom,msm8916-a53pll"
|
||||
|
||||
- reg : shall contain base register location and length
|
||||
|
||||
- #clock-cells : must be set to <0>
|
||||
|
||||
Example:
|
||||
|
||||
a53pll: clock@b016000 {
|
||||
compatible = "qcom,msm8916-a53pll";
|
||||
reg = <0xb016000 0x40>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
40
Documentation/devicetree/bindings/clock/qcom,a53pll.yaml
Normal file
40
Documentation/devicetree/bindings/clock/qcom,a53pll.yaml
Normal file
@ -0,0 +1,40 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/qcom,a53pll.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm A53 PLL Binding
|
||||
|
||||
maintainers:
|
||||
- Sivaprakash Murugesan <sivaprak@codeaurora.org>
|
||||
|
||||
description:
|
||||
The A53 PLL on few Qualcomm platforms is the main CPU PLL used used for
|
||||
frequencies above 1GHz.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,msm8916-a53pll
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
'#clock-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- '#clock-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
#Example 1 - A53 PLL found on MSM8916 devices
|
||||
- |
|
||||
a53pll: clock@b016000 {
|
||||
compatible = "qcom,msm8916-a53pll";
|
||||
reg = <0xb016000 0x40>;
|
||||
#clock-cells = <0>;
|
||||
};
|
@ -22,6 +22,8 @@ description: |
|
||||
- dt-bindings/reset/qcom,gcc-ipq6018.h
|
||||
- dt-bindings/clock/qcom,gcc-ipq806x.h (qcom,gcc-ipq8064)
|
||||
- dt-bindings/reset/qcom,gcc-ipq806x.h (qcom,gcc-ipq8064)
|
||||
- dt-bindings/clock/qcom,gcc-msm8939.h
|
||||
- dt-bindings/reset/qcom,gcc-msm8939.h
|
||||
- dt-bindings/clock/qcom,gcc-msm8660.h
|
||||
- dt-bindings/reset/qcom,gcc-msm8660.h
|
||||
- dt-bindings/clock/qcom,gcc-msm8974.h
|
||||
@ -41,6 +43,7 @@ properties:
|
||||
- qcom,gcc-ipq8064
|
||||
- qcom,gcc-msm8660
|
||||
- qcom,gcc-msm8916
|
||||
- qcom,gcc-msm8939
|
||||
- qcom,gcc-msm8960
|
||||
- qcom,gcc-msm8974
|
||||
- qcom,gcc-msm8974pro
|
||||
|
@ -67,6 +67,10 @@ properties:
|
||||
description:
|
||||
Protected clock specifier list as per common clock binding
|
||||
|
||||
vdd-gfx-supply:
|
||||
description:
|
||||
Regulator supply for the GPU_GX GDSC
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -0,0 +1,60 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/renesas,cpg-div6-clock.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas CPG DIV6 Clock
|
||||
|
||||
maintainers:
|
||||
- Geert Uytterhoeven <geert+renesas@glider.be>
|
||||
|
||||
description:
|
||||
The CPG DIV6 clocks are variable factor clocks provided by the Clock Pulse
|
||||
Generator (CPG). Their clock input is divided by a configurable factor from 1
|
||||
to 64.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,r8a73a4-div6-clock # R-Mobile APE6
|
||||
- renesas,r8a7740-div6-clock # R-Mobile A1
|
||||
- renesas,sh73a0-div6-clock # SH-Mobile AG5
|
||||
- const: renesas,cpg-div6-clock
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
oneOf:
|
||||
- maxItems: 1
|
||||
- maxItems: 4
|
||||
- maxItems: 8
|
||||
description:
|
||||
For clocks with multiple parents, invalid settings must be specified as
|
||||
"<0>".
|
||||
|
||||
'#clock-cells':
|
||||
const: 0
|
||||
|
||||
clock-output-names: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- '#clock-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/r8a73a4-clock.h>
|
||||
sdhi2_clk: sdhi2_clk@e615007c {
|
||||
compatible = "renesas,r8a73a4-div6-clock", "renesas,cpg-div6-clock";
|
||||
reg = <0xe615007c 4>;
|
||||
clocks = <&pll1_div2_clk>, <&cpg_clocks R8A73A4_CLK_PLL2S>, <0>,
|
||||
<&extal2_clk>;
|
||||
#clock-cells = <0>;
|
||||
};
|
@ -1,40 +0,0 @@
|
||||
* Renesas CPG DIV6 Clock
|
||||
|
||||
The CPG DIV6 clocks are variable factor clocks provided by the Clock Pulse
|
||||
Generator (CPG). Their clock input is divided by a configurable factor from 1
|
||||
to 64.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Must be one of the following
|
||||
- "renesas,r8a73a4-div6-clock" for R8A73A4 (R-Mobile APE6) DIV6 clocks
|
||||
- "renesas,r8a7740-div6-clock" for R8A7740 (R-Mobile A1) DIV6 clocks
|
||||
- "renesas,r8a7790-div6-clock" for R8A7790 (R-Car H2) DIV6 clocks
|
||||
- "renesas,r8a7791-div6-clock" for R8A7791 (R-Car M2-W) DIV6 clocks
|
||||
- "renesas,r8a7793-div6-clock" for R8A7793 (R-Car M2-N) DIV6 clocks
|
||||
- "renesas,r8a7794-div6-clock" for R8A7794 (R-Car E2) DIV6 clocks
|
||||
- "renesas,sh73a0-div6-clock" for SH73A0 (SH-Mobile AG5) DIV6 clocks
|
||||
and "renesas,cpg-div6-clock" as a fallback.
|
||||
- reg: Base address and length of the memory resource used by the DIV6 clock
|
||||
- clocks: Reference to the parent clock(s); either one, four, or eight
|
||||
clocks must be specified. For clocks with multiple parents, invalid
|
||||
settings must be specified as "<0>".
|
||||
- #clock-cells: Must be 0
|
||||
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- clock-output-names: The name of the clock as a free-form string
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
sdhi2_clk: sdhi2_clk@e615007c {
|
||||
compatible = "renesas,r8a73a4-div6-clock", "renesas,cpg-div6-clock";
|
||||
reg = <0 0xe615007c 0 4>;
|
||||
clocks = <&pll1_div2_clk>, <&cpg_clocks R8A73A4_CLK_PLL2S>,
|
||||
<0>, <&extal2_clk>;
|
||||
#clock-cells = <0>;
|
||||
clock-output-names = "sdhi2ck";
|
||||
};
|
@ -25,6 +25,7 @@ properties:
|
||||
compatible:
|
||||
enum:
|
||||
- renesas,r7s9210-cpg-mssr # RZ/A2
|
||||
- renesas,r8a7742-cpg-mssr # RZ/G1H
|
||||
- renesas,r8a7743-cpg-mssr # RZ/G1M
|
||||
- renesas,r8a7744-cpg-mssr # RZ/G1N
|
||||
- renesas,r8a7745-cpg-mssr # RZ/G1E
|
||||
|
@ -1,60 +0,0 @@
|
||||
* Renesas CPG Module Stop (MSTP) Clocks
|
||||
|
||||
The CPG can gate SoC device clocks. The gates are organized in groups of up to
|
||||
32 gates.
|
||||
|
||||
This device tree binding describes a single 32 gate clocks group per node.
|
||||
Clocks are referenced by user nodes by the MSTP node phandle and the clock
|
||||
index in the group, from 0 to 31.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Must be one of the following
|
||||
- "renesas,r7s72100-mstp-clocks" for R7S72100 (RZ) MSTP gate clocks
|
||||
- "renesas,r8a73a4-mstp-clocks" for R8A73A4 (R-Mobile APE6) MSTP gate clocks
|
||||
- "renesas,r8a7740-mstp-clocks" for R8A7740 (R-Mobile A1) MSTP gate clocks
|
||||
- "renesas,r8a7778-mstp-clocks" for R8A7778 (R-Car M1) MSTP gate clocks
|
||||
- "renesas,r8a7779-mstp-clocks" for R8A7779 (R-Car H1) MSTP gate clocks
|
||||
- "renesas,r8a7790-mstp-clocks" for R8A7790 (R-Car H2) MSTP gate clocks
|
||||
- "renesas,r8a7791-mstp-clocks" for R8A7791 (R-Car M2-W) MSTP gate clocks
|
||||
- "renesas,r8a7792-mstp-clocks" for R8A7792 (R-Car V2H) MSTP gate clocks
|
||||
- "renesas,r8a7793-mstp-clocks" for R8A7793 (R-Car M2-N) MSTP gate clocks
|
||||
- "renesas,r8a7794-mstp-clocks" for R8A7794 (R-Car E2) MSTP gate clocks
|
||||
- "renesas,sh73a0-mstp-clocks" for SH73A0 (SH-MobileAG5) MSTP gate clocks
|
||||
and "renesas,cpg-mstp-clocks" as a fallback.
|
||||
- reg: Base address and length of the I/O mapped registers used by the MSTP
|
||||
clocks. The first register is the clock control register and is mandatory.
|
||||
The second register is the clock status register and is optional when not
|
||||
implemented in hardware.
|
||||
- clocks: Reference to the parent clocks, one per output clock. The parents
|
||||
must appear in the same order as the output clocks.
|
||||
- #clock-cells: Must be 1
|
||||
- clock-output-names: The name of the clocks as free-form strings
|
||||
- clock-indices: Indices of the gate clocks into the group (0 to 31)
|
||||
|
||||
The clocks, clock-output-names and clock-indices properties contain one entry
|
||||
per gate clock. The MSTP groups are sparsely populated. Unimplemented gate
|
||||
clocks must not be declared.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
#include <dt-bindings/clock/r8a7790-clock.h>
|
||||
|
||||
mstp3_clks: mstp3_clks@e615013c {
|
||||
compatible = "renesas,r8a7790-mstp-clocks", "renesas,cpg-mstp-clocks";
|
||||
reg = <0 0xe615013c 0 4>, <0 0xe6150048 0 4>;
|
||||
clocks = <&cp_clk>, <&mmc1_clk>, <&sd3_clk>, <&sd2_clk>,
|
||||
<&cpg_clocks R8A7790_CLK_SD1>, <&cpg_clocks R8A7790_CLK_SD0>,
|
||||
<&mmc0_clk>;
|
||||
#clock-cells = <1>;
|
||||
clock-output-names =
|
||||
"tpu0", "mmcif1", "sdhi3", "sdhi2",
|
||||
"sdhi1", "sdhi0", "mmcif0";
|
||||
clock-indices = <
|
||||
R8A7790_CLK_TPU0 R8A7790_CLK_MMCIF1 R8A7790_CLK_SDHI3
|
||||
R8A7790_CLK_SDHI2 R8A7790_CLK_SDHI1 R8A7790_CLK_SDHI0
|
||||
R8A7790_CLK_MMCIF0
|
||||
>;
|
||||
};
|
@ -0,0 +1,82 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/renesas,cpg-mstp-clocks.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas Clock Pulse Generator (CPG) Module Stop (MSTP) Clocks
|
||||
|
||||
maintainers:
|
||||
- Geert Uytterhoeven <geert+renesas@glider.be>
|
||||
|
||||
description:
|
||||
The Clock Pulse Generator (CPG) can gate SoC device clocks. The gates are
|
||||
organized in groups of up to 32 gates.
|
||||
|
||||
This device tree binding describes a single 32 gate clocks group per node.
|
||||
Clocks are referenced by user nodes by the Module Stop (MSTP) node phandle
|
||||
and the clock index in the group, from 0 to 31.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,r7s72100-mstp-clocks # RZ/A1
|
||||
- renesas,r8a73a4-mstp-clocks # R-Mobile APE6
|
||||
- renesas,r8a7740-mstp-clocks # R-Mobile A1
|
||||
- renesas,r8a7778-mstp-clocks # R-Car M1
|
||||
- renesas,r8a7779-mstp-clocks # R-Car H1
|
||||
- renesas,sh73a0-mstp-clocks # SH-Mobile AG5
|
||||
- const: renesas,cpg-mstp-clocks
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Module Stop Control Register (MSTPCR)
|
||||
- description: Module Stop Status Register (MSTPSR)
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 32
|
||||
|
||||
'#clock-cells':
|
||||
const: 1
|
||||
|
||||
clock-indices:
|
||||
minItems: 1
|
||||
maxItems: 32
|
||||
|
||||
clock-output-names:
|
||||
minItems: 1
|
||||
maxItems: 32
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- '#clock-cells'
|
||||
- clock-indices
|
||||
- clock-output-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/r8a73a4-clock.h>
|
||||
mstp2_clks: mstp2_clks@e6150138 {
|
||||
compatible = "renesas,r8a73a4-mstp-clocks",
|
||||
"renesas,cpg-mstp-clocks";
|
||||
reg = <0xe6150138 4>, <0xe6150040 4>;
|
||||
clocks = <&mp_clk>, <&mp_clk>, <&mp_clk>, <&mp_clk>, <&mp_clk>,
|
||||
<&mp_clk>, <&cpg_clocks R8A73A4_CLK_HP>;
|
||||
#clock-cells = <1>;
|
||||
clock-indices = <
|
||||
R8A73A4_CLK_SCIFA0 R8A73A4_CLK_SCIFA1
|
||||
R8A73A4_CLK_SCIFB0 R8A73A4_CLK_SCIFB1
|
||||
R8A73A4_CLK_SCIFB2 R8A73A4_CLK_SCIFB3
|
||||
R8A73A4_CLK_DMAC
|
||||
>;
|
||||
clock-output-names =
|
||||
"scifa0", "scifa1", "scifb0", "scifb1", "scifb2", "scifb3",
|
||||
"dmac";
|
||||
};
|
@ -27,7 +27,9 @@ Required properties:
|
||||
- compatible: "renesas,r8a7795-rcar-usb2-clock-sel" if the device is a part of
|
||||
an R8A7795 SoC.
|
||||
"renesas,r8a7796-rcar-usb2-clock-sel" if the device if a part of
|
||||
an R8A7796 SoC.
|
||||
an R8A77960 SoC.
|
||||
"renesas,r8a77961-rcar-usb2-clock-sel" if the device if a part of
|
||||
an R8A77961 SoC.
|
||||
"renesas,rcar-gen3-usb2-clock-sel" for a generic R-Car Gen3
|
||||
compatible device.
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
Binding for Silicon Labs Si5341 and Si5340 programmable i2c clock generator.
|
||||
Binding for Silicon Labs Si5340, Si5341 Si5342, Si5344 and Si5345 programmable
|
||||
i2c clock generator.
|
||||
|
||||
Reference
|
||||
[1] Si5341 Data Sheet
|
||||
https://www.silabs.com/documents/public/data-sheets/Si5341-40-D-DataSheet.pdf
|
||||
[2] Si5341 Reference Manual
|
||||
https://www.silabs.com/documents/public/reference-manuals/Si5341-40-D-RM.pdf
|
||||
[3] Si5345 Reference Manual
|
||||
https://www.silabs.com/documents/public/reference-manuals/Si5345-44-42-D-RM.pdf
|
||||
|
||||
The Si5341 and Si5340 are programmable i2c clock generators with up to 10 output
|
||||
clocks. The chip contains a PLL that sources 5 (or 4) multisynth clocks, which
|
||||
in turn can be directed to any of the 10 (or 4) outputs through a divider.
|
||||
The internal structure of the clock generators can be found in [2].
|
||||
The Si5345 is similar to the Si5341 with the addition of fractional input
|
||||
dividers and automatic input selection, as described in [3].
|
||||
The Si5342 and Si5344 are smaller versions of the Si5345, with 2 or 4 outputs.
|
||||
|
||||
The driver can be used in "as is" mode, reading the current settings from the
|
||||
chip at boot, in case you have a (pre-)programmed device. If the PLL is not
|
||||
@ -28,6 +34,9 @@ Required properties:
|
||||
- compatible: shall be one of the following:
|
||||
"silabs,si5340" - Si5340 A/B/C/D
|
||||
"silabs,si5341" - Si5341 A/B/C/D
|
||||
"silabs,si5342" - Si5342 A/B/C/D
|
||||
"silabs,si5344" - Si5344 A/B/C/D
|
||||
"silabs,si5345" - Si5345 A/B/C/D
|
||||
- reg: i2c device address, usually 0x74
|
||||
- #clock-cells: from common clock binding; shall be set to 2.
|
||||
The first value is "0" for outputs, "1" for synthesizers.
|
||||
|
@ -28,6 +28,7 @@ properties:
|
||||
- sprd,sc9863a-rpll
|
||||
- sprd,sc9863a-dpll
|
||||
- sprd,sc9863a-mm-gate
|
||||
- sprd,sc9863a-mm-clk
|
||||
- sprd,sc9863a-apapb-gate
|
||||
|
||||
clocks:
|
||||
|
@ -14493,6 +14493,7 @@ M: Geert Uytterhoeven <geert+renesas@glider.be>
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
S: Supported
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers.git clk-renesas
|
||||
F: Documentation/devicetree/bindings/clock/renesas,*
|
||||
F: drivers/clk/renesas/
|
||||
|
||||
RENESAS EMEV2 I2C DRIVER
|
||||
|
@ -328,12 +328,6 @@ config HAVE_FUNCTION_ARG_ACCESS_API
|
||||
the API needed to access function arguments from pt_regs,
|
||||
declared in asm/ptrace.h
|
||||
|
||||
config HAVE_CLK
|
||||
bool
|
||||
help
|
||||
The <linux/clk.h> calls support software clock gating and
|
||||
thus are a key power management tool on many systems.
|
||||
|
||||
config HAVE_HW_BREAKPOINT
|
||||
bool
|
||||
depends on PERF_EVENTS
|
||||
|
@ -367,6 +367,7 @@ config ARCH_EP93XX
|
||||
select CPU_ARM920T
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select GPIOLIB
|
||||
select HAVE_LEGACY_CLK
|
||||
help
|
||||
This enables support for the Cirrus EP93xx series of CPUs.
|
||||
|
||||
@ -438,7 +439,6 @@ config ARCH_PXA
|
||||
select ARM_CPU_SUSPEND if PM
|
||||
select AUTO_ZRELADDR
|
||||
select COMMON_CLK
|
||||
select CLKDEV_LOOKUP
|
||||
select CLKSRC_PXA
|
||||
select CLKSRC_MMIO
|
||||
select TIMER_OF
|
||||
@ -477,7 +477,6 @@ config ARCH_SA1100
|
||||
bool "SA1100-based"
|
||||
select ARCH_MTD_XIP
|
||||
select ARCH_SPARSEMEM_ENABLE
|
||||
select CLKDEV_LOOKUP
|
||||
select CLKSRC_MMIO
|
||||
select CLKSRC_PXA
|
||||
select TIMER_OF if OF
|
||||
@ -498,7 +497,6 @@ config ARCH_SA1100
|
||||
config ARCH_S3C24XX
|
||||
bool "Samsung S3C24XX SoCs"
|
||||
select ATAGS
|
||||
select CLKDEV_LOOKUP
|
||||
select CLKSRC_SAMSUNG_PWM
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select GPIO_SAMSUNG
|
||||
@ -528,6 +526,7 @@ config ARCH_OMAP1
|
||||
select GENERIC_IRQ_MULTI_HANDLER
|
||||
select GPIOLIB
|
||||
select HAVE_IDE
|
||||
select HAVE_LEGACY_CLK
|
||||
select IRQ_DOMAIN
|
||||
select NEED_MACH_IO_H if PCCARD
|
||||
select NEED_MACH_MEMORY_H
|
||||
|
@ -124,6 +124,8 @@ config MACH_MMP2_DT
|
||||
select PINCTRL_SINGLE
|
||||
select ARCH_HAS_RESET_CONTROLLER
|
||||
select CPU_PJ4
|
||||
select PM_GENERIC_DOMAINS if PM
|
||||
select PM_GENERIC_DOMAINS_OF if PM && OF
|
||||
help
|
||||
Include support for Marvell MMP2 based platforms using
|
||||
the device tree.
|
||||
|
@ -12,12 +12,6 @@ obj-$(CONFIG_CPU_PXA910) += pxa910.o
|
||||
obj-$(CONFIG_CPU_MMP2) += mmp2.o
|
||||
obj-$(CONFIG_MMP_SRAM) += sram.o
|
||||
|
||||
ifeq ($(CONFIG_COMMON_CLK), )
|
||||
obj-y += clock.o
|
||||
obj-$(CONFIG_CPU_PXA168) += clock-pxa168.o
|
||||
obj-$(CONFIG_CPU_PXA910) += clock-pxa910.o
|
||||
obj-$(CONFIG_CPU_MMP2) += clock-mmp2.o
|
||||
endif
|
||||
ifeq ($(CONFIG_PM),y)
|
||||
obj-$(CONFIG_CPU_PXA910) += pm-pxa910.o
|
||||
obj-$(CONFIG_CPU_MMP2) += pm-mmp2.o
|
||||
|
@ -1,114 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk/mmp.h>
|
||||
|
||||
#include "addr-map.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "clock.h"
|
||||
|
||||
/*
|
||||
* APB Clock register offsets for MMP2
|
||||
*/
|
||||
#define APBC_RTC APBC_REG(0x000)
|
||||
#define APBC_TWSI1 APBC_REG(0x004)
|
||||
#define APBC_TWSI2 APBC_REG(0x008)
|
||||
#define APBC_TWSI3 APBC_REG(0x00c)
|
||||
#define APBC_TWSI4 APBC_REG(0x010)
|
||||
#define APBC_KPC APBC_REG(0x018)
|
||||
#define APBC_UART1 APBC_REG(0x02c)
|
||||
#define APBC_UART2 APBC_REG(0x030)
|
||||
#define APBC_UART3 APBC_REG(0x034)
|
||||
#define APBC_GPIO APBC_REG(0x038)
|
||||
#define APBC_PWM0 APBC_REG(0x03c)
|
||||
#define APBC_PWM1 APBC_REG(0x040)
|
||||
#define APBC_PWM2 APBC_REG(0x044)
|
||||
#define APBC_PWM3 APBC_REG(0x048)
|
||||
#define APBC_SSP0 APBC_REG(0x04c)
|
||||
#define APBC_SSP1 APBC_REG(0x050)
|
||||
#define APBC_SSP2 APBC_REG(0x054)
|
||||
#define APBC_SSP3 APBC_REG(0x058)
|
||||
#define APBC_SSP4 APBC_REG(0x05c)
|
||||
#define APBC_SSP5 APBC_REG(0x060)
|
||||
#define APBC_TWSI5 APBC_REG(0x07c)
|
||||
#define APBC_TWSI6 APBC_REG(0x080)
|
||||
#define APBC_UART4 APBC_REG(0x088)
|
||||
|
||||
#define APMU_USB APMU_REG(0x05c)
|
||||
#define APMU_NAND APMU_REG(0x060)
|
||||
#define APMU_SDH0 APMU_REG(0x054)
|
||||
#define APMU_SDH1 APMU_REG(0x058)
|
||||
#define APMU_SDH2 APMU_REG(0x0e8)
|
||||
#define APMU_SDH3 APMU_REG(0x0ec)
|
||||
|
||||
static void sdhc_clk_enable(struct clk *clk)
|
||||
{
|
||||
uint32_t clk_rst;
|
||||
|
||||
clk_rst = __raw_readl(clk->clk_rst);
|
||||
clk_rst |= clk->enable_val;
|
||||
__raw_writel(clk_rst, clk->clk_rst);
|
||||
}
|
||||
|
||||
static void sdhc_clk_disable(struct clk *clk)
|
||||
{
|
||||
uint32_t clk_rst;
|
||||
|
||||
clk_rst = __raw_readl(clk->clk_rst);
|
||||
clk_rst &= ~clk->enable_val;
|
||||
__raw_writel(clk_rst, clk->clk_rst);
|
||||
}
|
||||
|
||||
struct clkops sdhc_clk_ops = {
|
||||
.enable = sdhc_clk_enable,
|
||||
.disable = sdhc_clk_disable,
|
||||
};
|
||||
|
||||
/* APB peripheral clocks */
|
||||
static APBC_CLK(uart1, UART1, 1, 26000000);
|
||||
static APBC_CLK(uart2, UART2, 1, 26000000);
|
||||
static APBC_CLK(uart3, UART3, 1, 26000000);
|
||||
static APBC_CLK(uart4, UART4, 1, 26000000);
|
||||
static APBC_CLK(twsi1, TWSI1, 0, 26000000);
|
||||
static APBC_CLK(twsi2, TWSI2, 0, 26000000);
|
||||
static APBC_CLK(twsi3, TWSI3, 0, 26000000);
|
||||
static APBC_CLK(twsi4, TWSI4, 0, 26000000);
|
||||
static APBC_CLK(twsi5, TWSI5, 0, 26000000);
|
||||
static APBC_CLK(twsi6, TWSI6, 0, 26000000);
|
||||
static APBC_CLK(gpio, GPIO, 0, 26000000);
|
||||
|
||||
static APMU_CLK(nand, NAND, 0xbf, 100000000);
|
||||
static APMU_CLK_OPS(sdh0, SDH0, 0x1b, 200000000, &sdhc_clk_ops);
|
||||
static APMU_CLK_OPS(sdh1, SDH1, 0x1b, 200000000, &sdhc_clk_ops);
|
||||
static APMU_CLK_OPS(sdh2, SDH2, 0x1b, 200000000, &sdhc_clk_ops);
|
||||
static APMU_CLK_OPS(sdh3, SDH3, 0x1b, 200000000, &sdhc_clk_ops);
|
||||
|
||||
static struct clk_lookup mmp2_clkregs[] = {
|
||||
INIT_CLKREG(&clk_uart1, "pxa2xx-uart.0", NULL),
|
||||
INIT_CLKREG(&clk_uart2, "pxa2xx-uart.1", NULL),
|
||||
INIT_CLKREG(&clk_uart3, "pxa2xx-uart.2", NULL),
|
||||
INIT_CLKREG(&clk_uart4, "pxa2xx-uart.3", NULL),
|
||||
INIT_CLKREG(&clk_twsi1, "pxa2xx-i2c.0", NULL),
|
||||
INIT_CLKREG(&clk_twsi2, "pxa2xx-i2c.1", NULL),
|
||||
INIT_CLKREG(&clk_twsi3, "pxa2xx-i2c.2", NULL),
|
||||
INIT_CLKREG(&clk_twsi4, "pxa2xx-i2c.3", NULL),
|
||||
INIT_CLKREG(&clk_twsi5, "pxa2xx-i2c.4", NULL),
|
||||
INIT_CLKREG(&clk_twsi6, "pxa2xx-i2c.5", NULL),
|
||||
INIT_CLKREG(&clk_nand, "pxa3xx-nand", NULL),
|
||||
INIT_CLKREG(&clk_gpio, "mmp2-gpio", NULL),
|
||||
INIT_CLKREG(&clk_sdh0, "sdhci-pxav3.0", "PXA-SDHCLK"),
|
||||
INIT_CLKREG(&clk_sdh1, "sdhci-pxav3.1", "PXA-SDHCLK"),
|
||||
INIT_CLKREG(&clk_sdh2, "sdhci-pxav3.2", "PXA-SDHCLK"),
|
||||
INIT_CLKREG(&clk_sdh3, "sdhci-pxav3.3", "PXA-SDHCLK"),
|
||||
};
|
||||
|
||||
void __init mmp2_clk_init(phys_addr_t mpmu_phys, phys_addr_t apmu_phys,
|
||||
phys_addr_t apbc_phys)
|
||||
{
|
||||
clkdev_add_table(ARRAY_AND_SIZE(mmp2_clkregs));
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk/mmp.h>
|
||||
|
||||
#include "addr-map.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "clock.h"
|
||||
|
||||
/*
|
||||
* APB clock register offsets for PXA168
|
||||
*/
|
||||
#define APBC_UART1 APBC_REG(0x000)
|
||||
#define APBC_UART2 APBC_REG(0x004)
|
||||
#define APBC_GPIO APBC_REG(0x008)
|
||||
#define APBC_PWM1 APBC_REG(0x00c)
|
||||
#define APBC_PWM2 APBC_REG(0x010)
|
||||
#define APBC_PWM3 APBC_REG(0x014)
|
||||
#define APBC_PWM4 APBC_REG(0x018)
|
||||
#define APBC_RTC APBC_REG(0x028)
|
||||
#define APBC_TWSI0 APBC_REG(0x02c)
|
||||
#define APBC_KPC APBC_REG(0x030)
|
||||
#define APBC_TWSI1 APBC_REG(0x06c)
|
||||
#define APBC_UART3 APBC_REG(0x070)
|
||||
#define APBC_SSP1 APBC_REG(0x81c)
|
||||
#define APBC_SSP2 APBC_REG(0x820)
|
||||
#define APBC_SSP3 APBC_REG(0x84c)
|
||||
#define APBC_SSP4 APBC_REG(0x858)
|
||||
#define APBC_SSP5 APBC_REG(0x85c)
|
||||
|
||||
#define APMU_NAND APMU_REG(0x060)
|
||||
#define APMU_LCD APMU_REG(0x04c)
|
||||
#define APMU_ETH APMU_REG(0x0fc)
|
||||
#define APMU_USB APMU_REG(0x05c)
|
||||
|
||||
/* APB peripheral clocks */
|
||||
static APBC_CLK(uart1, UART1, 1, 14745600);
|
||||
static APBC_CLK(uart2, UART2, 1, 14745600);
|
||||
static APBC_CLK(uart3, UART3, 1, 14745600);
|
||||
static APBC_CLK(twsi0, TWSI0, 1, 33000000);
|
||||
static APBC_CLK(twsi1, TWSI1, 1, 33000000);
|
||||
static APBC_CLK(pwm1, PWM1, 1, 13000000);
|
||||
static APBC_CLK(pwm2, PWM2, 1, 13000000);
|
||||
static APBC_CLK(pwm3, PWM3, 1, 13000000);
|
||||
static APBC_CLK(pwm4, PWM4, 1, 13000000);
|
||||
static APBC_CLK(ssp1, SSP1, 4, 0);
|
||||
static APBC_CLK(ssp2, SSP2, 4, 0);
|
||||
static APBC_CLK(ssp3, SSP3, 4, 0);
|
||||
static APBC_CLK(ssp4, SSP4, 4, 0);
|
||||
static APBC_CLK(ssp5, SSP5, 4, 0);
|
||||
static APBC_CLK(gpio, GPIO, 0, 13000000);
|
||||
static APBC_CLK(keypad, KPC, 0, 32000);
|
||||
static APBC_CLK(rtc, RTC, 8, 32768);
|
||||
|
||||
static APMU_CLK(nand, NAND, 0x19b, 156000000);
|
||||
static APMU_CLK(lcd, LCD, 0x7f, 312000000);
|
||||
static APMU_CLK(eth, ETH, 0x09, 0);
|
||||
static APMU_CLK(usb, USB, 0x12, 0);
|
||||
|
||||
/* device and clock bindings */
|
||||
static struct clk_lookup pxa168_clkregs[] = {
|
||||
INIT_CLKREG(&clk_uart1, "pxa2xx-uart.0", NULL),
|
||||
INIT_CLKREG(&clk_uart2, "pxa2xx-uart.1", NULL),
|
||||
INIT_CLKREG(&clk_uart3, "pxa2xx-uart.2", NULL),
|
||||
INIT_CLKREG(&clk_twsi0, "pxa2xx-i2c.0", NULL),
|
||||
INIT_CLKREG(&clk_twsi1, "pxa2xx-i2c.1", NULL),
|
||||
INIT_CLKREG(&clk_pwm1, "pxa168-pwm.0", NULL),
|
||||
INIT_CLKREG(&clk_pwm2, "pxa168-pwm.1", NULL),
|
||||
INIT_CLKREG(&clk_pwm3, "pxa168-pwm.2", NULL),
|
||||
INIT_CLKREG(&clk_pwm4, "pxa168-pwm.3", NULL),
|
||||
INIT_CLKREG(&clk_ssp1, "pxa168-ssp.0", NULL),
|
||||
INIT_CLKREG(&clk_ssp2, "pxa168-ssp.1", NULL),
|
||||
INIT_CLKREG(&clk_ssp3, "pxa168-ssp.2", NULL),
|
||||
INIT_CLKREG(&clk_ssp4, "pxa168-ssp.3", NULL),
|
||||
INIT_CLKREG(&clk_ssp5, "pxa168-ssp.4", NULL),
|
||||
INIT_CLKREG(&clk_nand, "pxa3xx-nand", NULL),
|
||||
INIT_CLKREG(&clk_lcd, "pxa168-fb", NULL),
|
||||
INIT_CLKREG(&clk_gpio, "mmp-gpio", NULL),
|
||||
INIT_CLKREG(&clk_keypad, "pxa27x-keypad", NULL),
|
||||
INIT_CLKREG(&clk_eth, "pxa168-eth", "MFUCLK"),
|
||||
INIT_CLKREG(&clk_usb, NULL, "PXA168-USBCLK"),
|
||||
INIT_CLKREG(&clk_rtc, "sa1100-rtc", NULL),
|
||||
};
|
||||
|
||||
void __init pxa168_clk_init(phys_addr_t mpmu_phys, phys_addr_t apmu_phys,
|
||||
phys_addr_t apbc_phys)
|
||||
{
|
||||
clkdev_add_table(ARRAY_AND_SIZE(pxa168_clkregs));
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk/mmp.h>
|
||||
|
||||
#include "addr-map.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "clock.h"
|
||||
|
||||
/*
|
||||
* APB Clock register offsets for PXA910
|
||||
*/
|
||||
#define APBC_UART0 APBC_REG(0x000)
|
||||
#define APBC_UART1 APBC_REG(0x004)
|
||||
#define APBC_GPIO APBC_REG(0x008)
|
||||
#define APBC_PWM1 APBC_REG(0x00c)
|
||||
#define APBC_PWM2 APBC_REG(0x010)
|
||||
#define APBC_PWM3 APBC_REG(0x014)
|
||||
#define APBC_PWM4 APBC_REG(0x018)
|
||||
#define APBC_SSP1 APBC_REG(0x01c)
|
||||
#define APBC_SSP2 APBC_REG(0x020)
|
||||
#define APBC_RTC APBC_REG(0x028)
|
||||
#define APBC_TWSI0 APBC_REG(0x02c)
|
||||
#define APBC_KPC APBC_REG(0x030)
|
||||
#define APBC_SSP3 APBC_REG(0x04c)
|
||||
#define APBC_TWSI1 APBC_REG(0x06c)
|
||||
|
||||
#define APMU_NAND APMU_REG(0x060)
|
||||
#define APMU_USB APMU_REG(0x05c)
|
||||
|
||||
static APBC_CLK(uart1, UART0, 1, 14745600);
|
||||
static APBC_CLK(uart2, UART1, 1, 14745600);
|
||||
static APBC_CLK(twsi0, TWSI0, 1, 33000000);
|
||||
static APBC_CLK(twsi1, TWSI1, 1, 33000000);
|
||||
static APBC_CLK(pwm1, PWM1, 1, 13000000);
|
||||
static APBC_CLK(pwm2, PWM2, 1, 13000000);
|
||||
static APBC_CLK(pwm3, PWM3, 1, 13000000);
|
||||
static APBC_CLK(pwm4, PWM4, 1, 13000000);
|
||||
static APBC_CLK(gpio, GPIO, 0, 13000000);
|
||||
static APBC_CLK(rtc, RTC, 8, 32768);
|
||||
|
||||
static APMU_CLK(nand, NAND, 0x19b, 156000000);
|
||||
static APMU_CLK(u2o, USB, 0x1b, 480000000);
|
||||
|
||||
/* device and clock bindings */
|
||||
static struct clk_lookup pxa910_clkregs[] = {
|
||||
INIT_CLKREG(&clk_uart1, "pxa2xx-uart.0", NULL),
|
||||
INIT_CLKREG(&clk_uart2, "pxa2xx-uart.1", NULL),
|
||||
INIT_CLKREG(&clk_twsi0, "pxa2xx-i2c.0", NULL),
|
||||
INIT_CLKREG(&clk_twsi1, "pxa2xx-i2c.1", NULL),
|
||||
INIT_CLKREG(&clk_pwm1, "pxa910-pwm.0", NULL),
|
||||
INIT_CLKREG(&clk_pwm2, "pxa910-pwm.1", NULL),
|
||||
INIT_CLKREG(&clk_pwm3, "pxa910-pwm.2", NULL),
|
||||
INIT_CLKREG(&clk_pwm4, "pxa910-pwm.3", NULL),
|
||||
INIT_CLKREG(&clk_nand, "pxa3xx-nand", NULL),
|
||||
INIT_CLKREG(&clk_gpio, "mmp-gpio", NULL),
|
||||
INIT_CLKREG(&clk_u2o, NULL, "U2OCLK"),
|
||||
INIT_CLKREG(&clk_rtc, "sa1100-rtc", NULL),
|
||||
};
|
||||
|
||||
void __init pxa910_clk_init(phys_addr_t mpmu_phys, phys_addr_t apmu_phys,
|
||||
phys_addr_t apbc_phys, phys_addr_t apbcp_phys)
|
||||
{
|
||||
clkdev_add_table(ARRAY_AND_SIZE(pxa910_clkregs));
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* linux/arch/arm/mach-mmp/clock.c
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "regs-apbc.h"
|
||||
#include "clock.h"
|
||||
|
||||
static void apbc_clk_enable(struct clk *clk)
|
||||
{
|
||||
uint32_t clk_rst;
|
||||
|
||||
clk_rst = APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(clk->fnclksel);
|
||||
__raw_writel(clk_rst, clk->clk_rst);
|
||||
}
|
||||
|
||||
static void apbc_clk_disable(struct clk *clk)
|
||||
{
|
||||
__raw_writel(0, clk->clk_rst);
|
||||
}
|
||||
|
||||
struct clkops apbc_clk_ops = {
|
||||
.enable = apbc_clk_enable,
|
||||
.disable = apbc_clk_disable,
|
||||
};
|
||||
|
||||
static void apmu_clk_enable(struct clk *clk)
|
||||
{
|
||||
__raw_writel(clk->enable_val, clk->clk_rst);
|
||||
}
|
||||
|
||||
static void apmu_clk_disable(struct clk *clk)
|
||||
{
|
||||
__raw_writel(0, clk->clk_rst);
|
||||
}
|
||||
|
||||
struct clkops apmu_clk_ops = {
|
||||
.enable = apmu_clk_enable,
|
||||
.disable = apmu_clk_disable,
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(clocks_lock);
|
||||
|
||||
int clk_enable(struct clk *clk)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&clocks_lock, flags);
|
||||
if (clk->enabled++ == 0)
|
||||
clk->ops->enable(clk);
|
||||
spin_unlock_irqrestore(&clocks_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_enable);
|
||||
|
||||
void clk_disable(struct clk *clk)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!clk)
|
||||
return;
|
||||
|
||||
WARN_ON(clk->enabled == 0);
|
||||
|
||||
spin_lock_irqsave(&clocks_lock, flags);
|
||||
if (--clk->enabled == 0)
|
||||
clk->ops->disable(clk);
|
||||
spin_unlock_irqrestore(&clocks_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(clk_disable);
|
||||
|
||||
unsigned long clk_get_rate(struct clk *clk)
|
||||
{
|
||||
unsigned long rate;
|
||||
|
||||
if (clk->ops->getrate)
|
||||
rate = clk->ops->getrate(clk);
|
||||
else
|
||||
rate = clk->rate;
|
||||
|
||||
return rate;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_get_rate);
|
||||
|
||||
int clk_set_rate(struct clk *clk, unsigned long rate)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (clk->ops->setrate) {
|
||||
spin_lock_irqsave(&clocks_lock, flags);
|
||||
ret = clk->ops->setrate(clk, rate);
|
||||
spin_unlock_irqrestore(&clocks_lock, flags);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_set_rate);
|
@ -1,65 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <linux/clkdev.h>
|
||||
|
||||
struct clkops {
|
||||
void (*enable)(struct clk *);
|
||||
void (*disable)(struct clk *);
|
||||
unsigned long (*getrate)(struct clk *);
|
||||
int (*setrate)(struct clk *, unsigned long);
|
||||
};
|
||||
|
||||
struct clk {
|
||||
const struct clkops *ops;
|
||||
|
||||
void __iomem *clk_rst; /* clock reset control register */
|
||||
int fnclksel; /* functional clock select (APBC) */
|
||||
uint32_t enable_val; /* value for clock enable (APMU) */
|
||||
unsigned long rate;
|
||||
int enabled;
|
||||
};
|
||||
|
||||
extern struct clkops apbc_clk_ops;
|
||||
extern struct clkops apmu_clk_ops;
|
||||
|
||||
#define APBC_CLK(_name, _reg, _fnclksel, _rate) \
|
||||
struct clk clk_##_name = { \
|
||||
.clk_rst = APBC_##_reg, \
|
||||
.fnclksel = _fnclksel, \
|
||||
.rate = _rate, \
|
||||
.ops = &apbc_clk_ops, \
|
||||
}
|
||||
|
||||
#define APBC_CLK_OPS(_name, _reg, _fnclksel, _rate, _ops) \
|
||||
struct clk clk_##_name = { \
|
||||
.clk_rst = APBC_##_reg, \
|
||||
.fnclksel = _fnclksel, \
|
||||
.rate = _rate, \
|
||||
.ops = _ops, \
|
||||
}
|
||||
|
||||
#define APMU_CLK(_name, _reg, _eval, _rate) \
|
||||
struct clk clk_##_name = { \
|
||||
.clk_rst = APMU_##_reg, \
|
||||
.enable_val = _eval, \
|
||||
.rate = _rate, \
|
||||
.ops = &apmu_clk_ops, \
|
||||
}
|
||||
|
||||
#define APMU_CLK_OPS(_name, _reg, _eval, _rate, _ops) \
|
||||
struct clk clk_##_name = { \
|
||||
.clk_rst = APMU_##_reg, \
|
||||
.enable_val = _eval, \
|
||||
.rate = _rate, \
|
||||
.ops = _ops, \
|
||||
}
|
||||
|
||||
#define INIT_CLKREG(_clk, _devname, _conname) \
|
||||
{ \
|
||||
.clk = _clk, \
|
||||
.dev_id = _devname, \
|
||||
.con_id = _conname, \
|
||||
}
|
||||
|
||||
extern struct clk clk_pxa168_gpio;
|
||||
extern struct clk clk_pxa168_timers;
|
@ -19,7 +19,6 @@
|
||||
#include <asm/system_misc.h>
|
||||
|
||||
#include "addr-map.h"
|
||||
#include "clock.h"
|
||||
#include "common.h"
|
||||
#include <linux/soc/mmp/cputype.h>
|
||||
#include "devices.h"
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include "regs-apbc.h"
|
||||
#include "irqs.h"
|
||||
#include <linux/soc/mmp/cputype.h>
|
||||
#include "clock.h"
|
||||
|
||||
#define TIMERS_VIRT_BASE TIMERS1_VIRT_BASE
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
config ARCH_VT8500
|
||||
bool
|
||||
select GPIOLIB
|
||||
select CLKDEV_LOOKUP
|
||||
select VT8500_TIMER
|
||||
select PINCTRL
|
||||
help
|
||||
|
@ -235,7 +235,6 @@ config ARCH_TEGRA
|
||||
bool "NVIDIA Tegra SoC Family"
|
||||
select ARCH_HAS_RESET_CONTROLLER
|
||||
select ARM_GIC_PM
|
||||
select CLKDEV_LOOKUP
|
||||
select CLKSRC_MMIO
|
||||
select TIMER_OF
|
||||
select GENERIC_CLOCKEVENTS
|
||||
|
@ -11,6 +11,7 @@ config C6X
|
||||
select ARCH_HAS_SYNC_DMA_FOR_CPU
|
||||
select ARCH_HAS_SYNC_DMA_FOR_DEVICE
|
||||
select CLKDEV_LOOKUP
|
||||
select HAVE_LEGACY_CLK
|
||||
select GENERIC_ATOMIC64
|
||||
select GENERIC_IRQ_SHOW
|
||||
select HAVE_ARCH_TRACEHOOK
|
||||
|
@ -13,7 +13,6 @@ config H8300
|
||||
select GENERIC_CPU_DEVICES
|
||||
select MODULES_USE_ELF_RELA
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select CLKDEV_LOOKUP
|
||||
select COMMON_CLK
|
||||
select ARCH_WANT_FRAME_POINTERS
|
||||
select OF
|
||||
|
@ -28,7 +28,7 @@ config COLDFIRE
|
||||
select CPU_HAS_NO_MULDIV64
|
||||
select GENERIC_CSUM
|
||||
select GPIOLIB
|
||||
select HAVE_CLK
|
||||
select HAVE_LEGACY_CLK
|
||||
|
||||
endchoice
|
||||
|
||||
|
@ -184,7 +184,7 @@ config AR7
|
||||
select SYS_SUPPORTS_ZBOOT_UART16550
|
||||
select GPIOLIB
|
||||
select VLYNQ
|
||||
select HAVE_CLK
|
||||
select HAVE_LEGACY_CLK
|
||||
help
|
||||
Support for the Texas Instruments AR7 System-on-a-Chip
|
||||
family: TNETD7100, 7200 and 7300.
|
||||
@ -212,9 +212,7 @@ config ATH79
|
||||
select DMA_NONCOHERENT
|
||||
select GPIOLIB
|
||||
select PINCTRL
|
||||
select HAVE_CLK
|
||||
select COMMON_CLK
|
||||
select CLKDEV_LOOKUP
|
||||
select IRQ_MIPS_CPU
|
||||
select SYS_HAS_CPU_MIPS32_R2
|
||||
select SYS_HAS_EARLY_PRINTK
|
||||
@ -301,9 +299,9 @@ config BCM63XX
|
||||
select SYS_HAS_EARLY_PRINTK
|
||||
select SWAP_IO_SPACE
|
||||
select GPIOLIB
|
||||
select HAVE_CLK
|
||||
select MIPS_L1_CACHE_SHIFT_4
|
||||
select CLKDEV_LOOKUP
|
||||
select HAVE_LEGACY_CLK
|
||||
help
|
||||
Support for BCM63XX based boards
|
||||
|
||||
@ -424,6 +422,7 @@ config LANTIQ
|
||||
select SWAP_IO_SPACE
|
||||
select BOOT_RAW
|
||||
select CLKDEV_LOOKUP
|
||||
select HAVE_LEGACY_CLK
|
||||
select USE_OF
|
||||
select PINCTRL
|
||||
select PINCTRL_LANTIQ
|
||||
|
@ -27,18 +27,22 @@ choice
|
||||
config SOC_RT288X
|
||||
bool "RT288x"
|
||||
select MIPS_L1_CACHE_SHIFT_4
|
||||
select HAVE_LEGACY_CLK
|
||||
select HAVE_PCI
|
||||
|
||||
config SOC_RT305X
|
||||
bool "RT305x"
|
||||
select HAVE_LEGACY_CLK
|
||||
|
||||
config SOC_RT3883
|
||||
bool "RT3883"
|
||||
select HAVE_LEGACY_CLK
|
||||
select HAVE_PCI
|
||||
|
||||
config SOC_MT7620
|
||||
bool "MT7620/8"
|
||||
select CPU_MIPSR2_IRQ_VI
|
||||
select HAVE_LEGACY_CLK
|
||||
select HAVE_PCI
|
||||
|
||||
config SOC_MT7621
|
||||
|
@ -7,6 +7,11 @@ config SOLUTION_ENGINE
|
||||
config SH_ALPHA_BOARD
|
||||
bool
|
||||
|
||||
config SH_CUSTOM_CLK
|
||||
def_bool y
|
||||
depends on !SH_DEVICE_TREE
|
||||
select HAVE_LEGACY_CLK
|
||||
|
||||
config SH_DEVICE_TREE
|
||||
bool
|
||||
select OF
|
||||
|
@ -70,7 +70,7 @@ config ARCH_PUV3
|
||||
def_bool y
|
||||
select CPU_UCV2
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select HAVE_CLK
|
||||
select HAVE_LEGACY_CLK
|
||||
select GPIOLIB
|
||||
|
||||
# CONFIGs for ARCH_PUV3
|
||||
|
@ -1,5 +1,11 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
config HAVE_CLK
|
||||
bool
|
||||
help
|
||||
The <linux/clk.h> calls support software clock gating and
|
||||
thus are a key power management tool on many systems.
|
||||
|
||||
config CLKDEV_LOOKUP
|
||||
bool
|
||||
select HAVE_CLK
|
||||
@ -7,8 +13,18 @@ config CLKDEV_LOOKUP
|
||||
config HAVE_CLK_PREPARE
|
||||
bool
|
||||
|
||||
config COMMON_CLK
|
||||
config HAVE_LEGACY_CLK # TODO: Remove once all legacy users are migrated
|
||||
bool
|
||||
select HAVE_CLK
|
||||
help
|
||||
Select this option when the clock API in <linux/clk.h> is implemented
|
||||
by platform/architecture code. This method is deprecated. Modern
|
||||
code should select COMMON_CLK instead and not define a custom
|
||||
'struct clk'.
|
||||
|
||||
menuconfig COMMON_CLK
|
||||
bool "Common Clock Framework"
|
||||
depends on !HAVE_LEGACY_CLK
|
||||
select HAVE_CLK_PREPARE
|
||||
select CLKDEV_LOOKUP
|
||||
select SRCU
|
||||
@ -20,8 +36,7 @@ config COMMON_CLK
|
||||
Architectures utilizing the common struct clk should select
|
||||
this option.
|
||||
|
||||
menu "Common Clock Framework"
|
||||
depends on COMMON_CLK
|
||||
if COMMON_CLK
|
||||
|
||||
config COMMON_CLK_WM831X
|
||||
tristate "Clock driver for WM831x/2x PMICs"
|
||||
@ -252,7 +267,7 @@ config COMMON_CLK_XGENE
|
||||
default ARCH_XGENE
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
---help---
|
||||
Sypport for the APM X-Gene SoC reference, PLL, and device clocks.
|
||||
Support for the APM X-Gene SoC reference, PLL, and device clocks.
|
||||
|
||||
config COMMON_CLK_LOCHNAGAR
|
||||
tristate "Cirrus Logic Lochnagar clock driver"
|
||||
@ -326,6 +341,12 @@ config COMMON_CLK_MMP2
|
||||
help
|
||||
Support for Marvell MMP2 and MMP3 SoC clocks
|
||||
|
||||
config COMMON_CLK_MMP2_AUDIO
|
||||
tristate "Clock driver for MMP2 Audio subsystem"
|
||||
depends on COMMON_CLK_MMP2 || COMPILE_TEST
|
||||
help
|
||||
This driver supports clocks for Audio subsystem on MMP2 SoC.
|
||||
|
||||
config COMMON_CLK_BD718XX
|
||||
tristate "Clock driver for 32K clk gates on ROHM PMICs"
|
||||
depends on MFD_ROHM_BD718XX || MFD_ROHM_BD70528 || MFD_ROHM_BD71828
|
||||
@ -341,6 +362,7 @@ config COMMON_CLK_FIXED_MMIO
|
||||
|
||||
source "drivers/clk/actions/Kconfig"
|
||||
source "drivers/clk/analogbits/Kconfig"
|
||||
source "drivers/clk/baikal-t1/Kconfig"
|
||||
source "drivers/clk/bcm/Kconfig"
|
||||
source "drivers/clk/hisilicon/Kconfig"
|
||||
source "drivers/clk/imgtec/Kconfig"
|
||||
@ -360,6 +382,7 @@ source "drivers/clk/sunxi-ng/Kconfig"
|
||||
source "drivers/clk/tegra/Kconfig"
|
||||
source "drivers/clk/ti/Kconfig"
|
||||
source "drivers/clk/uniphier/Kconfig"
|
||||
source "drivers/clk/x86/Kconfig"
|
||||
source "drivers/clk/zynqmp/Kconfig"
|
||||
|
||||
endmenu
|
||||
endif
|
||||
|
@ -75,6 +75,7 @@ obj-y += analogbits/
|
||||
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
|
||||
obj-$(CONFIG_ARCH_ARTPEC) += axis/
|
||||
obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/
|
||||
obj-$(CONFIG_CLK_BAIKAL_T1) += baikal-t1/
|
||||
obj-y += bcm/
|
||||
obj-$(CONFIG_ARCH_BERLIN) += berlin/
|
||||
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
|
||||
@ -104,10 +105,11 @@ obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
|
||||
obj-$(CONFIG_CLK_SIFIVE) += sifive/
|
||||
obj-$(CONFIG_ARCH_SIRF) += sirf/
|
||||
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
|
||||
obj-$(CONFIG_ARCH_AGILEX) += socfpga/
|
||||
obj-$(CONFIG_ARCH_STRATIX10) += socfpga/
|
||||
obj-$(CONFIG_PLAT_SPEAR) += spear/
|
||||
obj-y += sprd/
|
||||
obj-$(CONFIG_ARCH_STI) += st/
|
||||
obj-$(CONFIG_ARCH_STRATIX10) += socfpga/
|
||||
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
|
||||
obj-$(CONFIG_SUNXI_CCU) += sunxi-ng/
|
||||
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
||||
|
@ -98,9 +98,9 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
at91rm9200_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
at91rm9200_pmc = pmc_data_allocate(PMC_PLLBCK + 1,
|
||||
nck(at91rm9200_systemck),
|
||||
nck(at91rm9200_periphck), 0);
|
||||
nck(at91rm9200_periphck), 0, 4);
|
||||
if (!at91rm9200_pmc)
|
||||
return;
|
||||
|
||||
@ -123,12 +123,16 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91rm9200_pmc->chws[PMC_PLLACK] = hw;
|
||||
|
||||
hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1,
|
||||
&at91rm9200_pll_layout,
|
||||
&rm9200_pll_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91rm9200_pmc->chws[PMC_PLLBCK] = hw;
|
||||
|
||||
parent_names[0] = slowxtal_name;
|
||||
parent_names[1] = "mainck";
|
||||
parent_names[2] = "pllack";
|
||||
@ -159,6 +163,8 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
|
||||
&at91rm9200_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91rm9200_pmc->pchws[i] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(at91rm9200_systemck); i++) {
|
||||
@ -187,7 +193,7 @@ static void __init at91rm9200_pmc_setup(struct device_node *np)
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(at91rm9200_pmc);
|
||||
kfree(at91rm9200_pmc);
|
||||
}
|
||||
/*
|
||||
* While the TCB can be used as the clocksource, the system timer is most likely
|
||||
|
@ -352,9 +352,10 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
at91sam9260_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
at91sam9260_pmc = pmc_data_allocate(PMC_PLLBCK + 1,
|
||||
ndck(data->sck, data->num_sck),
|
||||
ndck(data->pck, data->num_pck), 0);
|
||||
ndck(data->pck, data->num_pck),
|
||||
0, data->num_progck);
|
||||
if (!at91sam9260_pmc)
|
||||
return;
|
||||
|
||||
@ -398,12 +399,16 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9260_pmc->chws[PMC_PLLACK] = hw;
|
||||
|
||||
hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1,
|
||||
data->pllb_layout,
|
||||
data->pllb_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9260_pmc->chws[PMC_PLLBCK] = hw;
|
||||
|
||||
parent_names[0] = slck_name;
|
||||
parent_names[1] = "mainck";
|
||||
parent_names[2] = "pllack";
|
||||
@ -434,6 +439,8 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
|
||||
&at91rm9200_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9260_pmc->pchws[i] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < data->num_sck; i++) {
|
||||
@ -462,7 +469,7 @@ static void __init at91sam926x_pmc_setup(struct device_node *np,
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(at91sam9260_pmc);
|
||||
kfree(at91sam9260_pmc);
|
||||
}
|
||||
|
||||
static void __init at91sam9260_pmc_setup(struct device_node *np)
|
||||
|
@ -115,9 +115,9 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
at91sam9g45_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
at91sam9g45_pmc = pmc_data_allocate(PMC_PLLACK + 1,
|
||||
nck(at91sam9g45_systemck),
|
||||
nck(at91sam9g45_periphck), 0);
|
||||
nck(at91sam9g45_periphck), 0, 2);
|
||||
if (!at91sam9g45_pmc)
|
||||
return;
|
||||
|
||||
@ -143,6 +143,8 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9g45_pmc->chws[PMC_PLLACK] = hw;
|
||||
|
||||
hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
@ -182,6 +184,8 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
|
||||
&at91sam9g45_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9g45_pmc->pchws[i] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(at91sam9g45_systemck); i++) {
|
||||
@ -210,7 +214,7 @@ static void __init at91sam9g45_pmc_setup(struct device_node *np)
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(at91sam9g45_pmc);
|
||||
kfree(at91sam9g45_pmc);
|
||||
}
|
||||
/*
|
||||
* The TCB is used as the clocksource so its clock is needed early. This means
|
||||
|
@ -128,8 +128,8 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
at91sam9n12_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
nck(at91sam9n12_systemck), 31, 0);
|
||||
at91sam9n12_pmc = pmc_data_allocate(PMC_PLLBCK + 1,
|
||||
nck(at91sam9n12_systemck), 31, 0, 2);
|
||||
if (!at91sam9n12_pmc)
|
||||
return;
|
||||
|
||||
@ -162,11 +162,15 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9n12_pmc->chws[PMC_PLLACK] = hw;
|
||||
|
||||
hw = at91_clk_register_pll(regmap, "pllbck", "mainck", 1,
|
||||
&at91rm9200_pll_layout, &pllb_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9n12_pmc->chws[PMC_PLLBCK] = hw;
|
||||
|
||||
parent_names[0] = slck_name;
|
||||
parent_names[1] = "mainck";
|
||||
parent_names[2] = "plladivck";
|
||||
@ -198,6 +202,8 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
|
||||
&at91sam9x5_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9n12_pmc->pchws[i] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(at91sam9n12_systemck); i++) {
|
||||
@ -228,7 +234,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(at91sam9n12_pmc);
|
||||
kfree(at91sam9n12_pmc);
|
||||
}
|
||||
/*
|
||||
* The TCB is used as the clocksource so its clock is needed early. This means
|
||||
|
@ -87,9 +87,9 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
at91sam9rl_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
at91sam9rl_pmc = pmc_data_allocate(PMC_PLLACK + 1,
|
||||
nck(at91sam9rl_systemck),
|
||||
nck(at91sam9rl_periphck), 0);
|
||||
nck(at91sam9rl_periphck), 0, 2);
|
||||
if (!at91sam9rl_pmc)
|
||||
return;
|
||||
|
||||
@ -105,6 +105,8 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9rl_pmc->chws[PMC_PLLACK] = hw;
|
||||
|
||||
hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
@ -138,6 +140,8 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
|
||||
&at91rm9200_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9rl_pmc->pchws[i] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(at91sam9rl_systemck); i++) {
|
||||
@ -166,6 +170,6 @@ static void __init at91sam9rl_pmc_setup(struct device_node *np)
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(at91sam9rl_pmc);
|
||||
kfree(at91sam9rl_pmc);
|
||||
}
|
||||
CLK_OF_DECLARE_DRIVER(at91sam9rl_pmc, "atmel,at91sam9rl-pmc", at91sam9rl_pmc_setup);
|
||||
|
@ -150,8 +150,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
at91sam9x5_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
nck(at91sam9x5_systemck), 31, 0);
|
||||
at91sam9x5_pmc = pmc_data_allocate(PMC_PLLACK + 1,
|
||||
nck(at91sam9x5_systemck), 31, 0, 2);
|
||||
if (!at91sam9x5_pmc)
|
||||
return;
|
||||
|
||||
@ -184,6 +184,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9x5_pmc->chws[PMC_PLLACK] = hw;
|
||||
|
||||
hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
@ -227,6 +229,8 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
|
||||
&at91sam9x5_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
at91sam9x5_pmc->pchws[i] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(at91sam9x5_systemck); i++) {
|
||||
@ -278,7 +282,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(at91sam9x5_pmc);
|
||||
kfree(at91sam9x5_pmc);
|
||||
}
|
||||
|
||||
static void __init at91sam9g15_pmc_setup(struct device_node *np)
|
||||
|
@ -67,6 +67,10 @@ struct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
|
||||
if (idx < pmc_data->ngck)
|
||||
return pmc_data->ghws[idx];
|
||||
break;
|
||||
case PMC_TYPE_PROGRAMMABLE:
|
||||
if (idx < pmc_data->npck)
|
||||
return pmc_data->pchws[idx];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -76,48 +80,34 @@ struct clk_hw *of_clk_hw_pmc_get(struct of_phandle_args *clkspec, void *data)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
void pmc_data_free(struct pmc_data *pmc_data)
|
||||
{
|
||||
kfree(pmc_data->chws);
|
||||
kfree(pmc_data->shws);
|
||||
kfree(pmc_data->phws);
|
||||
kfree(pmc_data->ghws);
|
||||
}
|
||||
|
||||
struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
|
||||
unsigned int nperiph, unsigned int ngck)
|
||||
unsigned int nperiph, unsigned int ngck,
|
||||
unsigned int npck)
|
||||
{
|
||||
struct pmc_data *pmc_data = kzalloc(sizeof(*pmc_data), GFP_KERNEL);
|
||||
unsigned int num_clks = ncore + nsystem + nperiph + ngck + npck;
|
||||
struct pmc_data *pmc_data;
|
||||
|
||||
pmc_data = kzalloc(struct_size(pmc_data, hwtable, num_clks),
|
||||
GFP_KERNEL);
|
||||
if (!pmc_data)
|
||||
return NULL;
|
||||
|
||||
pmc_data->ncore = ncore;
|
||||
pmc_data->chws = kcalloc(ncore, sizeof(struct clk_hw *), GFP_KERNEL);
|
||||
if (!pmc_data->chws)
|
||||
goto err;
|
||||
pmc_data->chws = pmc_data->hwtable;
|
||||
|
||||
pmc_data->nsystem = nsystem;
|
||||
pmc_data->shws = kcalloc(nsystem, sizeof(struct clk_hw *), GFP_KERNEL);
|
||||
if (!pmc_data->shws)
|
||||
goto err;
|
||||
pmc_data->shws = pmc_data->chws + ncore;
|
||||
|
||||
pmc_data->nperiph = nperiph;
|
||||
pmc_data->phws = kcalloc(nperiph, sizeof(struct clk_hw *), GFP_KERNEL);
|
||||
if (!pmc_data->phws)
|
||||
goto err;
|
||||
pmc_data->phws = pmc_data->shws + nsystem;
|
||||
|
||||
pmc_data->ngck = ngck;
|
||||
pmc_data->ghws = kcalloc(ngck, sizeof(struct clk_hw *), GFP_KERNEL);
|
||||
if (!pmc_data->ghws)
|
||||
goto err;
|
||||
pmc_data->ghws = pmc_data->phws + nperiph;
|
||||
|
||||
pmc_data->npck = npck;
|
||||
pmc_data->pchws = pmc_data->ghws + ngck;
|
||||
|
||||
return pmc_data;
|
||||
|
||||
err:
|
||||
pmc_data_free(pmc_data);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@ -274,8 +264,11 @@ static int __init pmc_register_ops(void)
|
||||
struct device_node *np;
|
||||
|
||||
np = of_find_matching_node(NULL, sama5d2_pmc_dt_ids);
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
pmcreg = device_node_to_regmap(np);
|
||||
of_node_put(np);
|
||||
if (IS_ERR(pmcreg))
|
||||
return PTR_ERR(pmcreg);
|
||||
|
||||
|
@ -24,6 +24,10 @@ struct pmc_data {
|
||||
struct clk_hw **phws;
|
||||
unsigned int ngck;
|
||||
struct clk_hw **ghws;
|
||||
unsigned int npck;
|
||||
struct clk_hw **pchws;
|
||||
|
||||
struct clk_hw *hwtable[];
|
||||
};
|
||||
|
||||
struct clk_range {
|
||||
@ -94,8 +98,8 @@ struct clk_pcr_layout {
|
||||
#define ndck(a, s) (a[s - 1].id + 1)
|
||||
#define nck(a) (a[ARRAY_SIZE(a) - 1].id + 1)
|
||||
struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
|
||||
unsigned int nperiph, unsigned int ngck);
|
||||
void pmc_data_free(struct pmc_data *pmc_data);
|
||||
unsigned int nperiph, unsigned int ngck,
|
||||
unsigned int npck);
|
||||
|
||||
int of_at91_get_clk_range(struct device_node *np, const char *propname,
|
||||
struct clk_range *range);
|
||||
|
@ -182,10 +182,10 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
sam9x60_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
sam9x60_pmc = pmc_data_allocate(PMC_PLLACK + 1,
|
||||
nck(sam9x60_systemck),
|
||||
nck(sam9x60_periphck),
|
||||
nck(sam9x60_gck));
|
||||
nck(sam9x60_gck), 8);
|
||||
if (!sam9x60_pmc)
|
||||
return;
|
||||
|
||||
@ -214,6 +214,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sam9x60_pmc->chws[PMC_PLLACK] = hw;
|
||||
|
||||
hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "upllck",
|
||||
"main_osc", 1, &upll_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
@ -255,6 +257,8 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
|
||||
&sam9x60_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sam9x60_pmc->pchws[i] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sam9x60_systemck); i++) {
|
||||
@ -299,7 +303,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(sam9x60_pmc);
|
||||
kfree(sam9x60_pmc);
|
||||
}
|
||||
/* Some clks are used for a clocksource */
|
||||
CLK_OF_DECLARE(sam9x60_pmc, "microchip,sam9x60-pmc", sam9x60_pmc_setup);
|
||||
|
@ -89,6 +89,7 @@ static const struct {
|
||||
{ .n = "i2s1_clk", .id = 55, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "can0_clk", .id = 56, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "can1_clk", .id = 57, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "ptc_clk", .id = 58, .r = { .min = 0, .max = 83000000 }, },
|
||||
{ .n = "classd_clk", .id = 59, .r = { .min = 0, .max = 83000000 }, },
|
||||
};
|
||||
|
||||
@ -166,10 +167,10 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
sama5d2_pmc = pmc_data_allocate(PMC_I2S1_MUX + 1,
|
||||
sama5d2_pmc = pmc_data_allocate(PMC_AUDIOPLLCK + 1,
|
||||
nck(sama5d2_systemck),
|
||||
nck(sama5d2_periph32ck),
|
||||
nck(sama5d2_gck));
|
||||
nck(sama5d2_gck), 3);
|
||||
if (!sama5d2_pmc)
|
||||
return;
|
||||
|
||||
@ -202,6 +203,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sama5d2_pmc->chws[PMC_PLLACK] = hw;
|
||||
|
||||
hw = at91_clk_register_audio_pll_frac(regmap, "audiopll_fracck",
|
||||
"mainck");
|
||||
if (IS_ERR(hw))
|
||||
@ -217,6 +220,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sama5d2_pmc->chws[PMC_AUDIOPLLCK] = hw;
|
||||
|
||||
regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr");
|
||||
if (IS_ERR(regmap_sfr))
|
||||
regmap_sfr = NULL;
|
||||
@ -267,6 +272,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
|
||||
&sama5d2_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sama5d2_pmc->pchws[i] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sama5d2_systemck); i++) {
|
||||
@ -350,6 +357,6 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(sama5d2_pmc);
|
||||
kfree(sama5d2_pmc);
|
||||
}
|
||||
CLK_OF_DECLARE_DRIVER(sama5d2_pmc, "atmel,sama5d2-pmc", sama5d2_pmc_setup);
|
||||
|
@ -125,9 +125,9 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
sama5d3_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
sama5d3_pmc = pmc_data_allocate(PMC_PLLACK + 1,
|
||||
nck(sama5d3_systemck),
|
||||
nck(sama5d3_periphck), 0);
|
||||
nck(sama5d3_periphck), 0, 3);
|
||||
if (!sama5d3_pmc)
|
||||
return;
|
||||
|
||||
@ -158,6 +158,8 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sama5d3_pmc->chws[PMC_PLLACK] = hw;
|
||||
|
||||
hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
@ -201,6 +203,8 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
|
||||
&at91sam9x5_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sama5d3_pmc->pchws[i] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sama5d3_systemck); i++) {
|
||||
@ -231,7 +235,7 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(sama5d3_pmc);
|
||||
kfree(sama5d3_pmc);
|
||||
}
|
||||
/*
|
||||
* The TCB is used as the clocksource so its clock is needed early. This means
|
||||
|
@ -140,9 +140,9 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
sama5d4_pmc = pmc_data_allocate(PMC_MCK2 + 1,
|
||||
sama5d4_pmc = pmc_data_allocate(PMC_PLLACK + 1,
|
||||
nck(sama5d4_systemck),
|
||||
nck(sama5d4_periph32ck), 0);
|
||||
nck(sama5d4_periph32ck), 0, 3);
|
||||
if (!sama5d4_pmc)
|
||||
return;
|
||||
|
||||
@ -173,6 +173,8 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sama5d4_pmc->chws[PMC_PLLACK] = hw;
|
||||
|
||||
hw = at91_clk_register_utmi(regmap, NULL, "utmick", "mainck");
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
@ -224,6 +226,8 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
|
||||
&at91sam9x5_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sama5d4_pmc->pchws[i] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sama5d4_systemck); i++) {
|
||||
@ -267,6 +271,6 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(sama5d4_pmc);
|
||||
kfree(sama5d4_pmc);
|
||||
}
|
||||
CLK_OF_DECLARE_DRIVER(sama5d4_pmc, "atmel,sama5d4-pmc", sama5d4_pmc_setup);
|
||||
|
42
drivers/clk/baikal-t1/Kconfig
Normal file
42
drivers/clk/baikal-t1/Kconfig
Normal file
@ -0,0 +1,42 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config CLK_BAIKAL_T1
|
||||
bool "Baikal-T1 Clocks Control Unit interface"
|
||||
depends on (MIPS_BAIKAL_T1 && OF) || COMPILE_TEST
|
||||
default MIPS_BAIKAL_T1
|
||||
help
|
||||
Clocks Control Unit is the core of Baikal-T1 SoC System Controller
|
||||
responsible for the chip subsystems clocking and resetting. It
|
||||
consists of multiple global clock domains, which can be reset by
|
||||
means of the CCU control registers. These domains and devices placed
|
||||
in them are fed with clocks generated by a hierarchy of PLLs,
|
||||
configurable and fixed clock dividers. Enable this option to be able
|
||||
to select Baikal-T1 CCU PLLs and Dividers drivers.
|
||||
|
||||
if CLK_BAIKAL_T1
|
||||
|
||||
config CLK_BT1_CCU_PLL
|
||||
bool "Baikal-T1 CCU PLLs support"
|
||||
select MFD_SYSCON
|
||||
default MIPS_BAIKAL_T1
|
||||
help
|
||||
Enable this to support the PLLs embedded into the Baikal-T1 SoC
|
||||
System Controller. These are five PLLs placed at the root of the
|
||||
clocks hierarchy, right after an external reference oscillator
|
||||
(normally of 25MHz). They are used to generate high frequency
|
||||
signals, which are either directly wired to the consumers (like
|
||||
CPUs, DDR, etc.) or passed over the clock dividers to be only
|
||||
then used as an individual reference clock of a target device.
|
||||
|
||||
config CLK_BT1_CCU_DIV
|
||||
bool "Baikal-T1 CCU Dividers support"
|
||||
select RESET_CONTROLLER
|
||||
select MFD_SYSCON
|
||||
default MIPS_BAIKAL_T1
|
||||
help
|
||||
Enable this to support the CCU dividers used to distribute clocks
|
||||
between AXI-bus and system devices coming from CCU PLLs of Baikal-T1
|
||||
SoC. CCU dividers can be either configurable or with fixed divider,
|
||||
either gateable or ungateable. Some of the CCU dividers can be as well
|
||||
used to reset the domains they're supplying clock to.
|
||||
|
||||
endif
|
3
drivers/clk/baikal-t1/Makefile
Normal file
3
drivers/clk/baikal-t1/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_CLK_BT1_CCU_PLL) += ccu-pll.o clk-ccu-pll.o
|
||||
obj-$(CONFIG_CLK_BT1_CCU_DIV) += ccu-div.o clk-ccu-div.o
|
602
drivers/clk/baikal-t1/ccu-div.c
Normal file
602
drivers/clk/baikal-t1/ccu-div.c
Normal file
@ -0,0 +1,602 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
|
||||
*
|
||||
* Authors:
|
||||
* Serge Semin <Sergey.Semin@baikalelectronics.ru>
|
||||
* Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
|
||||
*
|
||||
* Baikal-T1 CCU Dividers interface driver
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "bt1-ccu-div: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/time64.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "ccu-div.h"
|
||||
|
||||
#define CCU_DIV_CTL 0x00
|
||||
#define CCU_DIV_CTL_EN BIT(0)
|
||||
#define CCU_DIV_CTL_RST BIT(1)
|
||||
#define CCU_DIV_CTL_SET_CLKDIV BIT(2)
|
||||
#define CCU_DIV_CTL_CLKDIV_FLD 4
|
||||
#define CCU_DIV_CTL_CLKDIV_MASK(_width) \
|
||||
GENMASK((_width) + CCU_DIV_CTL_CLKDIV_FLD - 1, CCU_DIV_CTL_CLKDIV_FLD)
|
||||
#define CCU_DIV_CTL_LOCK_SHIFTED BIT(27)
|
||||
#define CCU_DIV_CTL_LOCK_NORMAL BIT(31)
|
||||
|
||||
#define CCU_DIV_RST_DELAY_US 1
|
||||
#define CCU_DIV_LOCK_CHECK_RETRIES 50
|
||||
|
||||
#define CCU_DIV_CLKDIV_MIN 0
|
||||
#define CCU_DIV_CLKDIV_MAX(_mask) \
|
||||
((_mask) >> CCU_DIV_CTL_CLKDIV_FLD)
|
||||
|
||||
/*
|
||||
* Use the next two methods until there are generic field setter and
|
||||
* getter available with non-constant mask support.
|
||||
*/
|
||||
static inline u32 ccu_div_get(u32 mask, u32 val)
|
||||
{
|
||||
return (val & mask) >> CCU_DIV_CTL_CLKDIV_FLD;
|
||||
}
|
||||
|
||||
static inline u32 ccu_div_prep(u32 mask, u32 val)
|
||||
{
|
||||
return (val << CCU_DIV_CTL_CLKDIV_FLD) & mask;
|
||||
}
|
||||
|
||||
static inline unsigned long ccu_div_lock_delay_ns(unsigned long ref_clk,
|
||||
unsigned long div)
|
||||
{
|
||||
u64 ns = 4ULL * (div ?: 1) * NSEC_PER_SEC;
|
||||
|
||||
do_div(ns, ref_clk);
|
||||
|
||||
return ns;
|
||||
}
|
||||
|
||||
static inline unsigned long ccu_div_calc_freq(unsigned long ref_clk,
|
||||
unsigned long div)
|
||||
{
|
||||
return ref_clk / (div ?: 1);
|
||||
}
|
||||
|
||||
static int ccu_div_var_update_clkdiv(struct ccu_div *div,
|
||||
unsigned long parent_rate,
|
||||
unsigned long divider)
|
||||
{
|
||||
unsigned long nd;
|
||||
u32 val = 0;
|
||||
u32 lock;
|
||||
int count;
|
||||
|
||||
nd = ccu_div_lock_delay_ns(parent_rate, divider);
|
||||
|
||||
if (div->features & CCU_DIV_LOCK_SHIFTED)
|
||||
lock = CCU_DIV_CTL_LOCK_SHIFTED;
|
||||
else
|
||||
lock = CCU_DIV_CTL_LOCK_NORMAL;
|
||||
|
||||
regmap_update_bits(div->sys_regs, div->reg_ctl,
|
||||
CCU_DIV_CTL_SET_CLKDIV, CCU_DIV_CTL_SET_CLKDIV);
|
||||
|
||||
/*
|
||||
* Until there is nsec-version of readl_poll_timeout() is available
|
||||
* we have to implement the next polling loop.
|
||||
*/
|
||||
count = CCU_DIV_LOCK_CHECK_RETRIES;
|
||||
do {
|
||||
ndelay(nd);
|
||||
regmap_read(div->sys_regs, div->reg_ctl, &val);
|
||||
if (val & lock)
|
||||
return 0;
|
||||
} while (--count);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int ccu_div_var_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_hw *parent_hw = clk_hw_get_parent(hw);
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
unsigned long flags;
|
||||
u32 val = 0;
|
||||
int ret;
|
||||
|
||||
if (!parent_hw) {
|
||||
pr_err("Can't enable '%s' with no parent", clk_hw_get_name(hw));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_read(div->sys_regs, div->reg_ctl, &val);
|
||||
if (val & CCU_DIV_CTL_EN)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&div->lock, flags);
|
||||
ret = ccu_div_var_update_clkdiv(div, clk_hw_get_rate(parent_hw),
|
||||
ccu_div_get(div->mask, val));
|
||||
if (!ret)
|
||||
regmap_update_bits(div->sys_regs, div->reg_ctl,
|
||||
CCU_DIV_CTL_EN, CCU_DIV_CTL_EN);
|
||||
spin_unlock_irqrestore(&div->lock, flags);
|
||||
if (ret)
|
||||
pr_err("Divider '%s' lock timed out\n", clk_hw_get_name(hw));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ccu_div_gate_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&div->lock, flags);
|
||||
regmap_update_bits(div->sys_regs, div->reg_ctl,
|
||||
CCU_DIV_CTL_EN, CCU_DIV_CTL_EN);
|
||||
spin_unlock_irqrestore(&div->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ccu_div_gate_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&div->lock, flags);
|
||||
regmap_update_bits(div->sys_regs, div->reg_ctl, CCU_DIV_CTL_EN, 0);
|
||||
spin_unlock_irqrestore(&div->lock, flags);
|
||||
}
|
||||
|
||||
static int ccu_div_gate_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
u32 val = 0;
|
||||
|
||||
regmap_read(div->sys_regs, div->reg_ctl, &val);
|
||||
|
||||
return !!(val & CCU_DIV_CTL_EN);
|
||||
}
|
||||
|
||||
static unsigned long ccu_div_var_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
unsigned long divider;
|
||||
u32 val = 0;
|
||||
|
||||
regmap_read(div->sys_regs, div->reg_ctl, &val);
|
||||
divider = ccu_div_get(div->mask, val);
|
||||
|
||||
return ccu_div_calc_freq(parent_rate, divider);
|
||||
}
|
||||
|
||||
static inline unsigned long ccu_div_var_calc_divider(unsigned long rate,
|
||||
unsigned long parent_rate,
|
||||
unsigned int mask)
|
||||
{
|
||||
unsigned long divider;
|
||||
|
||||
divider = parent_rate / rate;
|
||||
return clamp_t(unsigned long, divider, CCU_DIV_CLKDIV_MIN,
|
||||
CCU_DIV_CLKDIV_MAX(mask));
|
||||
}
|
||||
|
||||
static long ccu_div_var_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
unsigned long divider;
|
||||
|
||||
divider = ccu_div_var_calc_divider(rate, *parent_rate, div->mask);
|
||||
|
||||
return ccu_div_calc_freq(*parent_rate, divider);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is used for the clock divider blocks, which support the
|
||||
* on-the-fly rate change. So due to lacking the EN bit functionality
|
||||
* they can't be gated before the rate adjustment.
|
||||
*/
|
||||
static int ccu_div_var_set_rate_slow(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
unsigned long flags, divider;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
divider = ccu_div_var_calc_divider(rate, parent_rate, div->mask);
|
||||
if (divider == 1 && div->features & CCU_DIV_SKIP_ONE) {
|
||||
divider = 0;
|
||||
} else if (div->features & CCU_DIV_SKIP_ONE_TO_THREE) {
|
||||
if (divider == 1 || divider == 2)
|
||||
divider = 0;
|
||||
else if (divider == 3)
|
||||
divider = 4;
|
||||
}
|
||||
|
||||
val = ccu_div_prep(div->mask, divider);
|
||||
|
||||
spin_lock_irqsave(&div->lock, flags);
|
||||
regmap_update_bits(div->sys_regs, div->reg_ctl, div->mask, val);
|
||||
ret = ccu_div_var_update_clkdiv(div, parent_rate, divider);
|
||||
spin_unlock_irqrestore(&div->lock, flags);
|
||||
if (ret)
|
||||
pr_err("Divider '%s' lock timed out\n", clk_hw_get_name(hw));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is used for the clock divider blocks, which don't support
|
||||
* the on-the-fly rate change.
|
||||
*/
|
||||
static int ccu_div_var_set_rate_fast(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
unsigned long flags, divider;
|
||||
u32 val;
|
||||
|
||||
divider = ccu_div_var_calc_divider(rate, parent_rate, div->mask);
|
||||
val = ccu_div_prep(div->mask, divider);
|
||||
|
||||
/*
|
||||
* Also disable the clock divider block if it was enabled by default
|
||||
* or by the bootloader.
|
||||
*/
|
||||
spin_lock_irqsave(&div->lock, flags);
|
||||
regmap_update_bits(div->sys_regs, div->reg_ctl,
|
||||
div->mask | CCU_DIV_CTL_EN, val);
|
||||
spin_unlock_irqrestore(&div->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long ccu_div_fixed_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
|
||||
return ccu_div_calc_freq(parent_rate, div->divider);
|
||||
}
|
||||
|
||||
static long ccu_div_fixed_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
|
||||
return ccu_div_calc_freq(*parent_rate, div->divider);
|
||||
}
|
||||
|
||||
static int ccu_div_fixed_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ccu_div_reset_domain(struct ccu_div *div)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!div || !(div->features & CCU_DIV_RESET_DOMAIN))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&div->lock, flags);
|
||||
regmap_update_bits(div->sys_regs, div->reg_ctl,
|
||||
CCU_DIV_CTL_RST, CCU_DIV_CTL_RST);
|
||||
spin_unlock_irqrestore(&div->lock, flags);
|
||||
|
||||
/* The next delay must be enough to cover all the resets. */
|
||||
udelay(CCU_DIV_RST_DELAY_US);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
struct ccu_div_dbgfs_bit {
|
||||
struct ccu_div *div;
|
||||
const char *name;
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
#define CCU_DIV_DBGFS_BIT_ATTR(_name, _mask) { \
|
||||
.name = _name, \
|
||||
.mask = _mask \
|
||||
}
|
||||
|
||||
static const struct ccu_div_dbgfs_bit ccu_div_bits[] = {
|
||||
CCU_DIV_DBGFS_BIT_ATTR("div_en", CCU_DIV_CTL_EN),
|
||||
CCU_DIV_DBGFS_BIT_ATTR("div_rst", CCU_DIV_CTL_RST),
|
||||
CCU_DIV_DBGFS_BIT_ATTR("div_bypass", CCU_DIV_CTL_SET_CLKDIV),
|
||||
CCU_DIV_DBGFS_BIT_ATTR("div_lock", CCU_DIV_CTL_LOCK_NORMAL)
|
||||
};
|
||||
|
||||
#define CCU_DIV_DBGFS_BIT_NUM ARRAY_SIZE(ccu_div_bits)
|
||||
|
||||
/*
|
||||
* It can be dangerous to change the Divider settings behind clock framework
|
||||
* back, therefore we don't provide any kernel config based compile time option
|
||||
* for this feature to enable.
|
||||
*/
|
||||
#undef CCU_DIV_ALLOW_WRITE_DEBUGFS
|
||||
#ifdef CCU_DIV_ALLOW_WRITE_DEBUGFS
|
||||
|
||||
static int ccu_div_dbgfs_bit_set(void *priv, u64 val)
|
||||
{
|
||||
const struct ccu_div_dbgfs_bit *bit = priv;
|
||||
struct ccu_div *div = bit->div;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&div->lock, flags);
|
||||
regmap_update_bits(div->sys_regs, div->reg_ctl,
|
||||
bit->mask, val ? bit->mask : 0);
|
||||
spin_unlock_irqrestore(&div->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ccu_div_dbgfs_var_clkdiv_set(void *priv, u64 val)
|
||||
{
|
||||
struct ccu_div *div = priv;
|
||||
unsigned long flags;
|
||||
u32 data;
|
||||
|
||||
val = clamp_t(u64, val, CCU_DIV_CLKDIV_MIN,
|
||||
CCU_DIV_CLKDIV_MAX(div->mask));
|
||||
data = ccu_div_prep(div->mask, val);
|
||||
|
||||
spin_lock_irqsave(&div->lock, flags);
|
||||
regmap_update_bits(div->sys_regs, div->reg_ctl, div->mask, data);
|
||||
spin_unlock_irqrestore(&div->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ccu_div_dbgfs_mode 0644
|
||||
|
||||
#else /* !CCU_DIV_ALLOW_WRITE_DEBUGFS */
|
||||
|
||||
#define ccu_div_dbgfs_bit_set NULL
|
||||
#define ccu_div_dbgfs_var_clkdiv_set NULL
|
||||
#define ccu_div_dbgfs_mode 0444
|
||||
|
||||
#endif /* !CCU_DIV_ALLOW_WRITE_DEBUGFS */
|
||||
|
||||
static int ccu_div_dbgfs_bit_get(void *priv, u64 *val)
|
||||
{
|
||||
const struct ccu_div_dbgfs_bit *bit = priv;
|
||||
struct ccu_div *div = bit->div;
|
||||
u32 data = 0;
|
||||
|
||||
regmap_read(div->sys_regs, div->reg_ctl, &data);
|
||||
*val = !!(data & bit->mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_bit_fops,
|
||||
ccu_div_dbgfs_bit_get, ccu_div_dbgfs_bit_set, "%llu\n");
|
||||
|
||||
static int ccu_div_dbgfs_var_clkdiv_get(void *priv, u64 *val)
|
||||
{
|
||||
struct ccu_div *div = priv;
|
||||
u32 data = 0;
|
||||
|
||||
regmap_read(div->sys_regs, div->reg_ctl, &data);
|
||||
*val = ccu_div_get(div->mask, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_var_clkdiv_fops,
|
||||
ccu_div_dbgfs_var_clkdiv_get, ccu_div_dbgfs_var_clkdiv_set, "%llu\n");
|
||||
|
||||
static int ccu_div_dbgfs_fixed_clkdiv_get(void *priv, u64 *val)
|
||||
{
|
||||
struct ccu_div *div = priv;
|
||||
|
||||
*val = div->divider;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(ccu_div_dbgfs_fixed_clkdiv_fops,
|
||||
ccu_div_dbgfs_fixed_clkdiv_get, NULL, "%llu\n");
|
||||
|
||||
static void ccu_div_var_debug_init(struct clk_hw *hw, struct dentry *dentry)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
struct ccu_div_dbgfs_bit *bits;
|
||||
int didx, bidx, num = 2;
|
||||
const char *name;
|
||||
|
||||
num += !!(div->flags & CLK_SET_RATE_GATE) +
|
||||
!!(div->features & CCU_DIV_RESET_DOMAIN);
|
||||
|
||||
bits = kcalloc(num, sizeof(*bits), GFP_KERNEL);
|
||||
if (!bits)
|
||||
return;
|
||||
|
||||
for (didx = 0, bidx = 0; bidx < CCU_DIV_DBGFS_BIT_NUM; ++bidx) {
|
||||
name = ccu_div_bits[bidx].name;
|
||||
if (!(div->flags & CLK_SET_RATE_GATE) &&
|
||||
!strcmp("div_en", name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(div->features & CCU_DIV_RESET_DOMAIN) &&
|
||||
!strcmp("div_rst", name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bits[didx] = ccu_div_bits[bidx];
|
||||
bits[didx].div = div;
|
||||
|
||||
if (div->features & CCU_DIV_LOCK_SHIFTED &&
|
||||
!strcmp("div_lock", name)) {
|
||||
bits[didx].mask = CCU_DIV_CTL_LOCK_SHIFTED;
|
||||
}
|
||||
|
||||
debugfs_create_file_unsafe(bits[didx].name, ccu_div_dbgfs_mode,
|
||||
dentry, &bits[didx],
|
||||
&ccu_div_dbgfs_bit_fops);
|
||||
++didx;
|
||||
}
|
||||
|
||||
debugfs_create_file_unsafe("div_clkdiv", ccu_div_dbgfs_mode, dentry,
|
||||
div, &ccu_div_dbgfs_var_clkdiv_fops);
|
||||
}
|
||||
|
||||
static void ccu_div_gate_debug_init(struct clk_hw *hw, struct dentry *dentry)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
struct ccu_div_dbgfs_bit *bit;
|
||||
|
||||
bit = kmalloc(sizeof(*bit), GFP_KERNEL);
|
||||
if (!bit)
|
||||
return;
|
||||
|
||||
*bit = ccu_div_bits[0];
|
||||
bit->div = div;
|
||||
debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit,
|
||||
&ccu_div_dbgfs_bit_fops);
|
||||
|
||||
debugfs_create_file_unsafe("div_clkdiv", 0400, dentry, div,
|
||||
&ccu_div_dbgfs_fixed_clkdiv_fops);
|
||||
}
|
||||
|
||||
static void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
|
||||
debugfs_create_file_unsafe("div_clkdiv", 0400, dentry, div,
|
||||
&ccu_div_dbgfs_fixed_clkdiv_fops);
|
||||
}
|
||||
|
||||
#else /* !CONFIG_DEBUG_FS */
|
||||
|
||||
#define ccu_div_var_debug_init NULL
|
||||
#define ccu_div_gate_debug_init NULL
|
||||
#define ccu_div_fixed_debug_init NULL
|
||||
|
||||
#endif /* !CONFIG_DEBUG_FS */
|
||||
|
||||
static const struct clk_ops ccu_div_var_gate_to_set_ops = {
|
||||
.enable = ccu_div_var_enable,
|
||||
.disable = ccu_div_gate_disable,
|
||||
.is_enabled = ccu_div_gate_is_enabled,
|
||||
.recalc_rate = ccu_div_var_recalc_rate,
|
||||
.round_rate = ccu_div_var_round_rate,
|
||||
.set_rate = ccu_div_var_set_rate_fast,
|
||||
.debug_init = ccu_div_var_debug_init
|
||||
};
|
||||
|
||||
static const struct clk_ops ccu_div_var_nogate_ops = {
|
||||
.recalc_rate = ccu_div_var_recalc_rate,
|
||||
.round_rate = ccu_div_var_round_rate,
|
||||
.set_rate = ccu_div_var_set_rate_slow,
|
||||
.debug_init = ccu_div_var_debug_init
|
||||
};
|
||||
|
||||
static const struct clk_ops ccu_div_gate_ops = {
|
||||
.enable = ccu_div_gate_enable,
|
||||
.disable = ccu_div_gate_disable,
|
||||
.is_enabled = ccu_div_gate_is_enabled,
|
||||
.recalc_rate = ccu_div_fixed_recalc_rate,
|
||||
.round_rate = ccu_div_fixed_round_rate,
|
||||
.set_rate = ccu_div_fixed_set_rate,
|
||||
.debug_init = ccu_div_gate_debug_init
|
||||
};
|
||||
|
||||
static const struct clk_ops ccu_div_fixed_ops = {
|
||||
.recalc_rate = ccu_div_fixed_recalc_rate,
|
||||
.round_rate = ccu_div_fixed_round_rate,
|
||||
.set_rate = ccu_div_fixed_set_rate,
|
||||
.debug_init = ccu_div_fixed_debug_init
|
||||
};
|
||||
|
||||
struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *div_init)
|
||||
{
|
||||
struct clk_parent_data parent_data = { };
|
||||
struct clk_init_data hw_init = { };
|
||||
struct ccu_div *div;
|
||||
int ret;
|
||||
|
||||
if (!div_init)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
||||
if (!div)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* Note since Baikal-T1 System Controller registers are MMIO-backed
|
||||
* we won't check the regmap IO operations return status, because it
|
||||
* must be zero anyway.
|
||||
*/
|
||||
div->hw.init = &hw_init;
|
||||
div->id = div_init->id;
|
||||
div->reg_ctl = div_init->base + CCU_DIV_CTL;
|
||||
div->sys_regs = div_init->sys_regs;
|
||||
div->flags = div_init->flags;
|
||||
div->features = div_init->features;
|
||||
spin_lock_init(&div->lock);
|
||||
|
||||
hw_init.name = div_init->name;
|
||||
hw_init.flags = div_init->flags;
|
||||
|
||||
if (div_init->type == CCU_DIV_VAR) {
|
||||
if (hw_init.flags & CLK_SET_RATE_GATE)
|
||||
hw_init.ops = &ccu_div_var_gate_to_set_ops;
|
||||
else
|
||||
hw_init.ops = &ccu_div_var_nogate_ops;
|
||||
div->mask = CCU_DIV_CTL_CLKDIV_MASK(div_init->width);
|
||||
} else if (div_init->type == CCU_DIV_GATE) {
|
||||
hw_init.ops = &ccu_div_gate_ops;
|
||||
div->divider = div_init->divider;
|
||||
} else if (div_init->type == CCU_DIV_FIXED) {
|
||||
hw_init.ops = &ccu_div_fixed_ops;
|
||||
div->divider = div_init->divider;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto err_free_div;
|
||||
}
|
||||
|
||||
if (!div_init->parent_name) {
|
||||
ret = -EINVAL;
|
||||
goto err_free_div;
|
||||
}
|
||||
parent_data.fw_name = div_init->parent_name;
|
||||
hw_init.parent_data = &parent_data;
|
||||
hw_init.num_parents = 1;
|
||||
|
||||
ret = of_clk_hw_register(div_init->np, &div->hw);
|
||||
if (ret)
|
||||
goto err_free_div;
|
||||
|
||||
return div;
|
||||
|
||||
err_free_div:
|
||||
kfree(div);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void ccu_div_hw_unregister(struct ccu_div *div)
|
||||
{
|
||||
clk_hw_unregister(&div->hw);
|
||||
|
||||
kfree(div);
|
||||
}
|
110
drivers/clk/baikal-t1/ccu-div.h
Normal file
110
drivers/clk/baikal-t1/ccu-div.h
Normal file
@ -0,0 +1,110 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
|
||||
*
|
||||
* Baikal-T1 CCU Dividers interface driver
|
||||
*/
|
||||
#ifndef __CLK_BT1_CCU_DIV_H__
|
||||
#define __CLK_BT1_CCU_DIV_H__
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/*
|
||||
* CCU Divider private flags
|
||||
* @CCU_DIV_SKIP_ONE: Due to some reason divider can't be set to 1.
|
||||
* It can be 0 though, which is functionally the same.
|
||||
* @CCU_DIV_SKIP_ONE_TO_THREE: For some reason divider can't be within [1,3].
|
||||
* It can be either 0 or greater than 3.
|
||||
* @CCU_DIV_LOCK_SHIFTED: Find lock-bit at non-standard position.
|
||||
* @CCU_DIV_RESET_DOMAIN: Provide reset clock domain method.
|
||||
*/
|
||||
#define CCU_DIV_SKIP_ONE BIT(1)
|
||||
#define CCU_DIV_SKIP_ONE_TO_THREE BIT(2)
|
||||
#define CCU_DIV_LOCK_SHIFTED BIT(3)
|
||||
#define CCU_DIV_RESET_DOMAIN BIT(4)
|
||||
|
||||
/*
|
||||
* enum ccu_div_type - CCU Divider types
|
||||
* @CCU_DIV_VAR: Clocks gate with variable divider.
|
||||
* @CCU_DIV_GATE: Clocks gate with fixed divider.
|
||||
* @CCU_DIV_FIXED: Ungateable clock with fixed divider.
|
||||
*/
|
||||
enum ccu_div_type {
|
||||
CCU_DIV_VAR,
|
||||
CCU_DIV_GATE,
|
||||
CCU_DIV_FIXED
|
||||
};
|
||||
|
||||
/*
|
||||
* struct ccu_div_init_data - CCU Divider initialization data
|
||||
* @id: Clocks private identifier.
|
||||
* @name: Clocks name.
|
||||
* @parent_name: Parent clocks name in a fw node.
|
||||
* @base: Divider register base address with respect to the sys_regs base.
|
||||
* @sys_regs: Baikal-T1 System Controller registers map.
|
||||
* @np: Pointer to the node describing the CCU Dividers.
|
||||
* @type: CCU divider type (variable, fixed with and without gate).
|
||||
* @width: Divider width if it's variable.
|
||||
* @divider: Divider fixed value.
|
||||
* @flags: CCU Divider clock flags.
|
||||
* @features: CCU Divider private features.
|
||||
*/
|
||||
struct ccu_div_init_data {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned int base;
|
||||
struct regmap *sys_regs;
|
||||
struct device_node *np;
|
||||
enum ccu_div_type type;
|
||||
union {
|
||||
unsigned int width;
|
||||
unsigned int divider;
|
||||
};
|
||||
unsigned long flags;
|
||||
unsigned long features;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct ccu_div - CCU Divider descriptor
|
||||
* @hw: clk_hw of the divider.
|
||||
* @id: Clock private identifier.
|
||||
* @reg_ctl: Divider control register base address.
|
||||
* @sys_regs: Baikal-T1 System Controller registers map.
|
||||
* @lock: Divider state change spin-lock.
|
||||
* @mask: Divider field mask.
|
||||
* @divider: Divider fixed value.
|
||||
* @flags: Divider clock flags.
|
||||
* @features: CCU Divider private features.
|
||||
*/
|
||||
struct ccu_div {
|
||||
struct clk_hw hw;
|
||||
unsigned int id;
|
||||
unsigned int reg_ctl;
|
||||
struct regmap *sys_regs;
|
||||
spinlock_t lock;
|
||||
union {
|
||||
u32 mask;
|
||||
unsigned int divider;
|
||||
};
|
||||
unsigned long flags;
|
||||
unsigned long features;
|
||||
};
|
||||
#define to_ccu_div(_hw) container_of(_hw, struct ccu_div, hw)
|
||||
|
||||
static inline struct clk_hw *ccu_div_get_clk_hw(struct ccu_div *div)
|
||||
{
|
||||
return div ? &div->hw : NULL;
|
||||
}
|
||||
|
||||
struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *init);
|
||||
|
||||
void ccu_div_hw_unregister(struct ccu_div *div);
|
||||
|
||||
int ccu_div_reset_domain(struct ccu_div *div);
|
||||
|
||||
#endif /* __CLK_BT1_CCU_DIV_H__ */
|
558
drivers/clk/baikal-t1/ccu-pll.c
Normal file
558
drivers/clk/baikal-t1/ccu-pll.c
Normal file
@ -0,0 +1,558 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
|
||||
*
|
||||
* Authors:
|
||||
* Serge Semin <Sergey.Semin@baikalelectronics.ru>
|
||||
* Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
|
||||
*
|
||||
* Baikal-T1 CCU PLL interface driver
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "bt1-ccu-pll: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/time64.h>
|
||||
#include <linux/rational.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "ccu-pll.h"
|
||||
|
||||
#define CCU_PLL_CTL 0x000
|
||||
#define CCU_PLL_CTL_EN BIT(0)
|
||||
#define CCU_PLL_CTL_RST BIT(1)
|
||||
#define CCU_PLL_CTL_CLKR_FLD 2
|
||||
#define CCU_PLL_CTL_CLKR_MASK GENMASK(7, CCU_PLL_CTL_CLKR_FLD)
|
||||
#define CCU_PLL_CTL_CLKF_FLD 8
|
||||
#define CCU_PLL_CTL_CLKF_MASK GENMASK(20, CCU_PLL_CTL_CLKF_FLD)
|
||||
#define CCU_PLL_CTL_CLKOD_FLD 21
|
||||
#define CCU_PLL_CTL_CLKOD_MASK GENMASK(24, CCU_PLL_CTL_CLKOD_FLD)
|
||||
#define CCU_PLL_CTL_BYPASS BIT(30)
|
||||
#define CCU_PLL_CTL_LOCK BIT(31)
|
||||
#define CCU_PLL_CTL1 0x004
|
||||
#define CCU_PLL_CTL1_BWADJ_FLD 3
|
||||
#define CCU_PLL_CTL1_BWADJ_MASK GENMASK(14, CCU_PLL_CTL1_BWADJ_FLD)
|
||||
|
||||
#define CCU_PLL_LOCK_CHECK_RETRIES 50
|
||||
|
||||
#define CCU_PLL_NR_MAX \
|
||||
((CCU_PLL_CTL_CLKR_MASK >> CCU_PLL_CTL_CLKR_FLD) + 1)
|
||||
#define CCU_PLL_NF_MAX \
|
||||
((CCU_PLL_CTL_CLKF_MASK >> (CCU_PLL_CTL_CLKF_FLD + 1)) + 1)
|
||||
#define CCU_PLL_OD_MAX \
|
||||
((CCU_PLL_CTL_CLKOD_MASK >> CCU_PLL_CTL_CLKOD_FLD) + 1)
|
||||
#define CCU_PLL_NB_MAX \
|
||||
((CCU_PLL_CTL1_BWADJ_MASK >> CCU_PLL_CTL1_BWADJ_FLD) + 1)
|
||||
#define CCU_PLL_FDIV_MIN 427000UL
|
||||
#define CCU_PLL_FDIV_MAX 3500000000UL
|
||||
#define CCU_PLL_FOUT_MIN 200000000UL
|
||||
#define CCU_PLL_FOUT_MAX 2500000000UL
|
||||
#define CCU_PLL_FVCO_MIN 700000000UL
|
||||
#define CCU_PLL_FVCO_MAX 3500000000UL
|
||||
#define CCU_PLL_CLKOD_FACTOR 2
|
||||
|
||||
static inline unsigned long ccu_pll_lock_delay_us(unsigned long ref_clk,
|
||||
unsigned long nr)
|
||||
{
|
||||
u64 us = 500ULL * nr * USEC_PER_SEC;
|
||||
|
||||
do_div(us, ref_clk);
|
||||
|
||||
return us;
|
||||
}
|
||||
|
||||
static inline unsigned long ccu_pll_calc_freq(unsigned long ref_clk,
|
||||
unsigned long nr,
|
||||
unsigned long nf,
|
||||
unsigned long od)
|
||||
{
|
||||
u64 tmp = ref_clk;
|
||||
|
||||
do_div(tmp, nr);
|
||||
tmp *= nf;
|
||||
do_div(tmp, od);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static int ccu_pll_reset(struct ccu_pll *pll, unsigned long ref_clk,
|
||||
unsigned long nr)
|
||||
{
|
||||
unsigned long ud, ut;
|
||||
u32 val;
|
||||
|
||||
ud = ccu_pll_lock_delay_us(ref_clk, nr);
|
||||
ut = ud * CCU_PLL_LOCK_CHECK_RETRIES;
|
||||
|
||||
regmap_update_bits(pll->sys_regs, pll->reg_ctl,
|
||||
CCU_PLL_CTL_RST, CCU_PLL_CTL_RST);
|
||||
|
||||
return regmap_read_poll_timeout_atomic(pll->sys_regs, pll->reg_ctl, val,
|
||||
val & CCU_PLL_CTL_LOCK, ud, ut);
|
||||
}
|
||||
|
||||
static int ccu_pll_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_hw *parent_hw = clk_hw_get_parent(hw);
|
||||
struct ccu_pll *pll = to_ccu_pll(hw);
|
||||
unsigned long flags;
|
||||
u32 val = 0;
|
||||
int ret;
|
||||
|
||||
if (!parent_hw) {
|
||||
pr_err("Can't enable '%s' with no parent", clk_hw_get_name(hw));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
regmap_read(pll->sys_regs, pll->reg_ctl, &val);
|
||||
if (val & CCU_PLL_CTL_EN)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&pll->lock, flags);
|
||||
regmap_write(pll->sys_regs, pll->reg_ctl, val | CCU_PLL_CTL_EN);
|
||||
ret = ccu_pll_reset(pll, clk_hw_get_rate(parent_hw),
|
||||
FIELD_GET(CCU_PLL_CTL_CLKR_MASK, val) + 1);
|
||||
spin_unlock_irqrestore(&pll->lock, flags);
|
||||
if (ret)
|
||||
pr_err("PLL '%s' reset timed out\n", clk_hw_get_name(hw));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ccu_pll_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct ccu_pll *pll = to_ccu_pll(hw);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pll->lock, flags);
|
||||
regmap_update_bits(pll->sys_regs, pll->reg_ctl, CCU_PLL_CTL_EN, 0);
|
||||
spin_unlock_irqrestore(&pll->lock, flags);
|
||||
}
|
||||
|
||||
static int ccu_pll_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct ccu_pll *pll = to_ccu_pll(hw);
|
||||
u32 val = 0;
|
||||
|
||||
regmap_read(pll->sys_regs, pll->reg_ctl, &val);
|
||||
|
||||
return !!(val & CCU_PLL_CTL_EN);
|
||||
}
|
||||
|
||||
static unsigned long ccu_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct ccu_pll *pll = to_ccu_pll(hw);
|
||||
unsigned long nr, nf, od;
|
||||
u32 val = 0;
|
||||
|
||||
regmap_read(pll->sys_regs, pll->reg_ctl, &val);
|
||||
nr = FIELD_GET(CCU_PLL_CTL_CLKR_MASK, val) + 1;
|
||||
nf = FIELD_GET(CCU_PLL_CTL_CLKF_MASK, val) + 1;
|
||||
od = FIELD_GET(CCU_PLL_CTL_CLKOD_MASK, val) + 1;
|
||||
|
||||
return ccu_pll_calc_freq(parent_rate, nr, nf, od);
|
||||
}
|
||||
|
||||
static void ccu_pll_calc_factors(unsigned long rate, unsigned long parent_rate,
|
||||
unsigned long *nr, unsigned long *nf,
|
||||
unsigned long *od)
|
||||
{
|
||||
unsigned long err, freq, min_err = ULONG_MAX;
|
||||
unsigned long num, denom, n1, d1, nri;
|
||||
unsigned long nr_max, nf_max, od_max;
|
||||
|
||||
/*
|
||||
* Make sure PLL is working with valid input signal (Fdiv). If
|
||||
* you want to speed the function up just reduce CCU_PLL_NR_MAX.
|
||||
* This will cause a worse approximation though.
|
||||
*/
|
||||
nri = (parent_rate / CCU_PLL_FDIV_MAX) + 1;
|
||||
nr_max = min(parent_rate / CCU_PLL_FDIV_MIN, CCU_PLL_NR_MAX);
|
||||
|
||||
/*
|
||||
* Find a closest [nr;nf;od] vector taking into account the
|
||||
* limitations like: 1) 700MHz <= Fvco <= 3.5GHz, 2) PLL Od is
|
||||
* either 1 or even number within the acceptable range (alas 1s
|
||||
* is also excluded by the next loop).
|
||||
*/
|
||||
for (; nri <= nr_max; ++nri) {
|
||||
/* Use Od factor to fulfill the limitation 2). */
|
||||
num = CCU_PLL_CLKOD_FACTOR * rate;
|
||||
denom = parent_rate / nri;
|
||||
|
||||
/*
|
||||
* Make sure Fvco is within the acceptable range to fulfill
|
||||
* the condition 1). Note due to the CCU_PLL_CLKOD_FACTOR value
|
||||
* the actual upper limit is also divided by that factor.
|
||||
* It's not big problem for us since practically there is no
|
||||
* need in clocks with that high frequency.
|
||||
*/
|
||||
nf_max = min(CCU_PLL_FVCO_MAX / denom, CCU_PLL_NF_MAX);
|
||||
od_max = CCU_PLL_OD_MAX / CCU_PLL_CLKOD_FACTOR;
|
||||
|
||||
/*
|
||||
* Bypass the out-of-bound values, which can't be properly
|
||||
* handled by the rational fraction approximation algorithm.
|
||||
*/
|
||||
if (num / denom >= nf_max) {
|
||||
n1 = nf_max;
|
||||
d1 = 1;
|
||||
} else if (denom / num >= od_max) {
|
||||
n1 = 1;
|
||||
d1 = od_max;
|
||||
} else {
|
||||
rational_best_approximation(num, denom, nf_max, od_max,
|
||||
&n1, &d1);
|
||||
}
|
||||
|
||||
/* Select the best approximation of the target rate. */
|
||||
freq = ccu_pll_calc_freq(parent_rate, nri, n1, d1);
|
||||
err = abs((int64_t)freq - num);
|
||||
if (err < min_err) {
|
||||
min_err = err;
|
||||
*nr = nri;
|
||||
*nf = n1;
|
||||
*od = CCU_PLL_CLKOD_FACTOR * d1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static long ccu_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned long nr = 1, nf = 1, od = 1;
|
||||
|
||||
ccu_pll_calc_factors(rate, *parent_rate, &nr, &nf, &od);
|
||||
|
||||
return ccu_pll_calc_freq(*parent_rate, nr, nf, od);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is used for PLLs, which support the on-the-fly dividers
|
||||
* adjustment. So there is no need in gating such clocks.
|
||||
*/
|
||||
static int ccu_pll_set_rate_reset(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct ccu_pll *pll = to_ccu_pll(hw);
|
||||
unsigned long nr, nf, od;
|
||||
unsigned long flags;
|
||||
u32 mask, val;
|
||||
int ret;
|
||||
|
||||
ccu_pll_calc_factors(rate, parent_rate, &nr, &nf, &od);
|
||||
|
||||
mask = CCU_PLL_CTL_CLKR_MASK | CCU_PLL_CTL_CLKF_MASK |
|
||||
CCU_PLL_CTL_CLKOD_MASK;
|
||||
val = FIELD_PREP(CCU_PLL_CTL_CLKR_MASK, nr - 1) |
|
||||
FIELD_PREP(CCU_PLL_CTL_CLKF_MASK, nf - 1) |
|
||||
FIELD_PREP(CCU_PLL_CTL_CLKOD_MASK, od - 1);
|
||||
|
||||
spin_lock_irqsave(&pll->lock, flags);
|
||||
regmap_update_bits(pll->sys_regs, pll->reg_ctl, mask, val);
|
||||
ret = ccu_pll_reset(pll, parent_rate, nr);
|
||||
spin_unlock_irqrestore(&pll->lock, flags);
|
||||
if (ret)
|
||||
pr_err("PLL '%s' reset timed out\n", clk_hw_get_name(hw));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is used for PLLs, which don't support the on-the-fly dividers
|
||||
* adjustment. So the corresponding clocks are supposed to be gated first.
|
||||
*/
|
||||
static int ccu_pll_set_rate_norst(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct ccu_pll *pll = to_ccu_pll(hw);
|
||||
unsigned long nr, nf, od;
|
||||
unsigned long flags;
|
||||
u32 mask, val;
|
||||
|
||||
ccu_pll_calc_factors(rate, parent_rate, &nr, &nf, &od);
|
||||
|
||||
/*
|
||||
* Disable PLL if it was enabled by default or left enabled by the
|
||||
* system bootloader.
|
||||
*/
|
||||
mask = CCU_PLL_CTL_CLKR_MASK | CCU_PLL_CTL_CLKF_MASK |
|
||||
CCU_PLL_CTL_CLKOD_MASK | CCU_PLL_CTL_EN;
|
||||
val = FIELD_PREP(CCU_PLL_CTL_CLKR_MASK, nr - 1) |
|
||||
FIELD_PREP(CCU_PLL_CTL_CLKF_MASK, nf - 1) |
|
||||
FIELD_PREP(CCU_PLL_CTL_CLKOD_MASK, od - 1);
|
||||
|
||||
spin_lock_irqsave(&pll->lock, flags);
|
||||
regmap_update_bits(pll->sys_regs, pll->reg_ctl, mask, val);
|
||||
spin_unlock_irqrestore(&pll->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
struct ccu_pll_dbgfs_bit {
|
||||
struct ccu_pll *pll;
|
||||
const char *name;
|
||||
unsigned int reg;
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
struct ccu_pll_dbgfs_fld {
|
||||
struct ccu_pll *pll;
|
||||
const char *name;
|
||||
unsigned int reg;
|
||||
unsigned int lsb;
|
||||
u32 mask;
|
||||
u32 min;
|
||||
u32 max;
|
||||
};
|
||||
|
||||
#define CCU_PLL_DBGFS_BIT_ATTR(_name, _reg, _mask) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.reg = _reg, \
|
||||
.mask = _mask \
|
||||
}
|
||||
|
||||
#define CCU_PLL_DBGFS_FLD_ATTR(_name, _reg, _lsb, _mask, _min, _max) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.reg = _reg, \
|
||||
.lsb = _lsb, \
|
||||
.mask = _mask, \
|
||||
.min = _min, \
|
||||
.max = _max \
|
||||
}
|
||||
|
||||
static const struct ccu_pll_dbgfs_bit ccu_pll_bits[] = {
|
||||
CCU_PLL_DBGFS_BIT_ATTR("pll_en", CCU_PLL_CTL, CCU_PLL_CTL_EN),
|
||||
CCU_PLL_DBGFS_BIT_ATTR("pll_rst", CCU_PLL_CTL, CCU_PLL_CTL_RST),
|
||||
CCU_PLL_DBGFS_BIT_ATTR("pll_bypass", CCU_PLL_CTL, CCU_PLL_CTL_BYPASS),
|
||||
CCU_PLL_DBGFS_BIT_ATTR("pll_lock", CCU_PLL_CTL, CCU_PLL_CTL_LOCK)
|
||||
};
|
||||
|
||||
#define CCU_PLL_DBGFS_BIT_NUM ARRAY_SIZE(ccu_pll_bits)
|
||||
|
||||
static const struct ccu_pll_dbgfs_fld ccu_pll_flds[] = {
|
||||
CCU_PLL_DBGFS_FLD_ATTR("pll_nr", CCU_PLL_CTL, CCU_PLL_CTL_CLKR_FLD,
|
||||
CCU_PLL_CTL_CLKR_MASK, 1, CCU_PLL_NR_MAX),
|
||||
CCU_PLL_DBGFS_FLD_ATTR("pll_nf", CCU_PLL_CTL, CCU_PLL_CTL_CLKF_FLD,
|
||||
CCU_PLL_CTL_CLKF_MASK, 1, CCU_PLL_NF_MAX),
|
||||
CCU_PLL_DBGFS_FLD_ATTR("pll_od", CCU_PLL_CTL, CCU_PLL_CTL_CLKOD_FLD,
|
||||
CCU_PLL_CTL_CLKOD_MASK, 1, CCU_PLL_OD_MAX),
|
||||
CCU_PLL_DBGFS_FLD_ATTR("pll_nb", CCU_PLL_CTL1, CCU_PLL_CTL1_BWADJ_FLD,
|
||||
CCU_PLL_CTL1_BWADJ_MASK, 1, CCU_PLL_NB_MAX)
|
||||
};
|
||||
|
||||
#define CCU_PLL_DBGFS_FLD_NUM ARRAY_SIZE(ccu_pll_flds)
|
||||
|
||||
/*
|
||||
* It can be dangerous to change the PLL settings behind clock framework back,
|
||||
* therefore we don't provide any kernel config based compile time option for
|
||||
* this feature to enable.
|
||||
*/
|
||||
#undef CCU_PLL_ALLOW_WRITE_DEBUGFS
|
||||
#ifdef CCU_PLL_ALLOW_WRITE_DEBUGFS
|
||||
|
||||
static int ccu_pll_dbgfs_bit_set(void *priv, u64 val)
|
||||
{
|
||||
const struct ccu_pll_dbgfs_bit *bit = priv;
|
||||
struct ccu_pll *pll = bit->pll;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&pll->lock, flags);
|
||||
regmap_update_bits(pll->sys_regs, pll->reg_ctl + bit->reg,
|
||||
bit->mask, val ? bit->mask : 0);
|
||||
spin_unlock_irqrestore(&pll->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ccu_pll_dbgfs_fld_set(void *priv, u64 val)
|
||||
{
|
||||
struct ccu_pll_dbgfs_fld *fld = priv;
|
||||
struct ccu_pll *pll = fld->pll;
|
||||
unsigned long flags;
|
||||
u32 data;
|
||||
|
||||
val = clamp_t(u64, val, fld->min, fld->max);
|
||||
data = ((val - 1) << fld->lsb) & fld->mask;
|
||||
|
||||
spin_lock_irqsave(&pll->lock, flags);
|
||||
regmap_update_bits(pll->sys_regs, pll->reg_ctl + fld->reg, fld->mask,
|
||||
data);
|
||||
spin_unlock_irqrestore(&pll->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ccu_pll_dbgfs_mode 0644
|
||||
|
||||
#else /* !CCU_PLL_ALLOW_WRITE_DEBUGFS */
|
||||
|
||||
#define ccu_pll_dbgfs_bit_set NULL
|
||||
#define ccu_pll_dbgfs_fld_set NULL
|
||||
#define ccu_pll_dbgfs_mode 0444
|
||||
|
||||
#endif /* !CCU_PLL_ALLOW_WRITE_DEBUGFS */
|
||||
|
||||
static int ccu_pll_dbgfs_bit_get(void *priv, u64 *val)
|
||||
{
|
||||
struct ccu_pll_dbgfs_bit *bit = priv;
|
||||
struct ccu_pll *pll = bit->pll;
|
||||
u32 data = 0;
|
||||
|
||||
regmap_read(pll->sys_regs, pll->reg_ctl + bit->reg, &data);
|
||||
*val = !!(data & bit->mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(ccu_pll_dbgfs_bit_fops,
|
||||
ccu_pll_dbgfs_bit_get, ccu_pll_dbgfs_bit_set, "%llu\n");
|
||||
|
||||
static int ccu_pll_dbgfs_fld_get(void *priv, u64 *val)
|
||||
{
|
||||
struct ccu_pll_dbgfs_fld *fld = priv;
|
||||
struct ccu_pll *pll = fld->pll;
|
||||
u32 data = 0;
|
||||
|
||||
regmap_read(pll->sys_regs, pll->reg_ctl + fld->reg, &data);
|
||||
*val = ((data & fld->mask) >> fld->lsb) + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(ccu_pll_dbgfs_fld_fops,
|
||||
ccu_pll_dbgfs_fld_get, ccu_pll_dbgfs_fld_set, "%llu\n");
|
||||
|
||||
static void ccu_pll_debug_init(struct clk_hw *hw, struct dentry *dentry)
|
||||
{
|
||||
struct ccu_pll *pll = to_ccu_pll(hw);
|
||||
struct ccu_pll_dbgfs_bit *bits;
|
||||
struct ccu_pll_dbgfs_fld *flds;
|
||||
int idx;
|
||||
|
||||
bits = kcalloc(CCU_PLL_DBGFS_BIT_NUM, sizeof(*bits), GFP_KERNEL);
|
||||
if (!bits)
|
||||
return;
|
||||
|
||||
for (idx = 0; idx < CCU_PLL_DBGFS_BIT_NUM; ++idx) {
|
||||
bits[idx] = ccu_pll_bits[idx];
|
||||
bits[idx].pll = pll;
|
||||
|
||||
debugfs_create_file_unsafe(bits[idx].name, ccu_pll_dbgfs_mode,
|
||||
dentry, &bits[idx],
|
||||
&ccu_pll_dbgfs_bit_fops);
|
||||
}
|
||||
|
||||
flds = kcalloc(CCU_PLL_DBGFS_FLD_NUM, sizeof(*flds), GFP_KERNEL);
|
||||
if (!flds)
|
||||
return;
|
||||
|
||||
for (idx = 0; idx < CCU_PLL_DBGFS_FLD_NUM; ++idx) {
|
||||
flds[idx] = ccu_pll_flds[idx];
|
||||
flds[idx].pll = pll;
|
||||
|
||||
debugfs_create_file_unsafe(flds[idx].name, ccu_pll_dbgfs_mode,
|
||||
dentry, &flds[idx],
|
||||
&ccu_pll_dbgfs_fld_fops);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* !CONFIG_DEBUG_FS */
|
||||
|
||||
#define ccu_pll_debug_init NULL
|
||||
|
||||
#endif /* !CONFIG_DEBUG_FS */
|
||||
|
||||
static const struct clk_ops ccu_pll_gate_to_set_ops = {
|
||||
.enable = ccu_pll_enable,
|
||||
.disable = ccu_pll_disable,
|
||||
.is_enabled = ccu_pll_is_enabled,
|
||||
.recalc_rate = ccu_pll_recalc_rate,
|
||||
.round_rate = ccu_pll_round_rate,
|
||||
.set_rate = ccu_pll_set_rate_norst,
|
||||
.debug_init = ccu_pll_debug_init
|
||||
};
|
||||
|
||||
static const struct clk_ops ccu_pll_straight_set_ops = {
|
||||
.enable = ccu_pll_enable,
|
||||
.disable = ccu_pll_disable,
|
||||
.is_enabled = ccu_pll_is_enabled,
|
||||
.recalc_rate = ccu_pll_recalc_rate,
|
||||
.round_rate = ccu_pll_round_rate,
|
||||
.set_rate = ccu_pll_set_rate_reset,
|
||||
.debug_init = ccu_pll_debug_init
|
||||
};
|
||||
|
||||
struct ccu_pll *ccu_pll_hw_register(const struct ccu_pll_init_data *pll_init)
|
||||
{
|
||||
struct clk_parent_data parent_data = { };
|
||||
struct clk_init_data hw_init = { };
|
||||
struct ccu_pll *pll;
|
||||
int ret;
|
||||
|
||||
if (!pll_init)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* Note since Baikal-T1 System Controller registers are MMIO-backed
|
||||
* we won't check the regmap IO operations return status, because it
|
||||
* must be zero anyway.
|
||||
*/
|
||||
pll->hw.init = &hw_init;
|
||||
pll->reg_ctl = pll_init->base + CCU_PLL_CTL;
|
||||
pll->reg_ctl1 = pll_init->base + CCU_PLL_CTL1;
|
||||
pll->sys_regs = pll_init->sys_regs;
|
||||
pll->id = pll_init->id;
|
||||
spin_lock_init(&pll->lock);
|
||||
|
||||
hw_init.name = pll_init->name;
|
||||
hw_init.flags = pll_init->flags;
|
||||
|
||||
if (hw_init.flags & CLK_SET_RATE_GATE)
|
||||
hw_init.ops = &ccu_pll_gate_to_set_ops;
|
||||
else
|
||||
hw_init.ops = &ccu_pll_straight_set_ops;
|
||||
|
||||
if (!pll_init->parent_name) {
|
||||
ret = -EINVAL;
|
||||
goto err_free_pll;
|
||||
}
|
||||
parent_data.fw_name = pll_init->parent_name;
|
||||
hw_init.parent_data = &parent_data;
|
||||
hw_init.num_parents = 1;
|
||||
|
||||
ret = of_clk_hw_register(pll_init->np, &pll->hw);
|
||||
if (ret)
|
||||
goto err_free_pll;
|
||||
|
||||
return pll;
|
||||
|
||||
err_free_pll:
|
||||
kfree(pll);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void ccu_pll_hw_unregister(struct ccu_pll *pll)
|
||||
{
|
||||
clk_hw_unregister(&pll->hw);
|
||||
|
||||
kfree(pll);
|
||||
}
|
64
drivers/clk/baikal-t1/ccu-pll.h
Normal file
64
drivers/clk/baikal-t1/ccu-pll.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
|
||||
*
|
||||
* Baikal-T1 CCU PLL interface driver
|
||||
*/
|
||||
#ifndef __CLK_BT1_CCU_PLL_H__
|
||||
#define __CLK_BT1_CCU_PLL_H__
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/*
|
||||
* struct ccu_pll_init_data - CCU PLL initialization data
|
||||
* @id: Clock private identifier.
|
||||
* @name: Clocks name.
|
||||
* @parent_name: Clocks parent name in a fw node.
|
||||
* @base: PLL registers base address with respect to the sys_regs base.
|
||||
* @sys_regs: Baikal-T1 System Controller registers map.
|
||||
* @np: Pointer to the node describing the CCU PLLs.
|
||||
* @flags: PLL clock flags.
|
||||
*/
|
||||
struct ccu_pll_init_data {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned int base;
|
||||
struct regmap *sys_regs;
|
||||
struct device_node *np;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct ccu_pll - CCU PLL descriptor
|
||||
* @hw: clk_hw of the PLL.
|
||||
* @id: Clock private identifier.
|
||||
* @reg_ctl: PLL control register base.
|
||||
* @reg_ctl1: PLL control1 register base.
|
||||
* @sys_regs: Baikal-T1 System Controller registers map.
|
||||
* @lock: PLL state change spin-lock.
|
||||
*/
|
||||
struct ccu_pll {
|
||||
struct clk_hw hw;
|
||||
unsigned int id;
|
||||
unsigned int reg_ctl;
|
||||
unsigned int reg_ctl1;
|
||||
struct regmap *sys_regs;
|
||||
spinlock_t lock;
|
||||
};
|
||||
#define to_ccu_pll(_hw) container_of(_hw, struct ccu_pll, hw)
|
||||
|
||||
static inline struct clk_hw *ccu_pll_get_clk_hw(struct ccu_pll *pll)
|
||||
{
|
||||
return pll ? &pll->hw : NULL;
|
||||
}
|
||||
|
||||
struct ccu_pll *ccu_pll_hw_register(const struct ccu_pll_init_data *init);
|
||||
|
||||
void ccu_pll_hw_unregister(struct ccu_pll *pll);
|
||||
|
||||
#endif /* __CLK_BT1_CCU_PLL_H__ */
|
485
drivers/clk/baikal-t1/clk-ccu-div.c
Normal file
485
drivers/clk/baikal-t1/clk-ccu-div.c
Normal file
@ -0,0 +1,485 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
|
||||
*
|
||||
* Authors:
|
||||
* Serge Semin <Sergey.Semin@baikalelectronics.ru>
|
||||
* Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
|
||||
*
|
||||
* Baikal-T1 CCU Dividers clock driver
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "bt1-ccu-div: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/clock/bt1-ccu.h>
|
||||
#include <dt-bindings/reset/bt1-ccu.h>
|
||||
|
||||
#include "ccu-div.h"
|
||||
|
||||
#define CCU_AXI_MAIN_BASE 0x030
|
||||
#define CCU_AXI_DDR_BASE 0x034
|
||||
#define CCU_AXI_SATA_BASE 0x038
|
||||
#define CCU_AXI_GMAC0_BASE 0x03C
|
||||
#define CCU_AXI_GMAC1_BASE 0x040
|
||||
#define CCU_AXI_XGMAC_BASE 0x044
|
||||
#define CCU_AXI_PCIE_M_BASE 0x048
|
||||
#define CCU_AXI_PCIE_S_BASE 0x04C
|
||||
#define CCU_AXI_USB_BASE 0x050
|
||||
#define CCU_AXI_HWA_BASE 0x054
|
||||
#define CCU_AXI_SRAM_BASE 0x058
|
||||
|
||||
#define CCU_SYS_SATA_REF_BASE 0x060
|
||||
#define CCU_SYS_APB_BASE 0x064
|
||||
#define CCU_SYS_GMAC0_BASE 0x068
|
||||
#define CCU_SYS_GMAC1_BASE 0x06C
|
||||
#define CCU_SYS_XGMAC_BASE 0x070
|
||||
#define CCU_SYS_USB_BASE 0x074
|
||||
#define CCU_SYS_PVT_BASE 0x078
|
||||
#define CCU_SYS_HWA_BASE 0x07C
|
||||
#define CCU_SYS_UART_BASE 0x084
|
||||
#define CCU_SYS_TIMER0_BASE 0x088
|
||||
#define CCU_SYS_TIMER1_BASE 0x08C
|
||||
#define CCU_SYS_TIMER2_BASE 0x090
|
||||
#define CCU_SYS_WDT_BASE 0x150
|
||||
|
||||
#define CCU_DIV_VAR_INFO(_id, _name, _pname, _base, _width, _flags, _features) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.base = _base, \
|
||||
.type = CCU_DIV_VAR, \
|
||||
.width = _width, \
|
||||
.flags = _flags, \
|
||||
.features = _features \
|
||||
}
|
||||
|
||||
#define CCU_DIV_GATE_INFO(_id, _name, _pname, _base, _divider) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.base = _base, \
|
||||
.type = CCU_DIV_GATE, \
|
||||
.divider = _divider \
|
||||
}
|
||||
|
||||
#define CCU_DIV_FIXED_INFO(_id, _name, _pname, _divider) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.type = CCU_DIV_FIXED, \
|
||||
.divider = _divider \
|
||||
}
|
||||
|
||||
#define CCU_DIV_RST_MAP(_rst_id, _clk_id) \
|
||||
{ \
|
||||
.rst_id = _rst_id, \
|
||||
.clk_id = _clk_id \
|
||||
}
|
||||
|
||||
struct ccu_div_info {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned int base;
|
||||
enum ccu_div_type type;
|
||||
union {
|
||||
unsigned int width;
|
||||
unsigned int divider;
|
||||
};
|
||||
unsigned long flags;
|
||||
unsigned long features;
|
||||
};
|
||||
|
||||
struct ccu_div_rst_map {
|
||||
unsigned int rst_id;
|
||||
unsigned int clk_id;
|
||||
};
|
||||
|
||||
struct ccu_div_data {
|
||||
struct device_node *np;
|
||||
struct regmap *sys_regs;
|
||||
|
||||
unsigned int divs_num;
|
||||
const struct ccu_div_info *divs_info;
|
||||
struct ccu_div **divs;
|
||||
|
||||
unsigned int rst_num;
|
||||
const struct ccu_div_rst_map *rst_map;
|
||||
struct reset_controller_dev rcdev;
|
||||
};
|
||||
#define to_ccu_div_data(_rcdev) container_of(_rcdev, struct ccu_div_data, rcdev)
|
||||
|
||||
/*
|
||||
* AXI Main Interconnect (axi_main_clk) and DDR AXI-bus (axi_ddr_clk) clocks
|
||||
* must be left enabled in any case, since former one is responsible for
|
||||
* clocking a bus between CPU cores and the rest of the SoC components, while
|
||||
* the later is clocking the AXI-bus between DDR controller and the Main
|
||||
* Interconnect. So should any of these clocks get to be disabled, the system
|
||||
* will literally stop working. That's why we marked them as critical.
|
||||
*/
|
||||
static const struct ccu_div_info axi_info[] = {
|
||||
CCU_DIV_VAR_INFO(CCU_AXI_MAIN_CLK, "axi_main_clk", "pcie_clk",
|
||||
CCU_AXI_MAIN_BASE, 4,
|
||||
CLK_IS_CRITICAL, CCU_DIV_RESET_DOMAIN),
|
||||
CCU_DIV_VAR_INFO(CCU_AXI_DDR_CLK, "axi_ddr_clk", "sata_clk",
|
||||
CCU_AXI_DDR_BASE, 4,
|
||||
CLK_IS_CRITICAL | CLK_SET_RATE_GATE,
|
||||
CCU_DIV_RESET_DOMAIN),
|
||||
CCU_DIV_VAR_INFO(CCU_AXI_SATA_CLK, "axi_sata_clk", "sata_clk",
|
||||
CCU_AXI_SATA_BASE, 4,
|
||||
CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
|
||||
CCU_DIV_VAR_INFO(CCU_AXI_GMAC0_CLK, "axi_gmac0_clk", "eth_clk",
|
||||
CCU_AXI_GMAC0_BASE, 4,
|
||||
CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
|
||||
CCU_DIV_VAR_INFO(CCU_AXI_GMAC1_CLK, "axi_gmac1_clk", "eth_clk",
|
||||
CCU_AXI_GMAC1_BASE, 4,
|
||||
CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
|
||||
CCU_DIV_VAR_INFO(CCU_AXI_XGMAC_CLK, "axi_xgmac_clk", "eth_clk",
|
||||
CCU_AXI_XGMAC_BASE, 4,
|
||||
CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
|
||||
CCU_DIV_VAR_INFO(CCU_AXI_PCIE_M_CLK, "axi_pcie_m_clk", "pcie_clk",
|
||||
CCU_AXI_PCIE_M_BASE, 4,
|
||||
CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
|
||||
CCU_DIV_VAR_INFO(CCU_AXI_PCIE_S_CLK, "axi_pcie_s_clk", "pcie_clk",
|
||||
CCU_AXI_PCIE_S_BASE, 4,
|
||||
CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
|
||||
CCU_DIV_VAR_INFO(CCU_AXI_USB_CLK, "axi_usb_clk", "sata_clk",
|
||||
CCU_AXI_USB_BASE, 4,
|
||||
CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
|
||||
CCU_DIV_VAR_INFO(CCU_AXI_HWA_CLK, "axi_hwa_clk", "sata_clk",
|
||||
CCU_AXI_HWA_BASE, 4,
|
||||
CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN),
|
||||
CCU_DIV_VAR_INFO(CCU_AXI_SRAM_CLK, "axi_sram_clk", "eth_clk",
|
||||
CCU_AXI_SRAM_BASE, 4,
|
||||
CLK_SET_RATE_GATE, CCU_DIV_RESET_DOMAIN)
|
||||
};
|
||||
|
||||
static const struct ccu_div_rst_map axi_rst_map[] = {
|
||||
CCU_DIV_RST_MAP(CCU_AXI_MAIN_RST, CCU_AXI_MAIN_CLK),
|
||||
CCU_DIV_RST_MAP(CCU_AXI_DDR_RST, CCU_AXI_DDR_CLK),
|
||||
CCU_DIV_RST_MAP(CCU_AXI_SATA_RST, CCU_AXI_SATA_CLK),
|
||||
CCU_DIV_RST_MAP(CCU_AXI_GMAC0_RST, CCU_AXI_GMAC0_CLK),
|
||||
CCU_DIV_RST_MAP(CCU_AXI_GMAC1_RST, CCU_AXI_GMAC1_CLK),
|
||||
CCU_DIV_RST_MAP(CCU_AXI_XGMAC_RST, CCU_AXI_XGMAC_CLK),
|
||||
CCU_DIV_RST_MAP(CCU_AXI_PCIE_M_RST, CCU_AXI_PCIE_M_CLK),
|
||||
CCU_DIV_RST_MAP(CCU_AXI_PCIE_S_RST, CCU_AXI_PCIE_S_CLK),
|
||||
CCU_DIV_RST_MAP(CCU_AXI_USB_RST, CCU_AXI_USB_CLK),
|
||||
CCU_DIV_RST_MAP(CCU_AXI_HWA_RST, CCU_AXI_HWA_CLK),
|
||||
CCU_DIV_RST_MAP(CCU_AXI_SRAM_RST, CCU_AXI_SRAM_CLK)
|
||||
};
|
||||
|
||||
/*
|
||||
* APB-bus clock is marked as critical since it's a main communication bus
|
||||
* for the SoC devices registers IO-operations.
|
||||
*/
|
||||
static const struct ccu_div_info sys_info[] = {
|
||||
CCU_DIV_VAR_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk",
|
||||
"sata_clk", CCU_SYS_SATA_REF_BASE, 4,
|
||||
CLK_SET_RATE_GATE,
|
||||
CCU_DIV_SKIP_ONE | CCU_DIV_LOCK_SHIFTED |
|
||||
CCU_DIV_RESET_DOMAIN),
|
||||
CCU_DIV_VAR_INFO(CCU_SYS_APB_CLK, "sys_apb_clk",
|
||||
"pcie_clk", CCU_SYS_APB_BASE, 5,
|
||||
CLK_IS_CRITICAL, CCU_DIV_RESET_DOMAIN),
|
||||
CCU_DIV_GATE_INFO(CCU_SYS_GMAC0_TX_CLK, "sys_gmac0_tx_clk",
|
||||
"eth_clk", CCU_SYS_GMAC0_BASE, 5),
|
||||
CCU_DIV_FIXED_INFO(CCU_SYS_GMAC0_PTP_CLK, "sys_gmac0_ptp_clk",
|
||||
"eth_clk", 10),
|
||||
CCU_DIV_GATE_INFO(CCU_SYS_GMAC1_TX_CLK, "sys_gmac1_tx_clk",
|
||||
"eth_clk", CCU_SYS_GMAC1_BASE, 5),
|
||||
CCU_DIV_FIXED_INFO(CCU_SYS_GMAC1_PTP_CLK, "sys_gmac1_ptp_clk",
|
||||
"eth_clk", 10),
|
||||
CCU_DIV_GATE_INFO(CCU_SYS_XGMAC_REF_CLK, "sys_xgmac_ref_clk",
|
||||
"eth_clk", CCU_SYS_XGMAC_BASE, 8),
|
||||
CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_PTP_CLK, "sys_xgmac_ptp_clk",
|
||||
"eth_clk", 10),
|
||||
CCU_DIV_GATE_INFO(CCU_SYS_USB_CLK, "sys_usb_clk",
|
||||
"eth_clk", CCU_SYS_USB_BASE, 10),
|
||||
CCU_DIV_VAR_INFO(CCU_SYS_PVT_CLK, "sys_pvt_clk",
|
||||
"ref_clk", CCU_SYS_PVT_BASE, 5,
|
||||
CLK_SET_RATE_GATE, 0),
|
||||
CCU_DIV_VAR_INFO(CCU_SYS_HWA_CLK, "sys_hwa_clk",
|
||||
"sata_clk", CCU_SYS_HWA_BASE, 4,
|
||||
CLK_SET_RATE_GATE, 0),
|
||||
CCU_DIV_VAR_INFO(CCU_SYS_UART_CLK, "sys_uart_clk",
|
||||
"eth_clk", CCU_SYS_UART_BASE, 17,
|
||||
CLK_SET_RATE_GATE, 0),
|
||||
CCU_DIV_FIXED_INFO(CCU_SYS_I2C1_CLK, "sys_i2c1_clk",
|
||||
"eth_clk", 10),
|
||||
CCU_DIV_FIXED_INFO(CCU_SYS_I2C2_CLK, "sys_i2c2_clk",
|
||||
"eth_clk", 10),
|
||||
CCU_DIV_FIXED_INFO(CCU_SYS_GPIO_CLK, "sys_gpio_clk",
|
||||
"ref_clk", 25),
|
||||
CCU_DIV_VAR_INFO(CCU_SYS_TIMER0_CLK, "sys_timer0_clk",
|
||||
"ref_clk", CCU_SYS_TIMER0_BASE, 17,
|
||||
CLK_SET_RATE_GATE, 0),
|
||||
CCU_DIV_VAR_INFO(CCU_SYS_TIMER1_CLK, "sys_timer1_clk",
|
||||
"ref_clk", CCU_SYS_TIMER1_BASE, 17,
|
||||
CLK_SET_RATE_GATE, 0),
|
||||
CCU_DIV_VAR_INFO(CCU_SYS_TIMER2_CLK, "sys_timer2_clk",
|
||||
"ref_clk", CCU_SYS_TIMER2_BASE, 17,
|
||||
CLK_SET_RATE_GATE, 0),
|
||||
CCU_DIV_VAR_INFO(CCU_SYS_WDT_CLK, "sys_wdt_clk",
|
||||
"eth_clk", CCU_SYS_WDT_BASE, 17,
|
||||
CLK_SET_RATE_GATE, CCU_DIV_SKIP_ONE_TO_THREE)
|
||||
};
|
||||
|
||||
static const struct ccu_div_rst_map sys_rst_map[] = {
|
||||
CCU_DIV_RST_MAP(CCU_SYS_SATA_REF_RST, CCU_SYS_SATA_REF_CLK),
|
||||
CCU_DIV_RST_MAP(CCU_SYS_APB_RST, CCU_SYS_APB_CLK),
|
||||
};
|
||||
|
||||
static struct ccu_div *ccu_div_find_desc(struct ccu_div_data *data,
|
||||
unsigned int clk_id)
|
||||
{
|
||||
struct ccu_div *div;
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < data->divs_num; ++idx) {
|
||||
div = data->divs[idx];
|
||||
if (div && div->id == clk_id)
|
||||
return div;
|
||||
}
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static int ccu_div_reset(struct reset_controller_dev *rcdev,
|
||||
unsigned long rst_id)
|
||||
{
|
||||
struct ccu_div_data *data = to_ccu_div_data(rcdev);
|
||||
const struct ccu_div_rst_map *map;
|
||||
struct ccu_div *div;
|
||||
int idx, ret;
|
||||
|
||||
for (idx = 0, map = data->rst_map; idx < data->rst_num; ++idx, ++map) {
|
||||
if (map->rst_id == rst_id)
|
||||
break;
|
||||
}
|
||||
if (idx == data->rst_num) {
|
||||
pr_err("Invalid reset ID %lu specified\n", rst_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
div = ccu_div_find_desc(data, map->clk_id);
|
||||
if (IS_ERR(div)) {
|
||||
pr_err("Invalid clock ID %d in mapping\n", map->clk_id);
|
||||
return PTR_ERR(div);
|
||||
}
|
||||
|
||||
ret = ccu_div_reset_domain(div);
|
||||
if (ret) {
|
||||
pr_err("Reset isn't supported by divider %s\n",
|
||||
clk_hw_get_name(ccu_div_get_clk_hw(div)));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct reset_control_ops ccu_div_rst_ops = {
|
||||
.reset = ccu_div_reset,
|
||||
};
|
||||
|
||||
static struct ccu_div_data *ccu_div_create_data(struct device_node *np)
|
||||
{
|
||||
struct ccu_div_data *data;
|
||||
int ret;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
data->np = np;
|
||||
if (of_device_is_compatible(np, "baikal,bt1-ccu-axi")) {
|
||||
data->divs_num = ARRAY_SIZE(axi_info);
|
||||
data->divs_info = axi_info;
|
||||
data->rst_num = ARRAY_SIZE(axi_rst_map);
|
||||
data->rst_map = axi_rst_map;
|
||||
} else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) {
|
||||
data->divs_num = ARRAY_SIZE(sys_info);
|
||||
data->divs_info = sys_info;
|
||||
data->rst_num = ARRAY_SIZE(sys_rst_map);
|
||||
data->rst_map = sys_rst_map;
|
||||
} else {
|
||||
pr_err("Incompatible DT node '%s' specified\n",
|
||||
of_node_full_name(np));
|
||||
ret = -EINVAL;
|
||||
goto err_kfree_data;
|
||||
}
|
||||
|
||||
data->divs = kcalloc(data->divs_num, sizeof(*data->divs), GFP_KERNEL);
|
||||
if (!data->divs) {
|
||||
ret = -ENOMEM;
|
||||
goto err_kfree_data;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
err_kfree_data:
|
||||
kfree(data);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void ccu_div_free_data(struct ccu_div_data *data)
|
||||
{
|
||||
kfree(data->divs);
|
||||
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
static int ccu_div_find_sys_regs(struct ccu_div_data *data)
|
||||
{
|
||||
data->sys_regs = syscon_node_to_regmap(data->np->parent);
|
||||
if (IS_ERR(data->sys_regs)) {
|
||||
pr_err("Failed to find syscon regs for '%s'\n",
|
||||
of_node_full_name(data->np));
|
||||
return PTR_ERR(data->sys_regs);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct clk_hw *ccu_div_of_clk_hw_get(struct of_phandle_args *clkspec,
|
||||
void *priv)
|
||||
{
|
||||
struct ccu_div_data *data = priv;
|
||||
struct ccu_div *div;
|
||||
unsigned int clk_id;
|
||||
|
||||
clk_id = clkspec->args[0];
|
||||
div = ccu_div_find_desc(data, clk_id);
|
||||
if (IS_ERR(div)) {
|
||||
pr_info("Invalid clock ID %d specified\n", clk_id);
|
||||
return ERR_CAST(div);
|
||||
}
|
||||
|
||||
return ccu_div_get_clk_hw(div);
|
||||
}
|
||||
|
||||
static int ccu_div_clk_register(struct ccu_div_data *data)
|
||||
{
|
||||
int idx, ret;
|
||||
|
||||
for (idx = 0; idx < data->divs_num; ++idx) {
|
||||
const struct ccu_div_info *info = &data->divs_info[idx];
|
||||
struct ccu_div_init_data init = {0};
|
||||
|
||||
init.id = info->id;
|
||||
init.name = info->name;
|
||||
init.parent_name = info->parent_name;
|
||||
init.np = data->np;
|
||||
init.type = info->type;
|
||||
init.flags = info->flags;
|
||||
init.features = info->features;
|
||||
|
||||
if (init.type == CCU_DIV_VAR) {
|
||||
init.base = info->base;
|
||||
init.sys_regs = data->sys_regs;
|
||||
init.width = info->width;
|
||||
} else if (init.type == CCU_DIV_GATE) {
|
||||
init.base = info->base;
|
||||
init.sys_regs = data->sys_regs;
|
||||
init.divider = info->divider;
|
||||
} else {
|
||||
init.divider = info->divider;
|
||||
}
|
||||
|
||||
data->divs[idx] = ccu_div_hw_register(&init);
|
||||
if (IS_ERR(data->divs[idx])) {
|
||||
ret = PTR_ERR(data->divs[idx]);
|
||||
pr_err("Couldn't register divider '%s' hw\n",
|
||||
init.name);
|
||||
goto err_hw_unregister;
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_clk_add_hw_provider(data->np, ccu_div_of_clk_hw_get, data);
|
||||
if (ret) {
|
||||
pr_err("Couldn't register dividers '%s' clock provider\n",
|
||||
of_node_full_name(data->np));
|
||||
goto err_hw_unregister;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_hw_unregister:
|
||||
for (--idx; idx >= 0; --idx)
|
||||
ccu_div_hw_unregister(data->divs[idx]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ccu_div_clk_unregister(struct ccu_div_data *data)
|
||||
{
|
||||
int idx;
|
||||
|
||||
of_clk_del_provider(data->np);
|
||||
|
||||
for (idx = 0; idx < data->divs_num; ++idx)
|
||||
ccu_div_hw_unregister(data->divs[idx]);
|
||||
}
|
||||
|
||||
static int ccu_div_rst_register(struct ccu_div_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
data->rcdev.ops = &ccu_div_rst_ops;
|
||||
data->rcdev.of_node = data->np;
|
||||
data->rcdev.nr_resets = data->rst_num;
|
||||
|
||||
ret = reset_controller_register(&data->rcdev);
|
||||
if (ret)
|
||||
pr_err("Couldn't register divider '%s' reset controller\n",
|
||||
of_node_full_name(data->np));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ccu_div_init(struct device_node *np)
|
||||
{
|
||||
struct ccu_div_data *data;
|
||||
int ret;
|
||||
|
||||
data = ccu_div_create_data(np);
|
||||
if (IS_ERR(data))
|
||||
return;
|
||||
|
||||
ret = ccu_div_find_sys_regs(data);
|
||||
if (ret)
|
||||
goto err_free_data;
|
||||
|
||||
ret = ccu_div_clk_register(data);
|
||||
if (ret)
|
||||
goto err_free_data;
|
||||
|
||||
ret = ccu_div_rst_register(data);
|
||||
if (ret)
|
||||
goto err_clk_unregister;
|
||||
|
||||
return;
|
||||
|
||||
err_clk_unregister:
|
||||
ccu_div_clk_unregister(data);
|
||||
|
||||
err_free_data:
|
||||
ccu_div_free_data(data);
|
||||
}
|
||||
|
||||
CLK_OF_DECLARE(ccu_axi, "baikal,bt1-ccu-axi", ccu_div_init);
|
||||
CLK_OF_DECLARE(ccu_sys, "baikal,bt1-ccu-sys", ccu_div_init);
|
204
drivers/clk/baikal-t1/clk-ccu-pll.c
Normal file
204
drivers/clk/baikal-t1/clk-ccu-pll.c
Normal file
@ -0,0 +1,204 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
|
||||
*
|
||||
* Authors:
|
||||
* Serge Semin <Sergey.Semin@baikalelectronics.ru>
|
||||
* Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
|
||||
*
|
||||
* Baikal-T1 CCU PLL clocks driver
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "bt1-ccu-pll: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/clock/bt1-ccu.h>
|
||||
|
||||
#include "ccu-pll.h"
|
||||
|
||||
#define CCU_CPU_PLL_BASE 0x000
|
||||
#define CCU_SATA_PLL_BASE 0x008
|
||||
#define CCU_DDR_PLL_BASE 0x010
|
||||
#define CCU_PCIE_PLL_BASE 0x018
|
||||
#define CCU_ETH_PLL_BASE 0x020
|
||||
|
||||
#define CCU_PLL_INFO(_id, _name, _pname, _base, _flags) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.base = _base, \
|
||||
.flags = _flags \
|
||||
}
|
||||
|
||||
#define CCU_PLL_NUM ARRAY_SIZE(pll_info)
|
||||
|
||||
struct ccu_pll_info {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
unsigned int base;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* Mark as critical all PLLs except Ethernet one. CPU and DDR PLLs are sources
|
||||
* of CPU cores and DDR controller reference clocks, due to which they
|
||||
* obviously shouldn't be ever gated. SATA and PCIe PLLs are the parents of
|
||||
* APB-bus and DDR controller AXI-bus clocks. If they are gated the system will
|
||||
* be unusable.
|
||||
*/
|
||||
static const struct ccu_pll_info pll_info[] = {
|
||||
CCU_PLL_INFO(CCU_CPU_PLL, "cpu_pll", "ref_clk", CCU_CPU_PLL_BASE,
|
||||
CLK_IS_CRITICAL),
|
||||
CCU_PLL_INFO(CCU_SATA_PLL, "sata_pll", "ref_clk", CCU_SATA_PLL_BASE,
|
||||
CLK_IS_CRITICAL | CLK_SET_RATE_GATE),
|
||||
CCU_PLL_INFO(CCU_DDR_PLL, "ddr_pll", "ref_clk", CCU_DDR_PLL_BASE,
|
||||
CLK_IS_CRITICAL | CLK_SET_RATE_GATE),
|
||||
CCU_PLL_INFO(CCU_PCIE_PLL, "pcie_pll", "ref_clk", CCU_PCIE_PLL_BASE,
|
||||
CLK_IS_CRITICAL),
|
||||
CCU_PLL_INFO(CCU_ETH_PLL, "eth_pll", "ref_clk", CCU_ETH_PLL_BASE,
|
||||
CLK_SET_RATE_GATE)
|
||||
};
|
||||
|
||||
struct ccu_pll_data {
|
||||
struct device_node *np;
|
||||
struct regmap *sys_regs;
|
||||
struct ccu_pll *plls[CCU_PLL_NUM];
|
||||
};
|
||||
|
||||
static struct ccu_pll *ccu_pll_find_desc(struct ccu_pll_data *data,
|
||||
unsigned int clk_id)
|
||||
{
|
||||
struct ccu_pll *pll;
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < CCU_PLL_NUM; ++idx) {
|
||||
pll = data->plls[idx];
|
||||
if (pll && pll->id == clk_id)
|
||||
return pll;
|
||||
}
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static struct ccu_pll_data *ccu_pll_create_data(struct device_node *np)
|
||||
{
|
||||
struct ccu_pll_data *data;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
data->np = np;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void ccu_pll_free_data(struct ccu_pll_data *data)
|
||||
{
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
static int ccu_pll_find_sys_regs(struct ccu_pll_data *data)
|
||||
{
|
||||
data->sys_regs = syscon_node_to_regmap(data->np->parent);
|
||||
if (IS_ERR(data->sys_regs)) {
|
||||
pr_err("Failed to find syscon regs for '%s'\n",
|
||||
of_node_full_name(data->np));
|
||||
return PTR_ERR(data->sys_regs);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct clk_hw *ccu_pll_of_clk_hw_get(struct of_phandle_args *clkspec,
|
||||
void *priv)
|
||||
{
|
||||
struct ccu_pll_data *data = priv;
|
||||
struct ccu_pll *pll;
|
||||
unsigned int clk_id;
|
||||
|
||||
clk_id = clkspec->args[0];
|
||||
pll = ccu_pll_find_desc(data, clk_id);
|
||||
if (IS_ERR(pll)) {
|
||||
pr_info("Invalid PLL clock ID %d specified\n", clk_id);
|
||||
return ERR_CAST(pll);
|
||||
}
|
||||
|
||||
return ccu_pll_get_clk_hw(pll);
|
||||
}
|
||||
|
||||
static int ccu_pll_clk_register(struct ccu_pll_data *data)
|
||||
{
|
||||
int idx, ret;
|
||||
|
||||
for (idx = 0; idx < CCU_PLL_NUM; ++idx) {
|
||||
const struct ccu_pll_info *info = &pll_info[idx];
|
||||
struct ccu_pll_init_data init = {0};
|
||||
|
||||
init.id = info->id;
|
||||
init.name = info->name;
|
||||
init.parent_name = info->parent_name;
|
||||
init.base = info->base;
|
||||
init.sys_regs = data->sys_regs;
|
||||
init.np = data->np;
|
||||
init.flags = info->flags;
|
||||
|
||||
data->plls[idx] = ccu_pll_hw_register(&init);
|
||||
if (IS_ERR(data->plls[idx])) {
|
||||
ret = PTR_ERR(data->plls[idx]);
|
||||
pr_err("Couldn't register PLL hw '%s'\n",
|
||||
init.name);
|
||||
goto err_hw_unregister;
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_clk_add_hw_provider(data->np, ccu_pll_of_clk_hw_get, data);
|
||||
if (ret) {
|
||||
pr_err("Couldn't register PLL provider of '%s'\n",
|
||||
of_node_full_name(data->np));
|
||||
goto err_hw_unregister;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_hw_unregister:
|
||||
for (--idx; idx >= 0; --idx)
|
||||
ccu_pll_hw_unregister(data->plls[idx]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __init void ccu_pll_init(struct device_node *np)
|
||||
{
|
||||
struct ccu_pll_data *data;
|
||||
int ret;
|
||||
|
||||
data = ccu_pll_create_data(np);
|
||||
if (IS_ERR(data))
|
||||
return;
|
||||
|
||||
ret = ccu_pll_find_sys_regs(data);
|
||||
if (ret)
|
||||
goto err_free_data;
|
||||
|
||||
ret = ccu_pll_clk_register(data);
|
||||
if (ret)
|
||||
goto err_free_data;
|
||||
|
||||
return;
|
||||
|
||||
err_free_data:
|
||||
ccu_pll_free_data(data);
|
||||
}
|
||||
CLK_OF_DECLARE(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init);
|
@ -396,8 +396,8 @@ out:
|
||||
}
|
||||
|
||||
static void bcm2835_debugfs_regset(struct bcm2835_cprman *cprman, u32 base,
|
||||
struct debugfs_reg32 *regs, size_t nregs,
|
||||
struct dentry *dentry)
|
||||
const struct debugfs_reg32 *regs,
|
||||
size_t nregs, struct dentry *dentry)
|
||||
{
|
||||
struct debugfs_regset32 *regset;
|
||||
|
||||
@ -1240,7 +1240,7 @@ static u8 bcm2835_clock_get_parent(struct clk_hw *hw)
|
||||
return (src & CM_SRC_MASK) >> CM_SRC_SHIFT;
|
||||
}
|
||||
|
||||
static struct debugfs_reg32 bcm2835_debugfs_clock_reg32[] = {
|
||||
static const struct debugfs_reg32 bcm2835_debugfs_clock_reg32[] = {
|
||||
{
|
||||
.name = "ctl",
|
||||
.offset = 0,
|
||||
@ -1296,8 +1296,9 @@ static const struct clk_ops bcm2835_vpu_clock_clk_ops = {
|
||||
};
|
||||
|
||||
static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
|
||||
const struct bcm2835_pll_data *data)
|
||||
const void *data)
|
||||
{
|
||||
const struct bcm2835_pll_data *pll_data = data;
|
||||
struct bcm2835_pll *pll;
|
||||
struct clk_init_data init;
|
||||
int ret;
|
||||
@ -1307,7 +1308,7 @@ static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
|
||||
/* All of the PLLs derive from the external oscillator. */
|
||||
init.parent_names = &cprman->real_parent_names[0];
|
||||
init.num_parents = 1;
|
||||
init.name = data->name;
|
||||
init.name = pll_data->name;
|
||||
init.ops = &bcm2835_pll_clk_ops;
|
||||
init.flags = CLK_IGNORE_UNUSED;
|
||||
|
||||
@ -1316,7 +1317,7 @@ static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
|
||||
return NULL;
|
||||
|
||||
pll->cprman = cprman;
|
||||
pll->data = data;
|
||||
pll->data = pll_data;
|
||||
pll->hw.init = &init;
|
||||
|
||||
ret = devm_clk_hw_register(cprman->dev, &pll->hw);
|
||||
@ -1327,35 +1328,36 @@ static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
|
||||
|
||||
static struct clk_hw *
|
||||
bcm2835_register_pll_divider(struct bcm2835_cprman *cprman,
|
||||
const struct bcm2835_pll_divider_data *data)
|
||||
const void *data)
|
||||
{
|
||||
const struct bcm2835_pll_divider_data *divider_data = data;
|
||||
struct bcm2835_pll_divider *divider;
|
||||
struct clk_init_data init;
|
||||
const char *divider_name;
|
||||
int ret;
|
||||
|
||||
if (data->fixed_divider != 1) {
|
||||
if (divider_data->fixed_divider != 1) {
|
||||
divider_name = devm_kasprintf(cprman->dev, GFP_KERNEL,
|
||||
"%s_prediv", data->name);
|
||||
"%s_prediv", divider_data->name);
|
||||
if (!divider_name)
|
||||
return NULL;
|
||||
} else {
|
||||
divider_name = data->name;
|
||||
divider_name = divider_data->name;
|
||||
}
|
||||
|
||||
memset(&init, 0, sizeof(init));
|
||||
|
||||
init.parent_names = &data->source_pll;
|
||||
init.parent_names = ÷r_data->source_pll;
|
||||
init.num_parents = 1;
|
||||
init.name = divider_name;
|
||||
init.ops = &bcm2835_pll_divider_clk_ops;
|
||||
init.flags = data->flags | CLK_IGNORE_UNUSED;
|
||||
init.flags = divider_data->flags | CLK_IGNORE_UNUSED;
|
||||
|
||||
divider = devm_kzalloc(cprman->dev, sizeof(*divider), GFP_KERNEL);
|
||||
if (!divider)
|
||||
return NULL;
|
||||
|
||||
divider->div.reg = cprman->regs + data->a2w_reg;
|
||||
divider->div.reg = cprman->regs + divider_data->a2w_reg;
|
||||
divider->div.shift = A2W_PLL_DIV_SHIFT;
|
||||
divider->div.width = A2W_PLL_DIV_BITS;
|
||||
divider->div.flags = CLK_DIVIDER_MAX_AT_ZERO;
|
||||
@ -1364,7 +1366,7 @@ bcm2835_register_pll_divider(struct bcm2835_cprman *cprman,
|
||||
divider->div.table = NULL;
|
||||
|
||||
divider->cprman = cprman;
|
||||
divider->data = data;
|
||||
divider->data = divider_data;
|
||||
|
||||
ret = devm_clk_hw_register(cprman->dev, ÷r->div.hw);
|
||||
if (ret)
|
||||
@ -1374,20 +1376,22 @@ bcm2835_register_pll_divider(struct bcm2835_cprman *cprman,
|
||||
* PLLH's channels have a fixed divide by 10 afterwards, which
|
||||
* is what our consumers are actually using.
|
||||
*/
|
||||
if (data->fixed_divider != 1) {
|
||||
return clk_hw_register_fixed_factor(cprman->dev, data->name,
|
||||
if (divider_data->fixed_divider != 1) {
|
||||
return clk_hw_register_fixed_factor(cprman->dev,
|
||||
divider_data->name,
|
||||
divider_name,
|
||||
CLK_SET_RATE_PARENT,
|
||||
1,
|
||||
data->fixed_divider);
|
||||
divider_data->fixed_divider);
|
||||
}
|
||||
|
||||
return ÷r->div.hw;
|
||||
}
|
||||
|
||||
static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman,
|
||||
const struct bcm2835_clock_data *data)
|
||||
const void *data)
|
||||
{
|
||||
const struct bcm2835_clock_data *clock_data = data;
|
||||
struct bcm2835_clock *clock;
|
||||
struct clk_init_data init;
|
||||
const char *parents[1 << CM_SRC_BITS];
|
||||
@ -1398,8 +1402,8 @@ static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman,
|
||||
* Replace our strings referencing parent clocks with the
|
||||
* actual clock-output-name of the parent.
|
||||
*/
|
||||
for (i = 0; i < data->num_mux_parents; i++) {
|
||||
parents[i] = data->parents[i];
|
||||
for (i = 0; i < clock_data->num_mux_parents; i++) {
|
||||
parents[i] = clock_data->parents[i];
|
||||
|
||||
ret = match_string(cprman_parent_names,
|
||||
ARRAY_SIZE(cprman_parent_names),
|
||||
@ -1410,18 +1414,18 @@ static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman,
|
||||
|
||||
memset(&init, 0, sizeof(init));
|
||||
init.parent_names = parents;
|
||||
init.num_parents = data->num_mux_parents;
|
||||
init.name = data->name;
|
||||
init.flags = data->flags | CLK_IGNORE_UNUSED;
|
||||
init.num_parents = clock_data->num_mux_parents;
|
||||
init.name = clock_data->name;
|
||||
init.flags = clock_data->flags | CLK_IGNORE_UNUSED;
|
||||
|
||||
/*
|
||||
* Pass the CLK_SET_RATE_PARENT flag if we are allowed to propagate
|
||||
* rate changes on at least of the parents.
|
||||
*/
|
||||
if (data->set_rate_parent)
|
||||
if (clock_data->set_rate_parent)
|
||||
init.flags |= CLK_SET_RATE_PARENT;
|
||||
|
||||
if (data->is_vpu_clock) {
|
||||
if (clock_data->is_vpu_clock) {
|
||||
init.ops = &bcm2835_vpu_clock_clk_ops;
|
||||
} else {
|
||||
init.ops = &bcm2835_clock_clk_ops;
|
||||
@ -1430,7 +1434,7 @@ static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman,
|
||||
/* If the clock wasn't actually enabled at boot, it's not
|
||||
* critical.
|
||||
*/
|
||||
if (!(cprman_read(cprman, data->ctl_reg) & CM_ENABLE))
|
||||
if (!(cprman_read(cprman, clock_data->ctl_reg) & CM_ENABLE))
|
||||
init.flags &= ~CLK_IS_CRITICAL;
|
||||
}
|
||||
|
||||
@ -1439,7 +1443,7 @@ static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman,
|
||||
return NULL;
|
||||
|
||||
clock->cprman = cprman;
|
||||
clock->data = data;
|
||||
clock->data = clock_data;
|
||||
clock->hw.init = &init;
|
||||
|
||||
ret = devm_clk_hw_register(cprman->dev, &clock->hw);
|
||||
@ -1448,25 +1452,27 @@ static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman,
|
||||
return &clock->hw;
|
||||
}
|
||||
|
||||
static struct clk *bcm2835_register_gate(struct bcm2835_cprman *cprman,
|
||||
const struct bcm2835_gate_data *data)
|
||||
static struct clk_hw *bcm2835_register_gate(struct bcm2835_cprman *cprman,
|
||||
const void *data)
|
||||
{
|
||||
return clk_register_gate(cprman->dev, data->name, data->parent,
|
||||
CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE,
|
||||
cprman->regs + data->ctl_reg,
|
||||
CM_GATE_BIT, 0, &cprman->regs_lock);
|
||||
const struct bcm2835_gate_data *gate_data = data;
|
||||
|
||||
return clk_hw_register_gate(cprman->dev, gate_data->name,
|
||||
gate_data->parent,
|
||||
CLK_IGNORE_UNUSED | CLK_SET_RATE_GATE,
|
||||
cprman->regs + gate_data->ctl_reg,
|
||||
CM_GATE_BIT, 0, &cprman->regs_lock);
|
||||
}
|
||||
|
||||
typedef struct clk_hw *(*bcm2835_clk_register)(struct bcm2835_cprman *cprman,
|
||||
const void *data);
|
||||
struct bcm2835_clk_desc {
|
||||
bcm2835_clk_register clk_register;
|
||||
struct clk_hw *(*clk_register)(struct bcm2835_cprman *cprman,
|
||||
const void *data);
|
||||
unsigned int supported;
|
||||
const void *data;
|
||||
};
|
||||
|
||||
/* assignment helper macros for different clock types */
|
||||
#define _REGISTER(f, s, ...) { .clk_register = (bcm2835_clk_register)f, \
|
||||
#define _REGISTER(f, s, ...) { .clk_register = f, \
|
||||
.supported = s, \
|
||||
.data = __VA_ARGS__ }
|
||||
#define REGISTER_PLL(s, ...) _REGISTER(&bcm2835_register_pll, \
|
||||
|
@ -642,14 +642,22 @@ static const u32 ast2600_a0_axi_ahb_div_table[] = {
|
||||
2, 2, 3, 5,
|
||||
};
|
||||
|
||||
static const u32 ast2600_a1_axi_ahb_div_table[] = {
|
||||
4, 6, 2, 4,
|
||||
static const u32 ast2600_a1_axi_ahb_div0_tbl[] = {
|
||||
3, 2, 3, 4,
|
||||
};
|
||||
|
||||
static const u32 ast2600_a1_axi_ahb_div1_tbl[] = {
|
||||
3, 4, 6, 8,
|
||||
};
|
||||
|
||||
static const u32 ast2600_a1_axi_ahb200_tbl[] = {
|
||||
3, 4, 3, 4, 2, 2, 2, 2,
|
||||
};
|
||||
|
||||
static void __init aspeed_g6_cc(struct regmap *map)
|
||||
{
|
||||
struct clk_hw *hw;
|
||||
u32 val, div, chip_id, axi_div, ahb_div;
|
||||
u32 val, div, divbits, chip_id, axi_div, ahb_div;
|
||||
|
||||
clk_hw_register_fixed_rate(NULL, "clkin", NULL, 0, 25000000);
|
||||
|
||||
@ -679,11 +687,22 @@ static void __init aspeed_g6_cc(struct regmap *map)
|
||||
else
|
||||
axi_div = 2;
|
||||
|
||||
divbits = (val >> 11) & 0x3;
|
||||
regmap_read(map, ASPEED_G6_SILICON_REV, &chip_id);
|
||||
if (chip_id & BIT(16))
|
||||
ahb_div = ast2600_a1_axi_ahb_div_table[(val >> 11) & 0x3];
|
||||
else
|
||||
if (chip_id & BIT(16)) {
|
||||
if (!divbits) {
|
||||
ahb_div = ast2600_a1_axi_ahb200_tbl[(val >> 8) & 0x3];
|
||||
if (val & BIT(16))
|
||||
ahb_div *= 2;
|
||||
} else {
|
||||
if (val & BIT(16))
|
||||
ahb_div = ast2600_a1_axi_ahb_div1_tbl[divbits];
|
||||
else
|
||||
ahb_div = ast2600_a1_axi_ahb_div0_tbl[divbits];
|
||||
}
|
||||
} else {
|
||||
ahb_div = ast2600_a0_axi_ahb_div_table[(val >> 11) & 0x3];
|
||||
}
|
||||
|
||||
hw = clk_hw_register_fixed_factor(NULL, "ahb", "hpll", 0, 1, axi_div * ahb_div);
|
||||
aspeed_g6_clk_data->hws[ASPEED_CLK_AHB] = hw;
|
||||
|
@ -53,35 +53,38 @@ struct hsdk_pll_cfg {
|
||||
u32 fbdiv;
|
||||
u32 odiv;
|
||||
u32 band;
|
||||
u32 bypass;
|
||||
};
|
||||
|
||||
static const struct hsdk_pll_cfg asdt_pll_cfg[] = {
|
||||
{ 100000000, 0, 11, 3, 0 },
|
||||
{ 133000000, 0, 15, 3, 0 },
|
||||
{ 200000000, 1, 47, 3, 0 },
|
||||
{ 233000000, 1, 27, 2, 0 },
|
||||
{ 300000000, 1, 35, 2, 0 },
|
||||
{ 333000000, 1, 39, 2, 0 },
|
||||
{ 400000000, 1, 47, 2, 0 },
|
||||
{ 500000000, 0, 14, 1, 0 },
|
||||
{ 600000000, 0, 17, 1, 0 },
|
||||
{ 700000000, 0, 20, 1, 0 },
|
||||
{ 800000000, 0, 23, 1, 0 },
|
||||
{ 900000000, 1, 26, 0, 0 },
|
||||
{ 1000000000, 1, 29, 0, 0 },
|
||||
{ 1100000000, 1, 32, 0, 0 },
|
||||
{ 1200000000, 1, 35, 0, 0 },
|
||||
{ 1300000000, 1, 38, 0, 0 },
|
||||
{ 1400000000, 1, 41, 0, 0 },
|
||||
{ 1500000000, 1, 44, 0, 0 },
|
||||
{ 1600000000, 1, 47, 0, 0 },
|
||||
{ 100000000, 0, 11, 3, 0, 0 },
|
||||
{ 133000000, 0, 15, 3, 0, 0 },
|
||||
{ 200000000, 1, 47, 3, 0, 0 },
|
||||
{ 233000000, 1, 27, 2, 0, 0 },
|
||||
{ 300000000, 1, 35, 2, 0, 0 },
|
||||
{ 333000000, 1, 39, 2, 0, 0 },
|
||||
{ 400000000, 1, 47, 2, 0, 0 },
|
||||
{ 500000000, 0, 14, 1, 0, 0 },
|
||||
{ 600000000, 0, 17, 1, 0, 0 },
|
||||
{ 700000000, 0, 20, 1, 0, 0 },
|
||||
{ 800000000, 0, 23, 1, 0, 0 },
|
||||
{ 900000000, 1, 26, 0, 0, 0 },
|
||||
{ 1000000000, 1, 29, 0, 0, 0 },
|
||||
{ 1100000000, 1, 32, 0, 0, 0 },
|
||||
{ 1200000000, 1, 35, 0, 0, 0 },
|
||||
{ 1300000000, 1, 38, 0, 0, 0 },
|
||||
{ 1400000000, 1, 41, 0, 0, 0 },
|
||||
{ 1500000000, 1, 44, 0, 0, 0 },
|
||||
{ 1600000000, 1, 47, 0, 0, 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct hsdk_pll_cfg hdmi_pll_cfg[] = {
|
||||
{ 297000000, 0, 21, 2, 0 },
|
||||
{ 540000000, 0, 19, 1, 0 },
|
||||
{ 594000000, 0, 21, 1, 0 },
|
||||
{ 27000000, 0, 0, 0, 0, 1 },
|
||||
{ 148500000, 0, 21, 3, 0, 0 },
|
||||
{ 297000000, 0, 21, 2, 0, 0 },
|
||||
{ 540000000, 0, 19, 1, 0, 0 },
|
||||
{ 594000000, 0, 21, 1, 0, 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -134,11 +137,16 @@ static inline void hsdk_pll_set_cfg(struct hsdk_pll_clk *clk,
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
/* Powerdown and Bypass bits should be cleared */
|
||||
val |= cfg->idiv << CGU_PLL_CTRL_IDIV_SHIFT;
|
||||
val |= cfg->fbdiv << CGU_PLL_CTRL_FBDIV_SHIFT;
|
||||
val |= cfg->odiv << CGU_PLL_CTRL_ODIV_SHIFT;
|
||||
val |= cfg->band << CGU_PLL_CTRL_BAND_SHIFT;
|
||||
if (cfg->bypass) {
|
||||
val = hsdk_pll_read(clk, CGU_PLL_CTRL);
|
||||
val |= CGU_PLL_CTRL_BYPASS;
|
||||
} else {
|
||||
/* Powerdown and Bypass bits should be cleared */
|
||||
val |= cfg->idiv << CGU_PLL_CTRL_IDIV_SHIFT;
|
||||
val |= cfg->fbdiv << CGU_PLL_CTRL_FBDIV_SHIFT;
|
||||
val |= cfg->odiv << CGU_PLL_CTRL_ODIV_SHIFT;
|
||||
val |= cfg->band << CGU_PLL_CTRL_BAND_SHIFT;
|
||||
}
|
||||
|
||||
dev_dbg(clk->dev, "write configuration: %#x\n", val);
|
||||
|
||||
@ -172,14 +180,14 @@ static unsigned long hsdk_pll_recalc_rate(struct clk_hw *hw,
|
||||
|
||||
dev_dbg(clk->dev, "current configuration: %#x\n", val);
|
||||
|
||||
/* Check if PLL is disabled */
|
||||
if (val & CGU_PLL_CTRL_PD)
|
||||
return 0;
|
||||
|
||||
/* Check if PLL is bypassed */
|
||||
if (val & CGU_PLL_CTRL_BYPASS)
|
||||
return parent_rate;
|
||||
|
||||
/* Check if PLL is disabled */
|
||||
if (val & CGU_PLL_CTRL_PD)
|
||||
return 0;
|
||||
|
||||
/* input divider = reg.idiv + 1 */
|
||||
idiv = 1 + ((val & CGU_PLL_CTRL_IDIV_MASK) >> CGU_PLL_CTRL_IDIV_SHIFT);
|
||||
/* fb divider = 2*(reg.fbdiv + 1) */
|
||||
|
@ -1,8 +1,14 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Driver for Silicon Labs Si5341/Si5340 Clock generator
|
||||
* Driver for Silicon Labs Si5340, Si5341, Si5342, Si5344 and Si5345
|
||||
* Copyright (C) 2019 Topic Embedded Products
|
||||
* Author: Mike Looijmans <mike.looijmans@topic.nl>
|
||||
*
|
||||
* The Si5341 has 10 outputs and 5 synthesizers.
|
||||
* The Si5340 is a smaller version of the Si5341 with only 4 outputs.
|
||||
* The Si5345 is similar to the Si5341, with the addition of fractional input
|
||||
* dividers and automatic input selection.
|
||||
* The Si5342 and Si5344 are smaller versions of the Si5345.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
@ -18,11 +24,17 @@
|
||||
|
||||
#define SI5341_NUM_INPUTS 4
|
||||
|
||||
#define SI5341_MAX_NUM_OUTPUTS 10
|
||||
#define SI5340_MAX_NUM_OUTPUTS 4
|
||||
#define SI5341_MAX_NUM_OUTPUTS 10
|
||||
#define SI5342_MAX_NUM_OUTPUTS 2
|
||||
#define SI5344_MAX_NUM_OUTPUTS 4
|
||||
#define SI5345_MAX_NUM_OUTPUTS 10
|
||||
|
||||
#define SI5341_NUM_SYNTH 5
|
||||
#define SI5340_NUM_SYNTH 4
|
||||
#define SI5341_NUM_SYNTH 5
|
||||
#define SI5342_NUM_SYNTH 2
|
||||
#define SI5344_NUM_SYNTH 4
|
||||
#define SI5345_NUM_SYNTH 5
|
||||
|
||||
/* Range of the synthesizer fractional divider */
|
||||
#define SI5341_SYNTH_N_MIN 10
|
||||
@ -65,6 +77,7 @@ struct clk_si5341 {
|
||||
u64 freq_vco; /* 13500–14256 MHz */
|
||||
u8 num_outputs;
|
||||
u8 num_synth;
|
||||
u16 chip_id;
|
||||
};
|
||||
#define to_clk_si5341(_hw) container_of(_hw, struct clk_si5341, hw)
|
||||
|
||||
@ -142,6 +155,7 @@ static const char * const si5341_input_clock_names[] = {
|
||||
};
|
||||
|
||||
/* Output configuration registers 0..9 are not quite logically organized */
|
||||
/* Also for si5345 */
|
||||
static const u16 si5341_reg_output_offset[] = {
|
||||
0x0108,
|
||||
0x010D,
|
||||
@ -155,6 +169,7 @@ static const u16 si5341_reg_output_offset[] = {
|
||||
0x013A,
|
||||
};
|
||||
|
||||
/* for si5340, si5342 and si5344 */
|
||||
static const u16 si5340_reg_output_offset[] = {
|
||||
0x0112,
|
||||
0x0117,
|
||||
@ -974,12 +989,32 @@ static int si5341_probe_chip_id(struct clk_si5341 *data)
|
||||
data->reg_output_offset = si5341_reg_output_offset;
|
||||
data->reg_rdiv_offset = si5341_reg_rdiv_offset;
|
||||
break;
|
||||
case 0x5342:
|
||||
data->num_outputs = SI5342_MAX_NUM_OUTPUTS;
|
||||
data->num_synth = SI5342_NUM_SYNTH;
|
||||
data->reg_output_offset = si5340_reg_output_offset;
|
||||
data->reg_rdiv_offset = si5340_reg_rdiv_offset;
|
||||
break;
|
||||
case 0x5344:
|
||||
data->num_outputs = SI5344_MAX_NUM_OUTPUTS;
|
||||
data->num_synth = SI5344_NUM_SYNTH;
|
||||
data->reg_output_offset = si5340_reg_output_offset;
|
||||
data->reg_rdiv_offset = si5340_reg_rdiv_offset;
|
||||
break;
|
||||
case 0x5345:
|
||||
data->num_outputs = SI5345_MAX_NUM_OUTPUTS;
|
||||
data->num_synth = SI5345_NUM_SYNTH;
|
||||
data->reg_output_offset = si5341_reg_output_offset;
|
||||
data->reg_rdiv_offset = si5341_reg_rdiv_offset;
|
||||
break;
|
||||
default:
|
||||
dev_err(&data->i2c_client->dev, "Model '%x' not supported\n",
|
||||
model);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->chip_id = model;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1054,6 +1089,11 @@ static const struct si5341_reg_default si5341_preamble[] = {
|
||||
{ 0x0B4E, 0x1A },
|
||||
};
|
||||
|
||||
static const struct si5341_reg_default si5345_preamble[] = {
|
||||
{ 0x0B25, 0x00 },
|
||||
{ 0x0540, 0x01 },
|
||||
};
|
||||
|
||||
static int si5341_send_preamble(struct clk_si5341 *data)
|
||||
{
|
||||
int res;
|
||||
@ -1068,8 +1108,14 @@ static int si5341_send_preamble(struct clk_si5341 *data)
|
||||
res = regmap_write(data->regmap, 0xB24, revision < 2 ? 0xD8 : 0xC0);
|
||||
if (res < 0)
|
||||
return res;
|
||||
res = si5341_write_multiple(data,
|
||||
si5341_preamble, ARRAY_SIZE(si5341_preamble));
|
||||
|
||||
/* The si5342..si5345 require a different preamble */
|
||||
if (data->chip_id > 0x5341)
|
||||
res = si5341_write_multiple(data,
|
||||
si5345_preamble, ARRAY_SIZE(si5345_preamble));
|
||||
else
|
||||
res = si5341_write_multiple(data,
|
||||
si5341_preamble, ARRAY_SIZE(si5341_preamble));
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
@ -1095,6 +1141,13 @@ static int si5341_finalize_defaults(struct clk_si5341 *data)
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
/* The si5342..si5345 have an additional post-amble */
|
||||
if (data->chip_id > 0x5341) {
|
||||
res = regmap_write(data->regmap, 0x540, 0x0);
|
||||
if (res < 0)
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Datasheet does not explain these nameless registers */
|
||||
res = regmap_write(data->regmap, 0xB24, revision < 2 ? 0xDB : 0xC3);
|
||||
if (res < 0)
|
||||
@ -1499,6 +1552,9 @@ static int si5341_probe(struct i2c_client *client,
|
||||
static const struct i2c_device_id si5341_id[] = {
|
||||
{ "si5340", 0 },
|
||||
{ "si5341", 1 },
|
||||
{ "si5342", 2 },
|
||||
{ "si5344", 4 },
|
||||
{ "si5345", 5 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si5341_id);
|
||||
@ -1506,6 +1562,9 @@ MODULE_DEVICE_TABLE(i2c, si5341_id);
|
||||
static const struct of_device_id clk_si5341_of_match[] = {
|
||||
{ .compatible = "silabs,si5340" },
|
||||
{ .compatible = "silabs,si5341" },
|
||||
{ .compatible = "silabs,si5342" },
|
||||
{ .compatible = "silabs,si5344" },
|
||||
{ .compatible = "silabs,si5345" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, clk_si5341_of_match);
|
||||
|
@ -124,6 +124,7 @@ enum vc5_model {
|
||||
IDT_VC5_5P49V5933,
|
||||
IDT_VC5_5P49V5935,
|
||||
IDT_VC6_5P49V6901,
|
||||
IDT_VC6_5P49V6965,
|
||||
};
|
||||
|
||||
/* Structure to describe features of a particular VC5 model */
|
||||
@ -683,6 +684,7 @@ static int vc5_map_index_to_output(const enum vc5_model model,
|
||||
case IDT_VC5_5P49V5925:
|
||||
case IDT_VC5_5P49V5935:
|
||||
case IDT_VC6_5P49V6901:
|
||||
case IDT_VC6_5P49V6965:
|
||||
default:
|
||||
return n;
|
||||
}
|
||||
@ -956,12 +958,20 @@ static const struct vc5_chip_info idt_5p49v6901_info = {
|
||||
.flags = VC5_HAS_PFD_FREQ_DBL,
|
||||
};
|
||||
|
||||
static const struct vc5_chip_info idt_5p49v6965_info = {
|
||||
.model = IDT_VC6_5P49V6965,
|
||||
.clk_fod_cnt = 4,
|
||||
.clk_out_cnt = 5,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id vc5_id[] = {
|
||||
{ "5p49v5923", .driver_data = IDT_VC5_5P49V5923 },
|
||||
{ "5p49v5925", .driver_data = IDT_VC5_5P49V5925 },
|
||||
{ "5p49v5933", .driver_data = IDT_VC5_5P49V5933 },
|
||||
{ "5p49v5935", .driver_data = IDT_VC5_5P49V5935 },
|
||||
{ "5p49v6901", .driver_data = IDT_VC6_5P49V6901 },
|
||||
{ "5p49v6965", .driver_data = IDT_VC6_5P49V6965 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, vc5_id);
|
||||
@ -972,6 +982,7 @@ static const struct of_device_id clk_vc5_of_match[] = {
|
||||
{ .compatible = "idt,5p49v5933", .data = &idt_5p49v5933_info },
|
||||
{ .compatible = "idt,5p49v5935", .data = &idt_5p49v5935_info },
|
||||
{ .compatible = "idt,5p49v6901", .data = &idt_5p49v6901_info },
|
||||
{ .compatible = "idt,5p49v6965", .data = &idt_5p49v6965_info },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, clk_vc5_of_match);
|
||||
|
@ -3299,10 +3299,6 @@ static int __init clk_debug_init(void)
|
||||
late_initcall(clk_debug_init);
|
||||
#else
|
||||
static inline void clk_debug_register(struct clk_core *core) { }
|
||||
static inline void clk_debug_reparent(struct clk_core *core,
|
||||
struct clk_core *new_parent)
|
||||
{
|
||||
}
|
||||
static inline void clk_debug_unregister(struct clk_core *core)
|
||||
{
|
||||
}
|
||||
|
@ -10,25 +10,25 @@ config MXC_CLK_SCU
|
||||
|
||||
config CLK_IMX8MM
|
||||
bool "IMX8MM CCM Clock Driver"
|
||||
depends on ARCH_MXC && ARM64
|
||||
depends on ARCH_MXC
|
||||
help
|
||||
Build the driver for i.MX8MM CCM Clock Driver
|
||||
|
||||
config CLK_IMX8MN
|
||||
bool "IMX8MN CCM Clock Driver"
|
||||
depends on ARCH_MXC && ARM64
|
||||
depends on ARCH_MXC
|
||||
help
|
||||
Build the driver for i.MX8MN CCM Clock Driver
|
||||
|
||||
config CLK_IMX8MP
|
||||
bool "IMX8MP CCM Clock Driver"
|
||||
depends on ARCH_MXC && ARM64
|
||||
depends on ARCH_MXC
|
||||
help
|
||||
Build the driver for i.MX8MP CCM Clock Driver
|
||||
|
||||
config CLK_IMX8MQ
|
||||
bool "IMX8MQ CCM Clock Driver"
|
||||
depends on ARCH_MXC && ARM64
|
||||
depends on ARCH_MXC
|
||||
help
|
||||
Build the driver for i.MX8MQ CCM Clock Driver
|
||||
|
||||
|
@ -124,6 +124,52 @@ static const struct clk_ops imx8m_clk_composite_divider_ops = {
|
||||
.set_rate = imx8m_clk_composite_divider_set_rate,
|
||||
};
|
||||
|
||||
static u8 imx8m_clk_composite_mux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
return clk_mux_ops.get_parent(hw);
|
||||
}
|
||||
|
||||
static int imx8m_clk_composite_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(hw);
|
||||
u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
|
||||
unsigned long flags = 0;
|
||||
u32 reg;
|
||||
|
||||
if (mux->lock)
|
||||
spin_lock_irqsave(mux->lock, flags);
|
||||
|
||||
reg = readl(mux->reg);
|
||||
reg &= ~(mux->mask << mux->shift);
|
||||
val = val << mux->shift;
|
||||
reg |= val;
|
||||
/*
|
||||
* write twice to make sure non-target interface
|
||||
* SEL_A/B point the same clk input.
|
||||
*/
|
||||
writel(reg, mux->reg);
|
||||
writel(reg, mux->reg);
|
||||
|
||||
if (mux->lock)
|
||||
spin_unlock_irqrestore(mux->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
imx8m_clk_composite_mux_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
return clk_mux_ops.determine_rate(hw, req);
|
||||
}
|
||||
|
||||
|
||||
static const struct clk_ops imx8m_clk_composite_mux_ops = {
|
||||
.get_parent = imx8m_clk_composite_mux_get_parent,
|
||||
.set_parent = imx8m_clk_composite_mux_set_parent,
|
||||
.determine_rate = imx8m_clk_composite_mux_determine_rate,
|
||||
};
|
||||
|
||||
struct clk_hw *imx8m_clk_hw_composite_flags(const char *name,
|
||||
const char * const *parent_names,
|
||||
int num_parents, void __iomem *reg,
|
||||
@ -136,6 +182,7 @@ struct clk_hw *imx8m_clk_hw_composite_flags(const char *name,
|
||||
struct clk_gate *gate = NULL;
|
||||
struct clk_mux *mux = NULL;
|
||||
const struct clk_ops *divider_ops;
|
||||
const struct clk_ops *mux_ops;
|
||||
|
||||
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
@ -157,10 +204,17 @@ struct clk_hw *imx8m_clk_hw_composite_flags(const char *name,
|
||||
div->shift = PCG_DIV_SHIFT;
|
||||
div->width = PCG_CORE_DIV_WIDTH;
|
||||
divider_ops = &clk_divider_ops;
|
||||
mux_ops = &imx8m_clk_composite_mux_ops;
|
||||
} else if (composite_flags & IMX_COMPOSITE_BUS) {
|
||||
div->shift = PCG_PREDIV_SHIFT;
|
||||
div->width = PCG_PREDIV_WIDTH;
|
||||
divider_ops = &imx8m_clk_composite_divider_ops;
|
||||
mux_ops = &imx8m_clk_composite_mux_ops;
|
||||
} else {
|
||||
div->shift = PCG_PREDIV_SHIFT;
|
||||
div->width = PCG_PREDIV_WIDTH;
|
||||
divider_ops = &imx8m_clk_composite_divider_ops;
|
||||
mux_ops = &clk_mux_ops;
|
||||
}
|
||||
|
||||
div->lock = &imx_ccm_lock;
|
||||
@ -176,7 +230,7 @@ struct clk_hw *imx8m_clk_hw_composite_flags(const char *name,
|
||||
gate->lock = &imx_ccm_lock;
|
||||
|
||||
hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
|
||||
mux_hw, &clk_mux_ops, div_hw,
|
||||
mux_hw, mux_ops, div_hw,
|
||||
divider_ops, gate_hw, &clk_gate_ops, flags);
|
||||
if (IS_ERR(hw))
|
||||
goto fail;
|
||||
|
@ -41,21 +41,26 @@ static int clk_gate2_enable(struct clk_hw *hw)
|
||||
struct clk_gate2 *gate = to_clk_gate2(hw);
|
||||
u32 reg;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(gate->lock, flags);
|
||||
|
||||
if (gate->share_count && (*gate->share_count)++ > 0)
|
||||
goto out;
|
||||
|
||||
reg = readl(gate->reg);
|
||||
reg &= ~(3 << gate->bit_idx);
|
||||
reg |= gate->cgr_val << gate->bit_idx;
|
||||
writel(reg, gate->reg);
|
||||
if (gate->flags & IMX_CLK_GATE2_SINGLE_BIT) {
|
||||
ret = clk_gate_ops.enable(hw);
|
||||
} else {
|
||||
reg = readl(gate->reg);
|
||||
reg &= ~(3 << gate->bit_idx);
|
||||
reg |= gate->cgr_val << gate->bit_idx;
|
||||
writel(reg, gate->reg);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(gate->lock, flags);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void clk_gate2_disable(struct clk_hw *hw)
|
||||
@ -73,9 +78,13 @@ static void clk_gate2_disable(struct clk_hw *hw)
|
||||
goto out;
|
||||
}
|
||||
|
||||
reg = readl(gate->reg);
|
||||
reg &= ~(3 << gate->bit_idx);
|
||||
writel(reg, gate->reg);
|
||||
if (gate->flags & IMX_CLK_GATE2_SINGLE_BIT) {
|
||||
clk_gate_ops.disable(hw);
|
||||
} else {
|
||||
reg = readl(gate->reg);
|
||||
reg &= ~(3 << gate->bit_idx);
|
||||
writel(reg, gate->reg);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(gate->lock, flags);
|
||||
@ -95,6 +104,9 @@ static int clk_gate2_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_gate2 *gate = to_clk_gate2(hw);
|
||||
|
||||
if (gate->flags & IMX_CLK_GATE2_SINGLE_BIT)
|
||||
return clk_gate_ops.is_enabled(hw);
|
||||
|
||||
return clk_gate2_reg_is_enabled(gate->reg, gate->bit_idx);
|
||||
}
|
||||
|
||||
@ -104,6 +116,9 @@ static void clk_gate2_disable_unused(struct clk_hw *hw)
|
||||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
if (gate->flags & IMX_CLK_GATE2_SINGLE_BIT)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(gate->lock, flags);
|
||||
|
||||
if (!gate->share_count || *gate->share_count == 0) {
|
||||
|
@ -503,7 +503,7 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node)
|
||||
clk_prepare_enable(hws[IMX6UL_CLK_USBPHY2_GATE]->clk);
|
||||
}
|
||||
|
||||
clk_set_parent(hws[IMX6UL_CLK_CAN_SEL]->clk, hws[IMX6UL_CLK_PLL3_60M]->clk);
|
||||
clk_set_parent(hws[IMX6UL_CLK_CAN_SEL]->clk, hws[IMX6UL_CLK_PLL3_80M]->clk);
|
||||
if (clk_on_imx6ul())
|
||||
clk_set_parent(hws[IMX6UL_CLK_SIM_PRE_SEL]->clk, hws[IMX6UL_CLK_PLL3_USB_OTG]->clk);
|
||||
else if (clk_on_imx6ull())
|
||||
|
@ -29,7 +29,7 @@ static const char * const ddr_sels[] = { "apll_pfd_sel", "dummy", "dummy", "dum
|
||||
static const char * const nic_sels[] = { "firc", "ddr_clk", };
|
||||
static const char * const periph_plat_sels[] = { "dummy", "nic1_bus_clk", "nic1_clk", "ddr_clk", "apll_pfd2", "apll_pfd1", "apll_pfd0", "upll", };
|
||||
static const char * const periph_bus_sels[] = { "dummy", "sosc_bus_clk", "dummy", "firc_bus_clk", "rosc", "nic1_bus_clk", "nic1_clk", "spll_bus_clk", };
|
||||
static const char * const arm_sels[] = { "divcore", "dummy", "dummy", "hsrun_divcore", };
|
||||
static const char * const arm_sels[] = { "core", "dummy", "dummy", "hsrun_core", };
|
||||
|
||||
/* used by sosc/sirc/firc/ddr/spll/apll dividers */
|
||||
static const struct clk_div_table ulp_div_table[] = {
|
||||
@ -121,7 +121,9 @@ static void __init imx7ulp_clk_scg1_init(struct device_node *np)
|
||||
hws[IMX7ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x30, 24, 2, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE);
|
||||
|
||||
hws[IMX7ULP_CLK_CORE_DIV] = imx_clk_hw_divider_flags("divcore", "scs_sel", base + 0x14, 16, 4, CLK_SET_RATE_PARENT);
|
||||
hws[IMX7ULP_CLK_CORE] = imx_clk_hw_cpu("core", "divcore", hws[IMX7ULP_CLK_CORE_DIV]->clk, hws[IMX7ULP_CLK_SYS_SEL]->clk, hws[IMX7ULP_CLK_SPLL_SEL]->clk, hws[IMX7ULP_CLK_FIRC]->clk);
|
||||
hws[IMX7ULP_CLK_HSRUN_CORE_DIV] = imx_clk_hw_divider_flags("hsrun_divcore", "hsrun_scs_sel", base + 0x1c, 16, 4, CLK_SET_RATE_PARENT);
|
||||
hws[IMX7ULP_CLK_HSRUN_CORE] = imx_clk_hw_cpu("hsrun_core", "hsrun_divcore", hws[IMX7ULP_CLK_HSRUN_CORE_DIV]->clk, hws[IMX7ULP_CLK_HSRUN_SYS_SEL]->clk, hws[IMX7ULP_CLK_SPLL_SEL]->clk, hws[IMX7ULP_CLK_FIRC]->clk);
|
||||
|
||||
hws[IMX7ULP_CLK_DDR_DIV] = imx_clk_hw_divider_gate("ddr_clk", "ddr_sel", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, base + 0x30, 0, 3,
|
||||
0, ulp_div_table, &imx_ccm_lock);
|
||||
@ -270,7 +272,7 @@ static void __init imx7ulp_clk_smc1_init(struct device_node *np)
|
||||
base = of_iomap(np, 0);
|
||||
WARN_ON(!base);
|
||||
|
||||
hws[IMX7ULP_CLK_ARM] = imx_clk_hw_mux_flags("arm", base + 0x10, 8, 2, arm_sels, ARRAY_SIZE(arm_sels), CLK_IS_CRITICAL);
|
||||
hws[IMX7ULP_CLK_ARM] = imx_clk_hw_mux_flags("arm", base + 0x10, 8, 2, arm_sels, ARRAY_SIZE(arm_sels), CLK_SET_RATE_PARENT);
|
||||
|
||||
imx_check_clk_hws(hws, clk_data->num);
|
||||
|
||||
|
@ -416,9 +416,9 @@ static int imx8mm_clocks_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(base);
|
||||
|
||||
/* Core Slice */
|
||||
hws[IMX8MM_CLK_A53_SRC] = imx_clk_hw_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mm_a53_sels, ARRAY_SIZE(imx8mm_a53_sels));
|
||||
hws[IMX8MM_CLK_A53_CG] = imx_clk_hw_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28);
|
||||
hws[IMX8MM_CLK_A53_DIV] = imx_clk_hw_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3);
|
||||
hws[IMX8MM_CLK_A53_DIV] = imx8m_clk_hw_composite_core("arm_a53_div", imx8mm_a53_sels, base + 0x8000);
|
||||
hws[IMX8MM_CLK_A53_CG] = hws[IMX8MM_CLK_A53_DIV];
|
||||
hws[IMX8MM_CLK_A53_SRC] = hws[IMX8MM_CLK_A53_DIV];
|
||||
|
||||
hws[IMX8MM_CLK_M4_CORE] = imx8m_clk_hw_composite_core("arm_m4_core", imx8mm_m4_sels, base + 0x8080);
|
||||
hws[IMX8MM_CLK_VPU_CORE] = imx8m_clk_hw_composite_core("vpu_core", imx8mm_vpu_sels, base + 0x8100);
|
||||
@ -444,21 +444,21 @@ static int imx8mm_clocks_probe(struct platform_device *pdev)
|
||||
|
||||
/* BUS */
|
||||
hws[IMX8MM_CLK_MAIN_AXI] = imx8m_clk_hw_composite_critical("main_axi", imx8mm_main_axi_sels, base + 0x8800);
|
||||
hws[IMX8MM_CLK_ENET_AXI] = imx8m_clk_hw_composite("enet_axi", imx8mm_enet_axi_sels, base + 0x8880);
|
||||
hws[IMX8MM_CLK_ENET_AXI] = imx8m_clk_hw_composite_bus("enet_axi", imx8mm_enet_axi_sels, base + 0x8880);
|
||||
hws[IMX8MM_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_critical("nand_usdhc_bus", imx8mm_nand_usdhc_sels, base + 0x8900);
|
||||
hws[IMX8MM_CLK_VPU_BUS] = imx8m_clk_hw_composite("vpu_bus", imx8mm_vpu_bus_sels, base + 0x8980);
|
||||
hws[IMX8MM_CLK_DISP_AXI] = imx8m_clk_hw_composite("disp_axi", imx8mm_disp_axi_sels, base + 0x8a00);
|
||||
hws[IMX8MM_CLK_DISP_APB] = imx8m_clk_hw_composite("disp_apb", imx8mm_disp_apb_sels, base + 0x8a80);
|
||||
hws[IMX8MM_CLK_DISP_RTRM] = imx8m_clk_hw_composite("disp_rtrm", imx8mm_disp_rtrm_sels, base + 0x8b00);
|
||||
hws[IMX8MM_CLK_USB_BUS] = imx8m_clk_hw_composite("usb_bus", imx8mm_usb_bus_sels, base + 0x8b80);
|
||||
hws[IMX8MM_CLK_GPU_AXI] = imx8m_clk_hw_composite("gpu_axi", imx8mm_gpu_axi_sels, base + 0x8c00);
|
||||
hws[IMX8MM_CLK_GPU_AHB] = imx8m_clk_hw_composite("gpu_ahb", imx8mm_gpu_ahb_sels, base + 0x8c80);
|
||||
hws[IMX8MM_CLK_VPU_BUS] = imx8m_clk_hw_composite_bus("vpu_bus", imx8mm_vpu_bus_sels, base + 0x8980);
|
||||
hws[IMX8MM_CLK_DISP_AXI] = imx8m_clk_hw_composite_bus("disp_axi", imx8mm_disp_axi_sels, base + 0x8a00);
|
||||
hws[IMX8MM_CLK_DISP_APB] = imx8m_clk_hw_composite_bus("disp_apb", imx8mm_disp_apb_sels, base + 0x8a80);
|
||||
hws[IMX8MM_CLK_DISP_RTRM] = imx8m_clk_hw_composite_bus("disp_rtrm", imx8mm_disp_rtrm_sels, base + 0x8b00);
|
||||
hws[IMX8MM_CLK_USB_BUS] = imx8m_clk_hw_composite_bus("usb_bus", imx8mm_usb_bus_sels, base + 0x8b80);
|
||||
hws[IMX8MM_CLK_GPU_AXI] = imx8m_clk_hw_composite_bus("gpu_axi", imx8mm_gpu_axi_sels, base + 0x8c00);
|
||||
hws[IMX8MM_CLK_GPU_AHB] = imx8m_clk_hw_composite_bus("gpu_ahb", imx8mm_gpu_ahb_sels, base + 0x8c80);
|
||||
hws[IMX8MM_CLK_NOC] = imx8m_clk_hw_composite_critical("noc", imx8mm_noc_sels, base + 0x8d00);
|
||||
hws[IMX8MM_CLK_NOC_APB] = imx8m_clk_hw_composite_critical("noc_apb", imx8mm_noc_apb_sels, base + 0x8d80);
|
||||
|
||||
/* AHB */
|
||||
hws[IMX8MM_CLK_AHB] = imx8m_clk_hw_composite_critical("ahb", imx8mm_ahb_sels, base + 0x9000);
|
||||
hws[IMX8MM_CLK_AUDIO_AHB] = imx8m_clk_hw_composite("audio_ahb", imx8mm_audio_ahb_sels, base + 0x9100);
|
||||
hws[IMX8MM_CLK_AUDIO_AHB] = imx8m_clk_hw_composite_bus("audio_ahb", imx8mm_audio_ahb_sels, base + 0x9100);
|
||||
|
||||
/* IPG */
|
||||
hws[IMX8MM_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb", base + 0x9080, 0, 1);
|
||||
@ -614,9 +614,6 @@ static int imx8mm_clocks_probe(struct platform_device *pdev)
|
||||
hws[IMX8MM_ARM_PLL_OUT]->clk,
|
||||
hws[IMX8MM_CLK_A53_DIV]->clk);
|
||||
|
||||
clk_hw_set_parent(hws[IMX8MM_CLK_A53_SRC], hws[IMX8MM_SYS_PLL1_800M]);
|
||||
clk_hw_set_parent(hws[IMX8MM_CLK_A53_CORE], hws[IMX8MM_ARM_PLL_OUT]);
|
||||
|
||||
imx_check_clk_hws(hws, IMX8MM_CLK_END);
|
||||
|
||||
ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
|
||||
|
@ -413,9 +413,9 @@ static int imx8mn_clocks_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/* CORE */
|
||||
hws[IMX8MN_CLK_A53_SRC] = imx_clk_hw_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mn_a53_sels, ARRAY_SIZE(imx8mn_a53_sels));
|
||||
hws[IMX8MN_CLK_A53_CG] = imx_clk_hw_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28);
|
||||
hws[IMX8MN_CLK_A53_DIV] = imx_clk_hw_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3);
|
||||
hws[IMX8MN_CLK_A53_DIV] = imx8m_clk_hw_composite_core("arm_a53_div", imx8mn_a53_sels, base + 0x8000);
|
||||
hws[IMX8MN_CLK_A53_SRC] = hws[IMX8MN_CLK_A53_DIV];
|
||||
hws[IMX8MN_CLK_A53_CG] = hws[IMX8MN_CLK_A53_DIV];
|
||||
|
||||
hws[IMX8MN_CLK_GPU_CORE] = imx8m_clk_hw_composite_core("gpu_core", imx8mn_gpu_core_sels, base + 0x8180);
|
||||
hws[IMX8MN_CLK_GPU_SHADER] = imx8m_clk_hw_composite_core("gpu_shader", imx8mn_gpu_shader_sels, base + 0x8200);
|
||||
@ -432,17 +432,17 @@ static int imx8mn_clocks_probe(struct platform_device *pdev)
|
||||
|
||||
/* BUS */
|
||||
hws[IMX8MN_CLK_MAIN_AXI] = imx8m_clk_hw_composite_critical("main_axi", imx8mn_main_axi_sels, base + 0x8800);
|
||||
hws[IMX8MN_CLK_ENET_AXI] = imx8m_clk_hw_composite("enet_axi", imx8mn_enet_axi_sels, base + 0x8880);
|
||||
hws[IMX8MN_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite("nand_usdhc_bus", imx8mn_nand_usdhc_sels, base + 0x8900);
|
||||
hws[IMX8MN_CLK_DISP_AXI] = imx8m_clk_hw_composite("disp_axi", imx8mn_disp_axi_sels, base + 0x8a00);
|
||||
hws[IMX8MN_CLK_DISP_APB] = imx8m_clk_hw_composite("disp_apb", imx8mn_disp_apb_sels, base + 0x8a80);
|
||||
hws[IMX8MN_CLK_USB_BUS] = imx8m_clk_hw_composite("usb_bus", imx8mn_usb_bus_sels, base + 0x8b80);
|
||||
hws[IMX8MN_CLK_GPU_AXI] = imx8m_clk_hw_composite("gpu_axi", imx8mn_gpu_axi_sels, base + 0x8c00);
|
||||
hws[IMX8MN_CLK_GPU_AHB] = imx8m_clk_hw_composite("gpu_ahb", imx8mn_gpu_ahb_sels, base + 0x8c80);
|
||||
hws[IMX8MN_CLK_ENET_AXI] = imx8m_clk_hw_composite_bus("enet_axi", imx8mn_enet_axi_sels, base + 0x8880);
|
||||
hws[IMX8MN_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_bus("nand_usdhc_bus", imx8mn_nand_usdhc_sels, base + 0x8900);
|
||||
hws[IMX8MN_CLK_DISP_AXI] = imx8m_clk_hw_composite_bus("disp_axi", imx8mn_disp_axi_sels, base + 0x8a00);
|
||||
hws[IMX8MN_CLK_DISP_APB] = imx8m_clk_hw_composite_bus("disp_apb", imx8mn_disp_apb_sels, base + 0x8a80);
|
||||
hws[IMX8MN_CLK_USB_BUS] = imx8m_clk_hw_composite_bus("usb_bus", imx8mn_usb_bus_sels, base + 0x8b80);
|
||||
hws[IMX8MN_CLK_GPU_AXI] = imx8m_clk_hw_composite_bus("gpu_axi", imx8mn_gpu_axi_sels, base + 0x8c00);
|
||||
hws[IMX8MN_CLK_GPU_AHB] = imx8m_clk_hw_composite_bus("gpu_ahb", imx8mn_gpu_ahb_sels, base + 0x8c80);
|
||||
hws[IMX8MN_CLK_NOC] = imx8m_clk_hw_composite_critical("noc", imx8mn_noc_sels, base + 0x8d00);
|
||||
|
||||
hws[IMX8MN_CLK_AHB] = imx8m_clk_hw_composite_critical("ahb", imx8mn_ahb_sels, base + 0x9000);
|
||||
hws[IMX8MN_CLK_AUDIO_AHB] = imx8m_clk_hw_composite("audio_ahb", imx8mn_audio_ahb_sels, base + 0x9100);
|
||||
hws[IMX8MN_CLK_AUDIO_AHB] = imx8m_clk_hw_composite_bus("audio_ahb", imx8mn_audio_ahb_sels, base + 0x9100);
|
||||
hws[IMX8MN_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb", base + 0x9080, 0, 1);
|
||||
hws[IMX8MN_CLK_IPG_AUDIO_ROOT] = imx_clk_hw_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1);
|
||||
hws[IMX8MN_CLK_DRAM_CORE] = imx_clk_hw_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mn_dram_core_sels, ARRAY_SIZE(imx8mn_dram_core_sels), CLK_IS_CRITICAL);
|
||||
@ -565,9 +565,6 @@ static int imx8mn_clocks_probe(struct platform_device *pdev)
|
||||
hws[IMX8MN_ARM_PLL_OUT]->clk,
|
||||
hws[IMX8MN_CLK_A53_DIV]->clk);
|
||||
|
||||
clk_hw_set_parent(hws[IMX8MN_CLK_A53_SRC], hws[IMX8MN_SYS_PLL1_800M]);
|
||||
clk_hw_set_parent(hws[IMX8MN_CLK_A53_CORE], hws[IMX8MN_ARM_PLL_OUT]);
|
||||
|
||||
imx_check_clk_hws(hws, IMX8MN_CLK_END);
|
||||
|
||||
ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
|
||||
|
@ -486,16 +486,16 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
|
||||
hws[IMX8MP_SYS_PLL2] = imx_clk_hw_pll14xx("sys_pll2", "sys_pll2_ref_sel", anatop_base + 0x104, &imx_1416x_pll);
|
||||
hws[IMX8MP_SYS_PLL3] = imx_clk_hw_pll14xx("sys_pll3", "sys_pll3_ref_sel", anatop_base + 0x114, &imx_1416x_pll);
|
||||
|
||||
hws[IMX8MP_AUDIO_PLL1_BYPASS] = imx_clk_hw_mux_flags("audio_pll1_bypass", anatop_base, 4, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_AUDIO_PLL2_BYPASS] = imx_clk_hw_mux_flags("audio_pll2_bypass", anatop_base + 0x14, 4, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_VIDEO_PLL1_BYPASS] = imx_clk_hw_mux_flags("video_pll1_bypass", anatop_base + 0x28, 4, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_DRAM_PLL_BYPASS] = imx_clk_hw_mux_flags("dram_pll_bypass", anatop_base + 0x50, 4, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_GPU_PLL_BYPASS] = imx_clk_hw_mux_flags("gpu_pll_bypass", anatop_base + 0x64, 4, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_VPU_PLL_BYPASS] = imx_clk_hw_mux_flags("vpu_pll_bypass", anatop_base + 0x74, 4, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_ARM_PLL_BYPASS] = imx_clk_hw_mux_flags("arm_pll_bypass", anatop_base + 0x84, 4, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_SYS_PLL1_BYPASS] = imx_clk_hw_mux_flags("sys_pll1_bypass", anatop_base + 0x94, 4, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_SYS_PLL2_BYPASS] = imx_clk_hw_mux_flags("sys_pll2_bypass", anatop_base + 0x104, 4, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_SYS_PLL3_BYPASS] = imx_clk_hw_mux_flags("sys_pll3_bypass", anatop_base + 0x114, 4, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_AUDIO_PLL1_BYPASS] = imx_clk_hw_mux_flags("audio_pll1_bypass", anatop_base, 16, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_AUDIO_PLL2_BYPASS] = imx_clk_hw_mux_flags("audio_pll2_bypass", anatop_base + 0x14, 16, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_VIDEO_PLL1_BYPASS] = imx_clk_hw_mux_flags("video_pll1_bypass", anatop_base + 0x28, 16, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_DRAM_PLL_BYPASS] = imx_clk_hw_mux_flags("dram_pll_bypass", anatop_base + 0x50, 16, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_GPU_PLL_BYPASS] = imx_clk_hw_mux_flags("gpu_pll_bypass", anatop_base + 0x64, 28, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_VPU_PLL_BYPASS] = imx_clk_hw_mux_flags("vpu_pll_bypass", anatop_base + 0x74, 28, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_ARM_PLL_BYPASS] = imx_clk_hw_mux_flags("arm_pll_bypass", anatop_base + 0x84, 28, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_SYS_PLL1_BYPASS] = imx_clk_hw_mux_flags("sys_pll1_bypass", anatop_base + 0x94, 28, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_SYS_PLL2_BYPASS] = imx_clk_hw_mux_flags("sys_pll2_bypass", anatop_base + 0x104, 28, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
hws[IMX8MP_SYS_PLL3_BYPASS] = imx_clk_hw_mux_flags("sys_pll3_bypass", anatop_base + 0x114, 28, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT);
|
||||
|
||||
hws[IMX8MP_AUDIO_PLL1_OUT] = imx_clk_hw_gate("audio_pll1_out", "audio_pll1_bypass", anatop_base, 13);
|
||||
hws[IMX8MP_AUDIO_PLL2_OUT] = imx_clk_hw_gate("audio_pll2_out", "audio_pll2_bypass", anatop_base + 0x14, 13);
|
||||
@ -504,79 +504,82 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
|
||||
hws[IMX8MP_GPU_PLL_OUT] = imx_clk_hw_gate("gpu_pll_out", "gpu_pll_bypass", anatop_base + 0x64, 11);
|
||||
hws[IMX8MP_VPU_PLL_OUT] = imx_clk_hw_gate("vpu_pll_out", "vpu_pll_bypass", anatop_base + 0x74, 11);
|
||||
hws[IMX8MP_ARM_PLL_OUT] = imx_clk_hw_gate("arm_pll_out", "arm_pll_bypass", anatop_base + 0x84, 11);
|
||||
hws[IMX8MP_SYS_PLL1_OUT] = imx_clk_hw_gate("sys_pll1_out", "sys_pll1_bypass", anatop_base + 0x94, 11);
|
||||
hws[IMX8MP_SYS_PLL2_OUT] = imx_clk_hw_gate("sys_pll2_out", "sys_pll2_bypass", anatop_base + 0x104, 11);
|
||||
hws[IMX8MP_SYS_PLL3_OUT] = imx_clk_hw_gate("sys_pll3_out", "sys_pll3_bypass", anatop_base + 0x114, 11);
|
||||
|
||||
hws[IMX8MP_SYS_PLL1_40M] = imx_clk_hw_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20);
|
||||
hws[IMX8MP_SYS_PLL1_80M] = imx_clk_hw_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10);
|
||||
hws[IMX8MP_SYS_PLL1_100M] = imx_clk_hw_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8);
|
||||
hws[IMX8MP_SYS_PLL1_133M] = imx_clk_hw_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6);
|
||||
hws[IMX8MP_SYS_PLL1_160M] = imx_clk_hw_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5);
|
||||
hws[IMX8MP_SYS_PLL1_200M] = imx_clk_hw_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4);
|
||||
hws[IMX8MP_SYS_PLL1_266M] = imx_clk_hw_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3);
|
||||
hws[IMX8MP_SYS_PLL1_400M] = imx_clk_hw_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2);
|
||||
hws[IMX8MP_SYS_PLL1_40M_CG] = imx_clk_hw_gate("sys_pll1_40m_cg", "sys_pll1_bypass", anatop_base + 0x94, 27);
|
||||
hws[IMX8MP_SYS_PLL1_80M_CG] = imx_clk_hw_gate("sys_pll1_80m_cg", "sys_pll1_bypass", anatop_base + 0x94, 25);
|
||||
hws[IMX8MP_SYS_PLL1_100M_CG] = imx_clk_hw_gate("sys_pll1_100m_cg", "sys_pll1_bypass", anatop_base + 0x94, 23);
|
||||
hws[IMX8MP_SYS_PLL1_133M_CG] = imx_clk_hw_gate("sys_pll1_133m_cg", "sys_pll1_bypass", anatop_base + 0x94, 21);
|
||||
hws[IMX8MP_SYS_PLL1_160M_CG] = imx_clk_hw_gate("sys_pll1_160m_cg", "sys_pll1_bypass", anatop_base + 0x94, 19);
|
||||
hws[IMX8MP_SYS_PLL1_200M_CG] = imx_clk_hw_gate("sys_pll1_200m_cg", "sys_pll1_bypass", anatop_base + 0x94, 17);
|
||||
hws[IMX8MP_SYS_PLL1_266M_CG] = imx_clk_hw_gate("sys_pll1_266m_cg", "sys_pll1_bypass", anatop_base + 0x94, 15);
|
||||
hws[IMX8MP_SYS_PLL1_400M_CG] = imx_clk_hw_gate("sys_pll1_400m_cg", "sys_pll1_bypass", anatop_base + 0x94, 13);
|
||||
hws[IMX8MP_SYS_PLL1_OUT] = imx_clk_hw_gate("sys_pll1_out", "sys_pll1_bypass", anatop_base + 0x94, 11);
|
||||
|
||||
hws[IMX8MP_SYS_PLL1_40M] = imx_clk_hw_fixed_factor("sys_pll1_40m", "sys_pll1_40m_cg", 1, 20);
|
||||
hws[IMX8MP_SYS_PLL1_80M] = imx_clk_hw_fixed_factor("sys_pll1_80m", "sys_pll1_80m_cg", 1, 10);
|
||||
hws[IMX8MP_SYS_PLL1_100M] = imx_clk_hw_fixed_factor("sys_pll1_100m", "sys_pll1_100m_cg", 1, 8);
|
||||
hws[IMX8MP_SYS_PLL1_133M] = imx_clk_hw_fixed_factor("sys_pll1_133m", "sys_pll1_133m_cg", 1, 6);
|
||||
hws[IMX8MP_SYS_PLL1_160M] = imx_clk_hw_fixed_factor("sys_pll1_160m", "sys_pll1_160m_cg", 1, 5);
|
||||
hws[IMX8MP_SYS_PLL1_200M] = imx_clk_hw_fixed_factor("sys_pll1_200m", "sys_pll1_200m_cg", 1, 4);
|
||||
hws[IMX8MP_SYS_PLL1_266M] = imx_clk_hw_fixed_factor("sys_pll1_266m", "sys_pll1_266m_cg", 1, 3);
|
||||
hws[IMX8MP_SYS_PLL1_400M] = imx_clk_hw_fixed_factor("sys_pll1_400m", "sys_pll1_400m_cg", 1, 2);
|
||||
hws[IMX8MP_SYS_PLL1_800M] = imx_clk_hw_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1);
|
||||
|
||||
hws[IMX8MP_SYS_PLL2_50M] = imx_clk_hw_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20);
|
||||
hws[IMX8MP_SYS_PLL2_100M] = imx_clk_hw_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10);
|
||||
hws[IMX8MP_SYS_PLL2_125M] = imx_clk_hw_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8);
|
||||
hws[IMX8MP_SYS_PLL2_166M] = imx_clk_hw_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6);
|
||||
hws[IMX8MP_SYS_PLL2_200M] = imx_clk_hw_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5);
|
||||
hws[IMX8MP_SYS_PLL2_250M] = imx_clk_hw_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4);
|
||||
hws[IMX8MP_SYS_PLL2_333M] = imx_clk_hw_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3);
|
||||
hws[IMX8MP_SYS_PLL2_500M] = imx_clk_hw_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2);
|
||||
hws[IMX8MP_SYS_PLL2_50M_CG] = imx_clk_hw_gate("sys_pll2_50m_cg", "sys_pll2_bypass", anatop_base + 0x104, 27);
|
||||
hws[IMX8MP_SYS_PLL2_100M_CG] = imx_clk_hw_gate("sys_pll2_100m_cg", "sys_pll2_bypass", anatop_base + 0x104, 25);
|
||||
hws[IMX8MP_SYS_PLL2_125M_CG] = imx_clk_hw_gate("sys_pll2_125m_cg", "sys_pll2_bypass", anatop_base + 0x104, 23);
|
||||
hws[IMX8MP_SYS_PLL2_166M_CG] = imx_clk_hw_gate("sys_pll2_166m_cg", "sys_pll2_bypass", anatop_base + 0x104, 21);
|
||||
hws[IMX8MP_SYS_PLL2_200M_CG] = imx_clk_hw_gate("sys_pll2_200m_cg", "sys_pll2_bypass", anatop_base + 0x104, 19);
|
||||
hws[IMX8MP_SYS_PLL2_250M_CG] = imx_clk_hw_gate("sys_pll2_250m_cg", "sys_pll2_bypass", anatop_base + 0x104, 17);
|
||||
hws[IMX8MP_SYS_PLL2_333M_CG] = imx_clk_hw_gate("sys_pll2_333m_cg", "sys_pll2_bypass", anatop_base + 0x104, 15);
|
||||
hws[IMX8MP_SYS_PLL2_500M_CG] = imx_clk_hw_gate("sys_pll2_500m_cg", "sys_pll2_bypass", anatop_base + 0x104, 13);
|
||||
hws[IMX8MP_SYS_PLL2_OUT] = imx_clk_hw_gate("sys_pll2_out", "sys_pll2_bypass", anatop_base + 0x104, 11);
|
||||
|
||||
hws[IMX8MP_SYS_PLL2_50M] = imx_clk_hw_fixed_factor("sys_pll2_50m", "sys_pll2_50m_cg", 1, 20);
|
||||
hws[IMX8MP_SYS_PLL2_100M] = imx_clk_hw_fixed_factor("sys_pll2_100m", "sys_pll2_100m_cg", 1, 10);
|
||||
hws[IMX8MP_SYS_PLL2_125M] = imx_clk_hw_fixed_factor("sys_pll2_125m", "sys_pll2_125m_cg", 1, 8);
|
||||
hws[IMX8MP_SYS_PLL2_166M] = imx_clk_hw_fixed_factor("sys_pll2_166m", "sys_pll2_166m_cg", 1, 6);
|
||||
hws[IMX8MP_SYS_PLL2_200M] = imx_clk_hw_fixed_factor("sys_pll2_200m", "sys_pll2_200m_cg", 1, 5);
|
||||
hws[IMX8MP_SYS_PLL2_250M] = imx_clk_hw_fixed_factor("sys_pll2_250m", "sys_pll2_250m_cg", 1, 4);
|
||||
hws[IMX8MP_SYS_PLL2_333M] = imx_clk_hw_fixed_factor("sys_pll2_333m", "sys_pll2_333m_cg", 1, 3);
|
||||
hws[IMX8MP_SYS_PLL2_500M] = imx_clk_hw_fixed_factor("sys_pll2_500m", "sys_pll2_500m_cg", 1, 2);
|
||||
hws[IMX8MP_SYS_PLL2_1000M] = imx_clk_hw_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1);
|
||||
|
||||
hws[IMX8MP_CLK_A53_SRC] = imx_clk_hw_mux2("arm_a53_src", ccm_base + 0x8000, 24, 3, imx8mp_a53_sels, ARRAY_SIZE(imx8mp_a53_sels));
|
||||
hws[IMX8MP_CLK_M7_SRC] = imx_clk_hw_mux2("arm_m7_src", ccm_base + 0x8080, 24, 3, imx8mp_m7_sels, ARRAY_SIZE(imx8mp_m7_sels));
|
||||
hws[IMX8MP_CLK_ML_SRC] = imx_clk_hw_mux2("ml_src", ccm_base + 0x8100, 24, 3, imx8mp_ml_sels, ARRAY_SIZE(imx8mp_ml_sels));
|
||||
hws[IMX8MP_CLK_GPU3D_CORE_SRC] = imx_clk_hw_mux2("gpu3d_core_src", ccm_base + 0x8180, 24, 3, imx8mp_gpu3d_core_sels, ARRAY_SIZE(imx8mp_gpu3d_core_sels));
|
||||
hws[IMX8MP_CLK_GPU3D_SHADER_SRC] = imx_clk_hw_mux2("gpu3d_shader_src", ccm_base + 0x8200, 24, 3, imx8mp_gpu3d_shader_sels, ARRAY_SIZE(imx8mp_gpu3d_shader_sels));
|
||||
hws[IMX8MP_CLK_GPU2D_SRC] = imx_clk_hw_mux2("gpu2d_src", ccm_base + 0x8280, 24, 3, imx8mp_gpu2d_sels, ARRAY_SIZE(imx8mp_gpu2d_sels));
|
||||
hws[IMX8MP_CLK_AUDIO_AXI_SRC] = imx_clk_hw_mux2("audio_axi_src", ccm_base + 0x8300, 24, 3, imx8mp_audio_axi_sels, ARRAY_SIZE(imx8mp_audio_axi_sels));
|
||||
hws[IMX8MP_CLK_HSIO_AXI_SRC] = imx_clk_hw_mux2("hsio_axi_src", ccm_base + 0x8380, 24, 3, imx8mp_hsio_axi_sels, ARRAY_SIZE(imx8mp_hsio_axi_sels));
|
||||
hws[IMX8MP_CLK_MEDIA_ISP_SRC] = imx_clk_hw_mux2("media_isp_src", ccm_base + 0x8400, 24, 3, imx8mp_media_isp_sels, ARRAY_SIZE(imx8mp_media_isp_sels));
|
||||
hws[IMX8MP_CLK_A53_CG] = imx_clk_hw_gate3("arm_a53_cg", "arm_a53_src", ccm_base + 0x8000, 28);
|
||||
hws[IMX8MP_CLK_M4_CG] = imx_clk_hw_gate3("arm_m7_cg", "arm_m7_src", ccm_base + 0x8080, 28);
|
||||
hws[IMX8MP_CLK_ML_CG] = imx_clk_hw_gate3("ml_cg", "ml_src", ccm_base + 0x8100, 28);
|
||||
hws[IMX8MP_CLK_GPU3D_CORE_CG] = imx_clk_hw_gate3("gpu3d_core_cg", "gpu3d_core_src", ccm_base + 0x8180, 28);
|
||||
hws[IMX8MP_CLK_GPU3D_SHADER_CG] = imx_clk_hw_gate3("gpu3d_shader_cg", "gpu3d_shader_src", ccm_base + 0x8200, 28);
|
||||
hws[IMX8MP_CLK_GPU2D_CG] = imx_clk_hw_gate3("gpu2d_cg", "gpu2d_src", ccm_base + 0x8280, 28);
|
||||
hws[IMX8MP_CLK_AUDIO_AXI_CG] = imx_clk_hw_gate3("audio_axi_cg", "audio_axi_src", ccm_base + 0x8300, 28);
|
||||
hws[IMX8MP_CLK_HSIO_AXI_CG] = imx_clk_hw_gate3("hsio_axi_cg", "hsio_axi_src", ccm_base + 0x8380, 28);
|
||||
hws[IMX8MP_CLK_MEDIA_ISP_CG] = imx_clk_hw_gate3("media_isp_cg", "media_isp_src", ccm_base + 0x8400, 28);
|
||||
hws[IMX8MP_CLK_A53_DIV] = imx_clk_hw_divider2("arm_a53_div", "arm_a53_cg", ccm_base + 0x8000, 0, 3);
|
||||
hws[IMX8MP_CLK_M7_DIV] = imx_clk_hw_divider2("arm_m7_div", "arm_m7_cg", ccm_base + 0x8080, 0, 3);
|
||||
hws[IMX8MP_CLK_ML_DIV] = imx_clk_hw_divider2("ml_div", "ml_cg", ccm_base + 0x8100, 0, 3);
|
||||
hws[IMX8MP_CLK_GPU3D_CORE_DIV] = imx_clk_hw_divider2("gpu3d_core_div", "gpu3d_core_cg", ccm_base + 0x8180, 0, 3);
|
||||
hws[IMX8MP_CLK_GPU3D_SHADER_DIV] = imx_clk_hw_divider2("gpu3d_shader_div", "gpu3d_shader_cg", ccm_base + 0x8200, 0, 3);
|
||||
hws[IMX8MP_CLK_GPU2D_DIV] = imx_clk_hw_divider2("gpu2d_div", "gpu2d_cg", ccm_base + 0x8280, 0, 3);
|
||||
hws[IMX8MP_CLK_AUDIO_AXI_DIV] = imx_clk_hw_divider2("audio_axi_div", "audio_axi_cg", ccm_base + 0x8300, 0, 3);
|
||||
hws[IMX8MP_CLK_HSIO_AXI_DIV] = imx_clk_hw_divider2("hsio_axi_div", "hsio_axi_cg", ccm_base + 0x8380, 0, 3);
|
||||
hws[IMX8MP_CLK_MEDIA_ISP_DIV] = imx_clk_hw_divider2("media_isp_div", "media_isp_cg", ccm_base + 0x8400, 0, 3);
|
||||
hws[IMX8MP_CLK_A53_DIV] = imx8m_clk_hw_composite_core("arm_a53_div", imx8mp_a53_sels, ccm_base + 0x8000);
|
||||
hws[IMX8MP_CLK_A53_SRC] = hws[IMX8MP_CLK_A53_DIV];
|
||||
hws[IMX8MP_CLK_A53_CG] = hws[IMX8MP_CLK_A53_DIV];
|
||||
hws[IMX8MP_CLK_M7_CORE] = imx8m_clk_hw_composite_core("m7_core", imx8mp_m7_sels, ccm_base + 0x8080);
|
||||
hws[IMX8MP_CLK_ML_CORE] = imx8m_clk_hw_composite_core("ml_core", imx8mp_ml_sels, ccm_base + 0x8100);
|
||||
hws[IMX8MP_CLK_GPU3D_CORE] = imx8m_clk_hw_composite_core("gpu3d_core", imx8mp_gpu3d_core_sels, ccm_base + 0x8180);
|
||||
hws[IMX8MP_CLK_GPU3D_SHADER_CORE] = imx8m_clk_hw_composite("gpu3d_shader_core", imx8mp_gpu3d_shader_sels, ccm_base + 0x8200);
|
||||
hws[IMX8MP_CLK_GPU2D_CORE] = imx8m_clk_hw_composite("gpu2d_core", imx8mp_gpu2d_sels, ccm_base + 0x8280);
|
||||
hws[IMX8MP_CLK_AUDIO_AXI] = imx8m_clk_hw_composite("audio_axi", imx8mp_audio_axi_sels, ccm_base + 0x8300);
|
||||
hws[IMX8MP_CLK_AUDIO_AXI_SRC] = hws[IMX8MP_CLK_AUDIO_AXI];
|
||||
hws[IMX8MP_CLK_HSIO_AXI] = imx8m_clk_hw_composite("hsio_axi", imx8mp_hsio_axi_sels, ccm_base + 0x8380);
|
||||
hws[IMX8MP_CLK_MEDIA_ISP] = imx8m_clk_hw_composite("media_isp", imx8mp_media_isp_sels, ccm_base + 0x8400);
|
||||
|
||||
/* CORE SEL */
|
||||
hws[IMX8MP_CLK_A53_CORE] = imx_clk_hw_mux2("arm_a53_core", ccm_base + 0x9880, 24, 1, imx8mp_a53_core_sels, ARRAY_SIZE(imx8mp_a53_core_sels));
|
||||
|
||||
hws[IMX8MP_CLK_MAIN_AXI] = imx8m_clk_hw_composite_critical("main_axi", imx8mp_main_axi_sels, ccm_base + 0x8800);
|
||||
hws[IMX8MP_CLK_ENET_AXI] = imx8m_clk_hw_composite("enet_axi", imx8mp_enet_axi_sels, ccm_base + 0x8880);
|
||||
hws[IMX8MP_CLK_ENET_AXI] = imx8m_clk_hw_composite_bus("enet_axi", imx8mp_enet_axi_sels, ccm_base + 0x8880);
|
||||
hws[IMX8MP_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_critical("nand_usdhc_bus", imx8mp_nand_usdhc_sels, ccm_base + 0x8900);
|
||||
hws[IMX8MP_CLK_VPU_BUS] = imx8m_clk_hw_composite("vpu_bus", imx8mp_vpu_bus_sels, ccm_base + 0x8980);
|
||||
hws[IMX8MP_CLK_MEDIA_AXI] = imx8m_clk_hw_composite("media_axi", imx8mp_media_axi_sels, ccm_base + 0x8a00);
|
||||
hws[IMX8MP_CLK_MEDIA_APB] = imx8m_clk_hw_composite("media_apb", imx8mp_media_apb_sels, ccm_base + 0x8a80);
|
||||
hws[IMX8MP_CLK_HDMI_APB] = imx8m_clk_hw_composite("hdmi_apb", imx8mp_media_apb_sels, ccm_base + 0x8b00);
|
||||
hws[IMX8MP_CLK_HDMI_AXI] = imx8m_clk_hw_composite("hdmi_axi", imx8mp_media_axi_sels, ccm_base + 0x8b80);
|
||||
hws[IMX8MP_CLK_GPU_AXI] = imx8m_clk_hw_composite("gpu_axi", imx8mp_gpu_axi_sels, ccm_base + 0x8c00);
|
||||
hws[IMX8MP_CLK_GPU_AHB] = imx8m_clk_hw_composite("gpu_ahb", imx8mp_gpu_ahb_sels, ccm_base + 0x8c80);
|
||||
hws[IMX8MP_CLK_VPU_BUS] = imx8m_clk_hw_composite_bus("vpu_bus", imx8mp_vpu_bus_sels, ccm_base + 0x8980);
|
||||
hws[IMX8MP_CLK_MEDIA_AXI] = imx8m_clk_hw_composite_bus("media_axi", imx8mp_media_axi_sels, ccm_base + 0x8a00);
|
||||
hws[IMX8MP_CLK_MEDIA_APB] = imx8m_clk_hw_composite_bus("media_apb", imx8mp_media_apb_sels, ccm_base + 0x8a80);
|
||||
hws[IMX8MP_CLK_HDMI_APB] = imx8m_clk_hw_composite_bus("hdmi_apb", imx8mp_media_apb_sels, ccm_base + 0x8b00);
|
||||
hws[IMX8MP_CLK_HDMI_AXI] = imx8m_clk_hw_composite_bus("hdmi_axi", imx8mp_media_axi_sels, ccm_base + 0x8b80);
|
||||
hws[IMX8MP_CLK_GPU_AXI] = imx8m_clk_hw_composite_bus("gpu_axi", imx8mp_gpu_axi_sels, ccm_base + 0x8c00);
|
||||
hws[IMX8MP_CLK_GPU_AHB] = imx8m_clk_hw_composite_bus("gpu_ahb", imx8mp_gpu_ahb_sels, ccm_base + 0x8c80);
|
||||
hws[IMX8MP_CLK_NOC] = imx8m_clk_hw_composite_critical("noc", imx8mp_noc_sels, ccm_base + 0x8d00);
|
||||
hws[IMX8MP_CLK_NOC_IO] = imx8m_clk_hw_composite_critical("noc_io", imx8mp_noc_io_sels, ccm_base + 0x8d80);
|
||||
hws[IMX8MP_CLK_ML_AXI] = imx8m_clk_hw_composite("ml_axi", imx8mp_ml_axi_sels, ccm_base + 0x8e00);
|
||||
hws[IMX8MP_CLK_ML_AHB] = imx8m_clk_hw_composite("ml_ahb", imx8mp_ml_ahb_sels, ccm_base + 0x8e80);
|
||||
hws[IMX8MP_CLK_ML_AXI] = imx8m_clk_hw_composite_bus("ml_axi", imx8mp_ml_axi_sels, ccm_base + 0x8e00);
|
||||
hws[IMX8MP_CLK_ML_AHB] = imx8m_clk_hw_composite_bus("ml_ahb", imx8mp_ml_ahb_sels, ccm_base + 0x8e80);
|
||||
|
||||
hws[IMX8MP_CLK_AHB] = imx8m_clk_hw_composite_critical("ahb_root", imx8mp_ahb_sels, ccm_base + 0x9000);
|
||||
hws[IMX8MP_CLK_AUDIO_AHB] = imx8m_clk_hw_composite("audio_ahb", imx8mp_audio_ahb_sels, ccm_base + 0x9100);
|
||||
hws[IMX8MP_CLK_MIPI_DSI_ESC_RX] = imx8m_clk_hw_composite("mipi_dsi_esc_rx", imx8mp_mipi_dsi_esc_rx_sels, ccm_base + 0x9200);
|
||||
hws[IMX8MP_CLK_AUDIO_AHB] = imx8m_clk_hw_composite_bus("audio_ahb", imx8mp_audio_ahb_sels, ccm_base + 0x9100);
|
||||
hws[IMX8MP_CLK_MIPI_DSI_ESC_RX] = imx8m_clk_hw_composite_bus("mipi_dsi_esc_rx", imx8mp_mipi_dsi_esc_rx_sels, ccm_base + 0x9200);
|
||||
|
||||
hws[IMX8MP_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb_root", ccm_base + 0x9080, 0, 1);
|
||||
hws[IMX8MP_CLK_IPG_AUDIO_ROOT] = imx_clk_hw_divider2("ipg_audio_root", "audio_ahb", ccm_base + 0x9180, 0, 1);
|
||||
@ -695,8 +698,8 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
|
||||
hws[IMX8MP_CLK_SDMA1_ROOT] = imx_clk_hw_gate4("sdma1_root_clk", "ipg_root", ccm_base + 0x43a0, 0);
|
||||
hws[IMX8MP_CLK_ENET_QOS_ROOT] = imx_clk_hw_gate4("enet_qos_root_clk", "sim_enet_root_clk", ccm_base + 0x43b0, 0);
|
||||
hws[IMX8MP_CLK_SIM_ENET_ROOT] = imx_clk_hw_gate4("sim_enet_root_clk", "enet_axi", ccm_base + 0x4400, 0);
|
||||
hws[IMX8MP_CLK_GPU2D_ROOT] = imx_clk_hw_gate4("gpu2d_root_clk", "gpu2d_div", ccm_base + 0x4450, 0);
|
||||
hws[IMX8MP_CLK_GPU3D_ROOT] = imx_clk_hw_gate4("gpu3d_root_clk", "gpu3d_core_div", ccm_base + 0x4460, 0);
|
||||
hws[IMX8MP_CLK_GPU2D_ROOT] = imx_clk_hw_gate4("gpu2d_root_clk", "gpu2d_core", ccm_base + 0x4450, 0);
|
||||
hws[IMX8MP_CLK_GPU3D_ROOT] = imx_clk_hw_gate4("gpu3d_root_clk", "gpu3d_core", ccm_base + 0x4460, 0);
|
||||
hws[IMX8MP_CLK_SNVS_ROOT] = imx_clk_hw_gate4("snvs_root_clk", "ipg_root", ccm_base + 0x4470, 0);
|
||||
hws[IMX8MP_CLK_UART1_ROOT] = imx_clk_hw_gate4("uart1_root_clk", "uart1", ccm_base + 0x4490, 0);
|
||||
hws[IMX8MP_CLK_UART2_ROOT] = imx_clk_hw_gate4("uart2_root_clk", "uart2", ccm_base + 0x44a0, 0);
|
||||
@ -713,7 +716,7 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
|
||||
hws[IMX8MP_CLK_GPU_ROOT] = imx_clk_hw_gate4("gpu_root_clk", "gpu_axi", ccm_base + 0x4570, 0);
|
||||
hws[IMX8MP_CLK_VPU_VC8KE_ROOT] = imx_clk_hw_gate4("vpu_vc8ke_root_clk", "vpu_vc8000e", ccm_base + 0x4590, 0);
|
||||
hws[IMX8MP_CLK_VPU_G2_ROOT] = imx_clk_hw_gate4("vpu_g2_root_clk", "vpu_g2", ccm_base + 0x45a0, 0);
|
||||
hws[IMX8MP_CLK_NPU_ROOT] = imx_clk_hw_gate4("npu_root_clk", "ml_div", ccm_base + 0x45b0, 0);
|
||||
hws[IMX8MP_CLK_NPU_ROOT] = imx_clk_hw_gate4("npu_root_clk", "ml_core", ccm_base + 0x45b0, 0);
|
||||
hws[IMX8MP_CLK_HSIO_ROOT] = imx_clk_hw_gate4("hsio_root_clk", "ipg_root", ccm_base + 0x45c0, 0);
|
||||
hws[IMX8MP_CLK_MEDIA_APB_ROOT] = imx_clk_hw_gate2_shared2("media_apb_root_clk", "media_apb", ccm_base + 0x45d0, 0, &share_count_media);
|
||||
hws[IMX8MP_CLK_MEDIA_AXI_ROOT] = imx_clk_hw_gate2_shared2("media_axi_root_clk", "media_axi", ccm_base + 0x45d0, 0, &share_count_media);
|
||||
@ -721,7 +724,7 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
|
||||
hws[IMX8MP_CLK_MEDIA_CAM2_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_cam2_pix_root_clk", "media_cam2_pix", ccm_base + 0x45d0, 0, &share_count_media);
|
||||
hws[IMX8MP_CLK_MEDIA_DISP1_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_disp1_pix_root_clk", "media_disp1_pix", ccm_base + 0x45d0, 0, &share_count_media);
|
||||
hws[IMX8MP_CLK_MEDIA_DISP2_PIX_ROOT] = imx_clk_hw_gate2_shared2("media_disp2_pix_root_clk", "media_disp2_pix", ccm_base + 0x45d0, 0, &share_count_media);
|
||||
hws[IMX8MP_CLK_MEDIA_ISP_ROOT] = imx_clk_hw_gate2_shared2("media_isp_root_clk", "media_isp_div", ccm_base + 0x45d0, 0, &share_count_media);
|
||||
hws[IMX8MP_CLK_MEDIA_ISP_ROOT] = imx_clk_hw_gate2_shared2("media_isp_root_clk", "media_isp", ccm_base + 0x45d0, 0, &share_count_media);
|
||||
|
||||
hws[IMX8MP_CLK_USDHC3_ROOT] = imx_clk_hw_gate4("usdhc3_root_clk", "usdhc3", ccm_base + 0x45e0, 0);
|
||||
hws[IMX8MP_CLK_HDMI_ROOT] = imx_clk_hw_gate4("hdmi_root_clk", "hdmi_axi", ccm_base + 0x45f0, 0);
|
||||
@ -735,9 +738,6 @@ static int imx8mp_clocks_probe(struct platform_device *pdev)
|
||||
hws[IMX8MP_ARM_PLL_OUT]->clk,
|
||||
hws[IMX8MP_CLK_A53_DIV]->clk);
|
||||
|
||||
clk_hw_set_parent(hws[IMX8MP_CLK_A53_SRC], hws[IMX8MP_SYS_PLL1_800M]);
|
||||
clk_hw_set_parent(hws[IMX8MP_CLK_A53_CORE], hws[IMX8MP_ARM_PLL_OUT]);
|
||||
|
||||
imx_check_clk_hws(hws, IMX8MP_CLK_END);
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
|
||||
|
@ -405,9 +405,9 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(base);
|
||||
|
||||
/* CORE */
|
||||
hws[IMX8MQ_CLK_A53_SRC] = imx_clk_hw_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mq_a53_sels, ARRAY_SIZE(imx8mq_a53_sels));
|
||||
hws[IMX8MQ_CLK_A53_CG] = imx_clk_hw_gate3_flags("arm_a53_cg", "arm_a53_src", base + 0x8000, 28, CLK_IS_CRITICAL);
|
||||
hws[IMX8MQ_CLK_A53_DIV] = imx_clk_hw_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3);
|
||||
hws[IMX8MQ_CLK_A53_DIV] = imx8m_clk_hw_composite_core("arm_a53_div", imx8mq_a53_sels, base + 0x8000);
|
||||
hws[IMX8MQ_CLK_A53_CG] = hws[IMX8MQ_CLK_A53_DIV];
|
||||
hws[IMX8MQ_CLK_A53_SRC] = hws[IMX8MQ_CLK_A53_DIV];
|
||||
|
||||
hws[IMX8MQ_CLK_M4_CORE] = imx8m_clk_hw_composite_core("arm_m4_core", imx8mq_arm_m4_sels, base + 0x8080);
|
||||
hws[IMX8MQ_CLK_VPU_CORE] = imx8m_clk_hw_composite_core("vpu_core", imx8mq_vpu_sels, base + 0x8100);
|
||||
@ -432,22 +432,22 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
|
||||
|
||||
/* BUS */
|
||||
hws[IMX8MQ_CLK_MAIN_AXI] = imx8m_clk_hw_composite_critical("main_axi", imx8mq_main_axi_sels, base + 0x8800);
|
||||
hws[IMX8MQ_CLK_ENET_AXI] = imx8m_clk_hw_composite("enet_axi", imx8mq_enet_axi_sels, base + 0x8880);
|
||||
hws[IMX8MQ_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite("nand_usdhc_bus", imx8mq_nand_usdhc_sels, base + 0x8900);
|
||||
hws[IMX8MQ_CLK_VPU_BUS] = imx8m_clk_hw_composite("vpu_bus", imx8mq_vpu_bus_sels, base + 0x8980);
|
||||
hws[IMX8MQ_CLK_DISP_AXI] = imx8m_clk_hw_composite("disp_axi", imx8mq_disp_axi_sels, base + 0x8a00);
|
||||
hws[IMX8MQ_CLK_DISP_APB] = imx8m_clk_hw_composite("disp_apb", imx8mq_disp_apb_sels, base + 0x8a80);
|
||||
hws[IMX8MQ_CLK_DISP_RTRM] = imx8m_clk_hw_composite("disp_rtrm", imx8mq_disp_rtrm_sels, base + 0x8b00);
|
||||
hws[IMX8MQ_CLK_USB_BUS] = imx8m_clk_hw_composite("usb_bus", imx8mq_usb_bus_sels, base + 0x8b80);
|
||||
hws[IMX8MQ_CLK_GPU_AXI] = imx8m_clk_hw_composite("gpu_axi", imx8mq_gpu_axi_sels, base + 0x8c00);
|
||||
hws[IMX8MQ_CLK_GPU_AHB] = imx8m_clk_hw_composite("gpu_ahb", imx8mq_gpu_ahb_sels, base + 0x8c80);
|
||||
hws[IMX8MQ_CLK_ENET_AXI] = imx8m_clk_hw_composite_bus("enet_axi", imx8mq_enet_axi_sels, base + 0x8880);
|
||||
hws[IMX8MQ_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_bus("nand_usdhc_bus", imx8mq_nand_usdhc_sels, base + 0x8900);
|
||||
hws[IMX8MQ_CLK_VPU_BUS] = imx8m_clk_hw_composite_bus("vpu_bus", imx8mq_vpu_bus_sels, base + 0x8980);
|
||||
hws[IMX8MQ_CLK_DISP_AXI] = imx8m_clk_hw_composite_bus("disp_axi", imx8mq_disp_axi_sels, base + 0x8a00);
|
||||
hws[IMX8MQ_CLK_DISP_APB] = imx8m_clk_hw_composite_bus("disp_apb", imx8mq_disp_apb_sels, base + 0x8a80);
|
||||
hws[IMX8MQ_CLK_DISP_RTRM] = imx8m_clk_hw_composite_bus("disp_rtrm", imx8mq_disp_rtrm_sels, base + 0x8b00);
|
||||
hws[IMX8MQ_CLK_USB_BUS] = imx8m_clk_hw_composite_bus("usb_bus", imx8mq_usb_bus_sels, base + 0x8b80);
|
||||
hws[IMX8MQ_CLK_GPU_AXI] = imx8m_clk_hw_composite_bus("gpu_axi", imx8mq_gpu_axi_sels, base + 0x8c00);
|
||||
hws[IMX8MQ_CLK_GPU_AHB] = imx8m_clk_hw_composite_bus("gpu_ahb", imx8mq_gpu_ahb_sels, base + 0x8c80);
|
||||
hws[IMX8MQ_CLK_NOC] = imx8m_clk_hw_composite_critical("noc", imx8mq_noc_sels, base + 0x8d00);
|
||||
hws[IMX8MQ_CLK_NOC_APB] = imx8m_clk_hw_composite_critical("noc_apb", imx8mq_noc_apb_sels, base + 0x8d80);
|
||||
|
||||
/* AHB */
|
||||
/* AHB clock is used by the AHB bus therefore marked as critical */
|
||||
hws[IMX8MQ_CLK_AHB] = imx8m_clk_hw_composite_critical("ahb", imx8mq_ahb_sels, base + 0x9000);
|
||||
hws[IMX8MQ_CLK_AUDIO_AHB] = imx8m_clk_hw_composite("audio_ahb", imx8mq_audio_ahb_sels, base + 0x9100);
|
||||
hws[IMX8MQ_CLK_AUDIO_AHB] = imx8m_clk_hw_composite_bus("audio_ahb", imx8mq_audio_ahb_sels, base + 0x9100);
|
||||
|
||||
/* IPG */
|
||||
hws[IMX8MQ_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb", base + 0x9080, 0, 1);
|
||||
@ -599,9 +599,6 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
|
||||
hws[IMX8MQ_ARM_PLL_OUT]->clk,
|
||||
hws[IMX8MQ_CLK_A53_DIV]->clk);
|
||||
|
||||
clk_hw_set_parent(hws[IMX8MQ_CLK_A53_SRC], hws[IMX8MQ_SYS1_PLL_800M]);
|
||||
clk_hw_set_parent(hws[IMX8MQ_CLK_A53_CORE], hws[IMX8MQ_ARM_PLL_OUT]);
|
||||
|
||||
imx_check_clk_hws(hws, IMX8MQ_CLK_END);
|
||||
|
||||
err = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_hw_data);
|
||||
|
@ -378,9 +378,9 @@ static const struct clk_ops clk_pll1443x_ops = {
|
||||
.set_rate = clk_pll1443x_set_rate,
|
||||
};
|
||||
|
||||
struct clk_hw *imx_clk_hw_pll14xx(const char *name, const char *parent_name,
|
||||
void __iomem *base,
|
||||
const struct imx_pll14xx_clk *pll_clk)
|
||||
struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name,
|
||||
const char *parent_name, void __iomem *base,
|
||||
const struct imx_pll14xx_clk *pll_clk)
|
||||
{
|
||||
struct clk_pll14xx *pll;
|
||||
struct clk_hw *hw;
|
||||
@ -426,7 +426,7 @@ struct clk_hw *imx_clk_hw_pll14xx(const char *name, const char *parent_name,
|
||||
|
||||
hw = &pll->hw;
|
||||
|
||||
ret = clk_hw_register(NULL, hw);
|
||||
ret = clk_hw_register(dev, hw);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to register pll %s %d\n",
|
||||
__func__, name, ret);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/err.h>
|
||||
@ -25,6 +26,8 @@
|
||||
#define IMX7_ENET_PLL_POWER (0x1 << 5)
|
||||
#define IMX7_DDR_PLL_POWER (0x1 << 20)
|
||||
|
||||
#define PLL_LOCK_TIMEOUT 10000
|
||||
|
||||
/**
|
||||
* struct clk_pllv3 - IMX PLL clock version 3
|
||||
* @clk_hw: clock source
|
||||
@ -53,23 +56,14 @@ struct clk_pllv3 {
|
||||
|
||||
static int clk_pllv3_wait_lock(struct clk_pllv3 *pll)
|
||||
{
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(10);
|
||||
u32 val = readl_relaxed(pll->base) & pll->power_bit;
|
||||
|
||||
/* No need to wait for lock when pll is not powered up */
|
||||
if ((pll->powerup_set && !val) || (!pll->powerup_set && val))
|
||||
return 0;
|
||||
|
||||
/* Wait for PLL to lock */
|
||||
do {
|
||||
if (readl_relaxed(pll->base) & BM_PLL_LOCK)
|
||||
break;
|
||||
if (time_after(jiffies, timeout))
|
||||
break;
|
||||
usleep_range(50, 500);
|
||||
} while (1);
|
||||
|
||||
return readl_relaxed(pll->base) & BM_PLL_LOCK ? 0 : -ETIMEDOUT;
|
||||
return readl_relaxed_poll_timeout(pll->base, val, val & BM_PLL_LOCK,
|
||||
500, PLL_LOCK_TIMEOUT);
|
||||
}
|
||||
|
||||
static int clk_pllv3_prepare(struct clk_hw *hw)
|
||||
|
@ -72,7 +72,6 @@ struct clk_sscg_pll_setup {
|
||||
int divr2, divf2;
|
||||
int divq;
|
||||
int bypass;
|
||||
|
||||
uint64_t vco1;
|
||||
uint64_t vco2;
|
||||
uint64_t fout;
|
||||
@ -86,11 +85,8 @@ struct clk_sscg_pll_setup {
|
||||
struct clk_sscg_pll {
|
||||
struct clk_hw hw;
|
||||
const struct clk_ops ops;
|
||||
|
||||
void __iomem *base;
|
||||
|
||||
struct clk_sscg_pll_setup setup;
|
||||
|
||||
u8 parent;
|
||||
u8 bypass1;
|
||||
u8 bypass2;
|
||||
@ -194,7 +190,6 @@ static int clk_sscg_pll2_find_setup(struct clk_sscg_pll_setup *setup,
|
||||
struct clk_sscg_pll_setup *temp_setup,
|
||||
uint64_t ref)
|
||||
{
|
||||
|
||||
int ret;
|
||||
|
||||
if (ref < PLL_STAGE1_MIN_FREQ || ref > PLL_STAGE1_MAX_FREQ)
|
||||
@ -253,7 +248,6 @@ static int clk_sscg_pll1_find_setup(struct clk_sscg_pll_setup *setup,
|
||||
struct clk_sscg_pll_setup *temp_setup,
|
||||
uint64_t ref)
|
||||
{
|
||||
|
||||
int ret;
|
||||
|
||||
if (ref < PLL_REF_MIN_FREQ || ref > PLL_REF_MAX_FREQ)
|
||||
@ -280,7 +274,6 @@ static int clk_sscg_pll_find_setup(struct clk_sscg_pll_setup *setup,
|
||||
temp_setup.fout_request = rate;
|
||||
|
||||
switch (try_bypass) {
|
||||
|
||||
case PLL_BYPASS2:
|
||||
if (prate == rate) {
|
||||
setup->bypass = PLL_BYPASS2;
|
||||
@ -288,11 +281,9 @@ static int clk_sscg_pll_find_setup(struct clk_sscg_pll_setup *setup,
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case PLL_BYPASS1:
|
||||
ret = clk_sscg_pll2_find_setup(setup, &temp_setup, prate);
|
||||
break;
|
||||
|
||||
case PLL_BYPASS_NONE:
|
||||
ret = clk_sscg_pll1_find_setup(setup, &temp_setup, prate);
|
||||
break;
|
||||
@ -301,7 +292,6 @@ static int clk_sscg_pll_find_setup(struct clk_sscg_pll_setup *setup,
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int clk_sscg_pll_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_sscg_pll *pll = to_clk_sscg_pll(hw);
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#define IMX_CLK_GATE2_SINGLE_BIT 1
|
||||
|
||||
extern spinlock_t imx_ccm_lock;
|
||||
|
||||
void imx_check_clocks(struct clk *clks[], unsigned int count);
|
||||
@ -131,9 +133,9 @@ struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
|
||||
#define imx_clk_pll14xx(name, parent_name, base, pll_clk) \
|
||||
to_clk(imx_clk_hw_pll14xx(name, parent_name, base, pll_clk))
|
||||
|
||||
struct clk_hw *imx_clk_hw_pll14xx(const char *name, const char *parent_name,
|
||||
void __iomem *base,
|
||||
const struct imx_pll14xx_clk *pll_clk);
|
||||
struct clk_hw *imx_dev_clk_hw_pll14xx(struct device *dev, const char *name,
|
||||
const char *parent_name, void __iomem *base,
|
||||
const struct imx_pll14xx_clk *pll_clk);
|
||||
|
||||
struct clk_hw *imx_clk_hw_pllv1(enum imx_pllv1_type type, const char *name,
|
||||
const char *parent, void __iomem *base);
|
||||
@ -240,6 +242,13 @@ static inline struct clk *to_clk(struct clk_hw *hw)
|
||||
return hw->clk;
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_pll14xx(const char *name, const char *parent_name,
|
||||
void __iomem *base,
|
||||
const struct imx_pll14xx_clk *pll_clk)
|
||||
{
|
||||
return imx_dev_clk_hw_pll14xx(NULL, name, parent_name, base, pll_clk);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_fixed(const char *name, int rate)
|
||||
{
|
||||
return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
|
||||
@ -310,6 +319,13 @@ static inline struct clk_hw *imx_clk_hw_gate(const char *name, const char *paren
|
||||
shift, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_dev_clk_hw_gate(struct device *dev, const char *name,
|
||||
const char *parent, void __iomem *reg, u8 shift)
|
||||
{
|
||||
return clk_hw_register_gate(dev, name, parent, CLK_SET_RATE_PARENT, reg,
|
||||
shift, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_clk_hw_gate_dis(const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift)
|
||||
{
|
||||
@ -355,6 +371,17 @@ static inline struct clk_hw *imx_clk_hw_gate2_shared2(const char *name,
|
||||
&imx_ccm_lock, share_count);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev,
|
||||
const char *name, const char *parent,
|
||||
void __iomem *reg, u8 shift,
|
||||
unsigned int *share_count)
|
||||
{
|
||||
return clk_hw_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT |
|
||||
CLK_OPS_PARENT_ENABLE, reg, shift, 0x3,
|
||||
IMX_CLK_GATE2_SINGLE_BIT,
|
||||
&imx_ccm_lock, share_count);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_gate2_cgr(const char *name,
|
||||
const char *parent, void __iomem *reg, u8 shift, u8 cgr_val)
|
||||
{
|
||||
@ -411,6 +438,15 @@ static inline struct clk_hw *imx_clk_hw_mux(const char *name, void __iomem *reg,
|
||||
width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_dev_clk_hw_mux(struct device *dev,
|
||||
const char *name, void __iomem *reg, u8 shift,
|
||||
u8 width, const char * const *parents, int num_parents)
|
||||
{
|
||||
return clk_hw_register_mux(dev, name, parents, num_parents,
|
||||
CLK_SET_RATE_NO_REPARENT | CLK_SET_PARENT_GATE,
|
||||
reg, shift, width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg,
|
||||
u8 shift, u8 width, const char * const *parents,
|
||||
int num_parents)
|
||||
@ -473,11 +509,25 @@ static inline struct clk_hw *imx_clk_hw_mux_flags(const char *name,
|
||||
reg, shift, width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_dev_clk_hw_mux_flags(struct device *dev,
|
||||
const char *name,
|
||||
void __iomem *reg, u8 shift,
|
||||
u8 width,
|
||||
const char * const *parents,
|
||||
int num_parents,
|
||||
unsigned long flags)
|
||||
{
|
||||
return clk_hw_register_mux(dev, name, parents, num_parents,
|
||||
flags | CLK_SET_RATE_NO_REPARENT,
|
||||
reg, shift, width, 0, &imx_ccm_lock);
|
||||
}
|
||||
|
||||
struct clk_hw *imx_clk_hw_cpu(const char *name, const char *parent_name,
|
||||
struct clk *div, struct clk *mux, struct clk *pll,
|
||||
struct clk *step);
|
||||
|
||||
#define IMX_COMPOSITE_CORE BIT(0)
|
||||
#define IMX_COMPOSITE_BUS BIT(1)
|
||||
|
||||
struct clk_hw *imx8m_clk_hw_composite_flags(const char *name,
|
||||
const char * const *parent_names,
|
||||
@ -486,6 +536,12 @@ struct clk_hw *imx8m_clk_hw_composite_flags(const char *name,
|
||||
u32 composite_flags,
|
||||
unsigned long flags);
|
||||
|
||||
#define imx8m_clk_hw_composite_bus(name, parent_names, reg) \
|
||||
imx8m_clk_hw_composite_flags(name, parent_names, \
|
||||
ARRAY_SIZE(parent_names), reg, \
|
||||
IMX_COMPOSITE_BUS, \
|
||||
CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE)
|
||||
|
||||
#define imx8m_clk_hw_composite_core(name, parent_names, reg) \
|
||||
imx8m_clk_hw_composite_flags(name, parent_names, \
|
||||
ARRAY_SIZE(parent_names), reg, \
|
||||
|
@ -55,6 +55,16 @@ config INGENIC_CGU_X1000
|
||||
|
||||
If building for a X1000 SoC, you want to say Y here.
|
||||
|
||||
config INGENIC_CGU_X1830
|
||||
bool "Ingenic X1830 CGU driver"
|
||||
default MACH_X1830
|
||||
select INGENIC_CGU_COMMON
|
||||
help
|
||||
Support the clocks provided by the CGU hardware on Ingenic X1830
|
||||
and compatible SoCs.
|
||||
|
||||
If building for a X1830 SoC, you want to say Y here.
|
||||
|
||||
config INGENIC_TCU_CLK
|
||||
bool "Ingenic JZ47xx TCU clocks driver"
|
||||
default MACH_INGENIC
|
||||
|
@ -5,4 +5,5 @@ obj-$(CONFIG_INGENIC_CGU_JZ4725B) += jz4725b-cgu.o
|
||||
obj-$(CONFIG_INGENIC_CGU_JZ4770) += jz4770-cgu.o
|
||||
obj-$(CONFIG_INGENIC_CGU_JZ4780) += jz4780-cgu.o
|
||||
obj-$(CONFIG_INGENIC_CGU_X1000) += x1000-cgu.o
|
||||
obj-$(CONFIG_INGENIC_CGU_X1830) += x1830-cgu.o
|
||||
obj-$(CONFIG_INGENIC_TCU_CLK) += tcu.o
|
||||
|
@ -76,16 +76,13 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
||||
const struct ingenic_cgu_pll_info *pll_info;
|
||||
unsigned m, n, od_enc, od;
|
||||
bool bypass;
|
||||
unsigned long flags;
|
||||
u32 ctl;
|
||||
|
||||
clk_info = &cgu->clock_info[ingenic_clk->idx];
|
||||
BUG_ON(clk_info->type != CGU_CLK_PLL);
|
||||
pll_info = &clk_info->pll;
|
||||
|
||||
spin_lock_irqsave(&cgu->lock, flags);
|
||||
ctl = readl(cgu->base + pll_info->reg);
|
||||
spin_unlock_irqrestore(&cgu->lock, flags);
|
||||
|
||||
m = (ctl >> pll_info->m_shift) & GENMASK(pll_info->m_bits - 1, 0);
|
||||
m += pll_info->m_offset;
|
||||
@ -93,6 +90,9 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
||||
n += pll_info->n_offset;
|
||||
od_enc = ctl >> pll_info->od_shift;
|
||||
od_enc &= GENMASK(pll_info->od_bits - 1, 0);
|
||||
|
||||
ctl = readl(cgu->base + pll_info->bypass_reg);
|
||||
|
||||
bypass = !pll_info->no_bypass_bit &&
|
||||
!!(ctl & BIT(pll_info->bypass_bit));
|
||||
|
||||
@ -106,7 +106,8 @@ ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
||||
BUG_ON(od == pll_info->od_max);
|
||||
od++;
|
||||
|
||||
return div_u64((u64)parent_rate * m, n * od);
|
||||
return div_u64((u64)parent_rate * m * pll_info->rate_multiplier,
|
||||
n * od);
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
@ -139,7 +140,8 @@ ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info,
|
||||
if (pod)
|
||||
*pod = od;
|
||||
|
||||
return div_u64((u64)parent_rate * m, n * od);
|
||||
return div_u64((u64)parent_rate * m * pll_info->rate_multiplier,
|
||||
n * od);
|
||||
}
|
||||
|
||||
static inline const struct ingenic_cgu_clk_info *to_clk_info(
|
||||
@ -212,9 +214,14 @@ static int ingenic_pll_enable(struct clk_hw *hw)
|
||||
u32 ctl;
|
||||
|
||||
spin_lock_irqsave(&cgu->lock, flags);
|
||||
ctl = readl(cgu->base + pll_info->reg);
|
||||
ctl = readl(cgu->base + pll_info->bypass_reg);
|
||||
|
||||
ctl &= ~BIT(pll_info->bypass_bit);
|
||||
|
||||
writel(ctl, cgu->base + pll_info->bypass_reg);
|
||||
|
||||
ctl = readl(cgu->base + pll_info->reg);
|
||||
|
||||
ctl |= BIT(pll_info->enable_bit);
|
||||
|
||||
writel(ctl, cgu->base + pll_info->reg);
|
||||
@ -259,12 +266,9 @@ static int ingenic_pll_is_enabled(struct clk_hw *hw)
|
||||
struct ingenic_cgu *cgu = ingenic_clk->cgu;
|
||||
const struct ingenic_cgu_clk_info *clk_info = to_clk_info(ingenic_clk);
|
||||
const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll;
|
||||
unsigned long flags;
|
||||
u32 ctl;
|
||||
|
||||
spin_lock_irqsave(&cgu->lock, flags);
|
||||
ctl = readl(cgu->base + pll_info->reg);
|
||||
spin_unlock_irqrestore(&cgu->lock, flags);
|
||||
|
||||
return !!(ctl & BIT(pll_info->enable_bit));
|
||||
}
|
||||
@ -562,16 +566,12 @@ static int ingenic_clk_is_enabled(struct clk_hw *hw)
|
||||
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
|
||||
struct ingenic_cgu *cgu = ingenic_clk->cgu;
|
||||
const struct ingenic_cgu_clk_info *clk_info;
|
||||
unsigned long flags;
|
||||
int enabled = 1;
|
||||
|
||||
clk_info = &cgu->clock_info[ingenic_clk->idx];
|
||||
|
||||
if (clk_info->type & CGU_CLK_GATE) {
|
||||
spin_lock_irqsave(&cgu->lock, flags);
|
||||
if (clk_info->type & CGU_CLK_GATE)
|
||||
enabled = !ingenic_cgu_gate_get(cgu, &clk_info->gate);
|
||||
spin_unlock_irqrestore(&cgu->lock, flags);
|
||||
}
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
/**
|
||||
* struct ingenic_cgu_pll_info - information about a PLL
|
||||
* @reg: the offset of the PLL's control register within the CGU
|
||||
* @rate_multiplier: the multiplier needed by pll rate calculation
|
||||
* @m_shift: the number of bits to shift the multiplier value by (ie. the
|
||||
* index of the lowest bit of the multiplier value in the PLL's
|
||||
* control register)
|
||||
@ -37,6 +38,7 @@
|
||||
* @od_encoding: a pointer to an array mapping post-VCO divider values to
|
||||
* their encoded values in the PLL control register, or -1 for
|
||||
* unsupported values
|
||||
* @bypass_reg: the offset of the bypass control register within the CGU
|
||||
* @bypass_bit: the index of the bypass bit in the PLL control register
|
||||
* @enable_bit: the index of the enable bit in the PLL control register
|
||||
* @stable_bit: the index of the stable bit in the PLL control register
|
||||
@ -44,10 +46,12 @@
|
||||
*/
|
||||
struct ingenic_cgu_pll_info {
|
||||
unsigned reg;
|
||||
unsigned rate_multiplier;
|
||||
const s8 *od_encoding;
|
||||
u8 m_shift, m_bits, m_offset;
|
||||
u8 n_shift, n_bits, n_offset;
|
||||
u8 od_shift, od_bits, od_max;
|
||||
unsigned bypass_reg;
|
||||
u8 bypass_bit;
|
||||
u8 enable_bit;
|
||||
u8 stable_bit;
|
||||
|
@ -9,7 +9,9 @@
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <dt-bindings/clock/jz4725b-cgu.h>
|
||||
|
||||
#include "cgu.h"
|
||||
#include "pm.h"
|
||||
|
||||
@ -54,6 +56,7 @@ static const struct ingenic_cgu_clk_info jz4725b_cgu_clocks[] = {
|
||||
.parents = { JZ4725B_CLK_EXT, -1, -1, -1 },
|
||||
.pll = {
|
||||
.reg = CGU_REG_CPPCR,
|
||||
.rate_multiplier = 1,
|
||||
.m_shift = 23,
|
||||
.m_bits = 9,
|
||||
.m_offset = 2,
|
||||
@ -65,6 +68,7 @@ static const struct ingenic_cgu_clk_info jz4725b_cgu_clocks[] = {
|
||||
.od_max = 4,
|
||||
.od_encoding = pll_od_encoding,
|
||||
.stable_bit = 10,
|
||||
.bypass_reg = CGU_REG_CPPCR,
|
||||
.bypass_bit = 9,
|
||||
.enable_bit = 8,
|
||||
},
|
||||
|
@ -10,7 +10,9 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <dt-bindings/clock/jz4740-cgu.h>
|
||||
|
||||
#include "cgu.h"
|
||||
#include "pm.h"
|
||||
|
||||
@ -69,6 +71,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {
|
||||
.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
|
||||
.pll = {
|
||||
.reg = CGU_REG_CPPCR,
|
||||
.rate_multiplier = 1,
|
||||
.m_shift = 23,
|
||||
.m_bits = 9,
|
||||
.m_offset = 2,
|
||||
@ -80,6 +83,7 @@ static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {
|
||||
.od_max = 4,
|
||||
.od_encoding = pll_od_encoding,
|
||||
.stable_bit = 10,
|
||||
.bypass_reg = CGU_REG_CPPCR,
|
||||
.bypass_bit = 9,
|
||||
.enable_bit = 8,
|
||||
},
|
||||
|
@ -9,7 +9,9 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <dt-bindings/clock/jz4770-cgu.h>
|
||||
|
||||
#include "cgu.h"
|
||||
#include "pm.h"
|
||||
|
||||
@ -102,6 +104,7 @@ static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
|
||||
.parents = { JZ4770_CLK_EXT },
|
||||
.pll = {
|
||||
.reg = CGU_REG_CPPCR0,
|
||||
.rate_multiplier = 1,
|
||||
.m_shift = 24,
|
||||
.m_bits = 7,
|
||||
.m_offset = 1,
|
||||
@ -112,6 +115,7 @@ static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
|
||||
.od_bits = 2,
|
||||
.od_max = 8,
|
||||
.od_encoding = pll_od_encoding,
|
||||
.bypass_reg = CGU_REG_CPPCR0,
|
||||
.bypass_bit = 9,
|
||||
.enable_bit = 8,
|
||||
.stable_bit = 10,
|
||||
@ -124,6 +128,7 @@ static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
|
||||
.parents = { JZ4770_CLK_EXT },
|
||||
.pll = {
|
||||
.reg = CGU_REG_CPPCR1,
|
||||
.rate_multiplier = 1,
|
||||
.m_shift = 24,
|
||||
.m_bits = 7,
|
||||
.m_offset = 1,
|
||||
@ -134,9 +139,10 @@ static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = {
|
||||
.od_bits = 2,
|
||||
.od_max = 8,
|
||||
.od_encoding = pll_od_encoding,
|
||||
.bypass_reg = CGU_REG_CPPCR1,
|
||||
.no_bypass_bit = true,
|
||||
.enable_bit = 7,
|
||||
.stable_bit = 6,
|
||||
.no_bypass_bit = true,
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <dt-bindings/clock/jz4780-cgu.h>
|
||||
|
||||
#include "cgu.h"
|
||||
#include "pm.h"
|
||||
|
||||
@ -266,6 +267,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = {
|
||||
|
||||
#define DEF_PLL(name) { \
|
||||
.reg = CGU_REG_ ## name, \
|
||||
.rate_multiplier = 1, \
|
||||
.m_shift = 19, \
|
||||
.m_bits = 13, \
|
||||
.m_offset = 1, \
|
||||
@ -277,6 +279,7 @@ static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = {
|
||||
.od_max = 16, \
|
||||
.od_encoding = pll_od_encoding, \
|
||||
.stable_bit = 6, \
|
||||
.bypass_reg = CGU_REG_ ## name, \
|
||||
.bypass_bit = 1, \
|
||||
.enable_bit = 0, \
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ static const struct ingenic_soc_info x1000_soc_info = {
|
||||
.has_tcu_clk = false,
|
||||
};
|
||||
|
||||
static const struct of_device_id ingenic_tcu_of_match[] __initconst = {
|
||||
static const struct of_device_id __maybe_unused ingenic_tcu_of_match[] __initconst = {
|
||||
{ .compatible = "ingenic,jz4740-tcu", .data = &jz4740_soc_info, },
|
||||
{ .compatible = "ingenic,jz4725b-tcu", .data = &jz4725b_soc_info, },
|
||||
{ .compatible = "ingenic,jz4770-tcu", .data = &jz4770_soc_info, },
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user