mirror of https://github.com/torvalds/linux.git
We've finally gotten rid of the struct clk_ops::round_rate() code after months
of effort from Brian Masney. Now the only option is to use determine_rate(),
which is good because that takes a struct argument instead of just a couple
unsigned longs, allowing us to easily modify the way we determine and set rates
in the clk tree.
Beyond that core framework change we've got the typical pile of new SoC clk
driver additions, fixes for clk data and/or adding missing clks because the
consumer driver using those clks wasn't ready, etc. The usual suspects are all
here: Qualcomm, Samsung, Mediatek, and Rockchip along with some newcomers
making RISC-V SoCs like ESWIN's eic700 and Tenstorrent's Atlantis. The clk
driver side of this looks pretty normal.
Core:
- Remove the round_rate() clk op (yay!)
New Drivers:
- ESWIN eic700 SoC clk support
- Econet EN751221 SoC clock/reset support
- Global TCSR, RPMh, and display clock controller support for
the Qualcomm Eliza platform
- TCSR, the multiple global, and the RPMh clock controller
support for the Qualcomm Nord platform
- GPU clock controller support for Qualcomm SM8750
- Video and GPU clock controller support for Qualcomm Glymur
- Global clock controller support for Qualcomm IPQ5210
- Axis ARTPEC-9: Add new PLL clocks and new drivers for eight clock
controllers on the SoC
- ExynosAutov920: Add G3D (GPU) clock controller
- Clock driver for the Rockchip RV1103B SoC
- Initial support for the Renesas RZ/G3L (R9A08G046) SoC
- Clock and reset controllers (e.g. PRCM) in the Tenstorrent Atlantis SoC
-----BEGIN PGP SIGNATURE-----
iQJIBAABCAAyFiEE9L57QeeUxqYDyoaDrQKIl8bklSUFAmnmb1QUHHN3Ym95ZEBj
aHJvbWl1bS5vcmcACgkQrQKIl8bklSUcUg/+PCWUrRlcgboA/xCl+qdfa7Pxd3X6
W6Z0IFwPrF6kZQnhlIIn3JlRcHixWilwNPgd02h5QK/2gA+Fa+T3h2+SE4oNW/qY
dZm2W8qDxRIB2+/okuUaDOp0crybtRKHkph9jW1YJo+EDLRhwAVE1SKbr/uyZiAk
1mr0lk8ZXbvhE/VoQysMjoZ8ITBEQiOwJEBNma6Oufl6dPEdSnaTKWkJZsUc3xjM
kFx666wNDVqwVobX2q3J6mb3/CyPEIpyFeOgAFVkRcVdPf53Xz7BijYkS2wtPclM
E58PKIjqk1TMt9nIdo5QuHZ5Og7nPFTQ9W1R0Qo/JGfjWnqqWTwCkEOXWWgTVD6x
F/gctH+X9JkQEsXid6P4HAdFqOm2UhoUJJ+yTcwXphaQXCctG/kYRW0dbxu8N/z6
hGpOKKeTmkioHIZoUW4Ap4L9futQWVmd45J9w6MGxF4QZL9apL2ILJ7jxhefxFH6
YDb8srZ50Mqco18TERxvxMhK5kKiyzz7uL927O9pofmRPwzSKlwIKgILhVKNJff2
TbCvOKi5oFpRizH/HmjVJ4SbKjWXrwbI6vTxy59FgKnAsmcwg1NQVBDu6Wo4ohtL
HVe94hPE55q8585D5f6xhfM0MTmE73prZxmb57FtXMJbHFDwYt50v4W95ToAOz4O
wN9cQVEL1vm6hx4=
=RdCb
-----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:
"We've finally gotten rid of the struct clk_ops::round_rate() code
after months of effort from Brian Masney. Now the only option is to
use determine_rate(), which is good because that takes a struct
argument instead of just a couple unsigned longs, allowing us to
easily modify the way we determine and set rates in the clk tree.
Beyond that core framework change we've got the typical pile of new
SoC clk driver additions, fixes for clk data and/or adding missing
clks because the consumer driver using those clks wasn't ready, etc.
The usual suspects are all here: Qualcomm, Samsung, Mediatek, and
Rockchip along with some newcomers making RISC-V SoCs like ESWIN's
eic700 and Tenstorrent's Atlantis. The clk driver side of this looks
pretty normal.
Core:
- Remove the round_rate() clk op (yay!)
New Drivers:
- ESWIN eic700 SoC clk support
- Econet EN751221 SoC clock/reset support
- Global TCSR, RPMh, and display clock controller support for the
Qualcomm Eliza platform
- TCSR, the multiple global, and the RPMh clock controller support
for the Qualcomm Nord platform
- GPU clock controller support for Qualcomm SM8750
- Video and GPU clock controller support for Qualcomm Glymur
- Global clock controller support for Qualcomm IPQ5210
- Axis ARTPEC-9: Add new PLL clocks and new drivers for eight clock
controllers on the SoC
- ExynosAutov920: Add G3D (GPU) clock controller
- Clock driver for the Rockchip RV1103B SoC
- Initial support for the Renesas RZ/G3L (R9A08G046) SoC
- Clock and reset controllers (e.g. PRCM) in the Tenstorrent Atlantis SoC"
* tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (132 commits)
clk: visconti: pll: initialize clk_init_data to zero
clk: fsl-sai: Add MCLK generation support
clk: fsl-sai: Extract clock setup into fsl_sai_clk_register()
dt-bindings: clock: fsl-sai: Document clock-cells = <1> support
clk: fsl-sai: Add i.MX8M support with 8 byte register offset
clk: fsl-sai: Sort the headers
dt-bindings: clock: fsl-sai: Document i.MX8M support
clk: qcom: gcc: Add multiple global clock controller driver for Nord SoC
clk: qcom: rpmh: Add support for Nord rpmh clocks
clk: qcom: Add TCSR clock driver for Nord SoC
dt-bindings: clock: qcom: Add Nord Global Clock Controller
dt-bindings: clock: qcom-rpmhcc: Add support for Nord SoCs
dt-bindings: clock: qcom: Document the Nord SoC TCSR Clock Controller
clk: qcom: gcc-x1e80100: Keep GCC USB QTB clock always ON
clk: qcom: Constify list of critical CBCR registers
clk: qcom: Constify qcom_cc_driver_data
clk: qcom: videocc-glymur: Constify qcom_cc_desc
clk: qcom: Add a driver for SM8750 GPU clocks
dt-bindings: clock: qcom: Add SM8750 GPU clocks
clk: qcom: ipq-cmn-pll: Add IPQ8074 SoC support
...
This commit is contained in:
commit
4ee64205ff
|
|
@ -32,6 +32,7 @@ properties:
|
|||
- enum:
|
||||
- airoha,en7523-scu
|
||||
- airoha,en7581-scu
|
||||
- econet,en751221-scu
|
||||
|
||||
reg:
|
||||
items:
|
||||
|
|
@ -67,7 +68,9 @@ allOf:
|
|||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: airoha,en7581-scu
|
||||
enum:
|
||||
- airoha,en7581-scu
|
||||
- econet,en751221-scu
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
|
|
@ -98,3 +101,4 @@ examples:
|
|||
#reset-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,196 +0,0 @@
|
|||
# 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
|
||||
|
||||
clocks:
|
||||
minItems: 3
|
||||
maxItems: 4
|
||||
|
||||
clock-names:
|
||||
minItems: 3
|
||||
maxItems: 4
|
||||
|
||||
additionalProperties: 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";
|
||||
};
|
||||
...
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
# 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
|
||||
|
||||
additionalProperties: 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";
|
||||
};
|
||||
...
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/eswin,eic7700-clock.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Eswin EIC7700 SoC clock controller
|
||||
|
||||
maintainers:
|
||||
- Yifeng Huang <huangyifeng@eswincomputing.com>
|
||||
- Xuyang Dong <dongxuyang@eswincomputing.com>
|
||||
|
||||
description:
|
||||
The clock controller generates and supplies clock to all the modules
|
||||
for eic7700 SoC.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: eswin,eic7700-clock
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: External 24MHz oscillator clock
|
||||
|
||||
'#clock-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- '#clock-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
clock-controller@51828000 {
|
||||
compatible = "eswin,eic7700-clock";
|
||||
reg = <0x51828000 0x300>;
|
||||
clocks = <&xtal24m>;
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
|
|
@ -10,10 +10,10 @@ maintainers:
|
|||
- Michael Walle <michael@walle.cc>
|
||||
|
||||
description: |
|
||||
It is possible to use the BCLK pin of a SAI module as a generic clock
|
||||
output. Some SoC are very constrained in their pin multiplexer
|
||||
configuration. Eg. pins can only be changed groups. For example, on the
|
||||
LS1028A SoC you can only enable SAIs in pairs. If you use only one SAI,
|
||||
It is possible to use the BCLK or MCLK pin of a SAI module as a generic
|
||||
clock output. Some SoC are very constrained in their pin multiplexer
|
||||
configuration. E.g. pins can only be changed in groups. For example, on
|
||||
the LS1028A SoC you can only enable SAIs in pairs. If you use only one SAI,
|
||||
the second pins are wasted. Using this binding it is possible to use the
|
||||
clock of the second SAI as a MCLK clock for an audio codec, for example.
|
||||
|
||||
|
|
@ -21,16 +21,45 @@ description: |
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: fsl,vf610-sai-clock
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx8mm-sai-clock
|
||||
- fsl,imx8mn-sai-clock
|
||||
- fsl,imx8mp-sai-clock
|
||||
- const: fsl,imx8mq-sai-clock
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx8mq-sai-clock
|
||||
- fsl,vf610-sai-clock
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: bus
|
||||
- const: mclk1
|
||||
|
||||
'#clock-cells':
|
||||
const: 0
|
||||
maximum: 1
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: fsl,vf610-sai-clock
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
clock-names: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
|
|
|||
|
|
@ -29,20 +29,24 @@ properties:
|
|||
const: 1
|
||||
|
||||
clocks:
|
||||
minItems: 5
|
||||
items:
|
||||
- description: 24m osc
|
||||
- description: 32k osc
|
||||
- description: ckih1 clock input
|
||||
- description: anaclk1 clock input
|
||||
- description: anaclk2 clock input
|
||||
- description: clock input from enet ref pad
|
||||
|
||||
clock-names:
|
||||
minItems: 5
|
||||
items:
|
||||
- const: osc
|
||||
- const: ckil
|
||||
- const: ckih1
|
||||
- const: anaclk1
|
||||
- const: anaclk2
|
||||
- const: enet_ref_pad
|
||||
|
||||
fsl,pmic-stby-poweroff:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
|
|
|||
|
|
@ -29,18 +29,22 @@ properties:
|
|||
const: 1
|
||||
|
||||
clocks:
|
||||
minItems: 4
|
||||
items:
|
||||
- description: 32k osc
|
||||
- description: 24m osc
|
||||
- description: ipp_di0 clock input
|
||||
- description: ipp_di1 clock input
|
||||
- description: clock input from enet1 ref pad
|
||||
|
||||
clock-names:
|
||||
minItems: 4
|
||||
items:
|
||||
- const: ckil
|
||||
- const: osc
|
||||
- const: ipp_di0
|
||||
- const: ipp_di1
|
||||
- const: enet1_ref_pad
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/qcom,eliza-dispcc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Display Clock & Reset Controller for Qualcomm Eliza SoC
|
||||
|
||||
maintainers:
|
||||
- Bjorn Andersson <andersson@kernel.org>
|
||||
- Konrad Dybcio <konradybcio@kernel.org>
|
||||
- Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
|
||||
|
||||
description: |
|
||||
Display clock control module provides the clocks, resets and power
|
||||
domains on Qualcomm Eliza SoC platform.
|
||||
|
||||
See also:
|
||||
- include/dt-bindings/clock/qcom,eliza-dispcc.h
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,eliza-dispcc
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Board XO source
|
||||
- description: Board Always On XO source
|
||||
- description: Display's AHB clock
|
||||
- description: sleep clock
|
||||
- description: Byte clock from DSI PHY0
|
||||
- description: Pixel clock from DSI PHY0
|
||||
- description: Byte clock from DSI PHY1
|
||||
- description: Pixel clock from DSI PHY1
|
||||
- description: Link clock from DP PHY0
|
||||
- description: VCO DIV clock from DP PHY0
|
||||
- description: Link clock from DP PHY1
|
||||
- description: VCO DIV clock from DP PHY1
|
||||
- description: Link clock from DP PHY2
|
||||
- description: VCO DIV clock from DP PHY2
|
||||
- description: Link clock from DP PHY3
|
||||
- description: VCO DIV clock from DP PHY3
|
||||
- description: HDMI link clock from HDMI PHY
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required-opps:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- '#power-domain-cells'
|
||||
|
||||
allOf:
|
||||
- $ref: qcom,gcc.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,dsi-phy-28nm.h>
|
||||
#include <dt-bindings/clock/qcom,eliza-gcc.h>
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
#include <dt-bindings/power/qcom,rpmhpd.h>
|
||||
clock-controller@af00000 {
|
||||
compatible = "qcom,eliza-dispcc";
|
||||
reg = <0x0af00000 0x20000>;
|
||||
clocks = <&bi_tcxo_div2>,
|
||||
<&bi_tcxo_ao_div2>,
|
||||
<&gcc GCC_DISP_AHB_CLK>,
|
||||
<&sleep_clk>,
|
||||
<&dsi0_phy DSI_BYTE_PLL_CLK>,
|
||||
<&dsi0_phy DSI_PIXEL_PLL_CLK>,
|
||||
<&dsi1_phy DSI_BYTE_PLL_CLK>,
|
||||
<&dsi1_phy DSI_PIXEL_PLL_CLK>,
|
||||
<&dp0_phy 0>,
|
||||
<&dp0_phy 1>,
|
||||
<&dp1_phy 0>,
|
||||
<&dp1_phy 1>,
|
||||
<&dp2_phy 0>,
|
||||
<&dp2_phy 1>,
|
||||
<&dp3_phy 0>,
|
||||
<&dp3_phy 1>,
|
||||
<&hdmi_phy>;
|
||||
|
||||
#clock-cells = <1>;
|
||||
#power-domain-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
|
||||
power-domains = <&rpmhpd RPMHPD_MMCX>;
|
||||
required-opps = <&rpmhpd_opp_low_svs>;
|
||||
};
|
||||
...
|
||||
|
|
@ -4,14 +4,14 @@
|
|||
$id: http://devicetree.org/schemas/clock/qcom,glymur-dispcc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Display Clock & Reset Controller on GLYMUR
|
||||
title: Qualcomm Display Clock & Reset Controller on Glymur SoC
|
||||
|
||||
maintainers:
|
||||
- Taniya Das <taniya.das@oss.qualcomm.com>
|
||||
|
||||
description: |
|
||||
Qualcomm display clock control module which supports the clocks, resets and
|
||||
power domains for the MDSS instances on GLYMUR SoC.
|
||||
power domains for the MDSS instances on Glymur SoC.
|
||||
|
||||
See also:
|
||||
include/dt-bindings/clock/qcom,dispcc-glymur.h
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ properties:
|
|||
enum:
|
||||
- qcom,ipq5018-cmn-pll
|
||||
- qcom,ipq5424-cmn-pll
|
||||
- qcom,ipq6018-cmn-pll
|
||||
- qcom,ipq8074-cmn-pll
|
||||
- qcom,ipq9574-cmn-pll
|
||||
|
||||
reg:
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ description: |
|
|||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,glymur-gxclkctl
|
||||
- qcom,kaanapali-gxclkctl
|
||||
- qcom,sm8750-gxclkctl
|
||||
|
||||
power-domains:
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -35,9 +35,14 @@ properties:
|
|||
- description: UFS Phy Tx symbol 0 clock source
|
||||
- description: USB3 Phy wrapper pipe clock source
|
||||
|
||||
power-domains:
|
||||
items:
|
||||
- description: CX domain
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- power-domains
|
||||
- '#power-domain-cells'
|
||||
|
||||
allOf:
|
||||
|
|
@ -48,6 +53,7 @@ unevaluatedProperties: false
|
|||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
#include <dt-bindings/power/qcom,rpmhpd.h>
|
||||
clock-controller@100000 {
|
||||
compatible = "qcom,milos-gcc";
|
||||
reg = <0x00100000 0x1f4200>;
|
||||
|
|
@ -59,6 +65,7 @@ examples:
|
|||
<&ufs_mem_phy 1>,
|
||||
<&ufs_mem_phy 2>,
|
||||
<&usb_1_qmpphy>;
|
||||
power-domains = <&rpmhpd RPMHPD_CX>;
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
#power-domain-cells = <1>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/qcom,nord-gcc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Global Clock & Reset Controller on Nord SoC
|
||||
|
||||
maintainers:
|
||||
- Taniya Das <taniya.das@oss.qualcomm.com>
|
||||
|
||||
description: |
|
||||
Qualcomm global clock control module provides the clocks, resets and power
|
||||
domains on Nord SoC.
|
||||
|
||||
See also: include/dt-bindings/clock/qcom,nord-gcc.h
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,nord-gcc
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Board XO source
|
||||
- description: Sleep clock source
|
||||
- description: PCIE A Pipe clock source
|
||||
- description: PCIE B Pipe clock source
|
||||
- description: PCIE C Pipe clock source
|
||||
- description: PCIE D Pipe clock source
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- '#power-domain-cells'
|
||||
|
||||
allOf:
|
||||
- $ref: qcom,gcc.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
clock-controller@100000 {
|
||||
compatible = "qcom,nord-gcc";
|
||||
reg = <0x00100000 0x1f4200>;
|
||||
clocks = <&rpmhcc RPMH_CXO_CLK>,
|
||||
<&sleep_clk>,
|
||||
<&pcie_a_pipe_clk>,
|
||||
<&pcie_b_pipe_clk>,
|
||||
<&pcie_c_pipe_clk>,
|
||||
<&pcie_d_pipe_clk>;
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
#power-domain-cells = <1>;
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/qcom,nord-negcc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Global North East Clock & Reset Controller on Nord SoC
|
||||
|
||||
maintainers:
|
||||
- Taniya Das <taniya.das@oss.qualcomm.com>
|
||||
|
||||
description: |
|
||||
Qualcomm global clock control (NE) module provides the clocks, resets
|
||||
and power domains on Nord SoC.
|
||||
|
||||
See also: include/dt-bindings/clock/qcom,nord-negcc.h
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,nord-negcc
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Board XO source
|
||||
- description: Sleep clock source
|
||||
- description: UFS Phy Rx symbol 0 clock source
|
||||
- description: UFS Phy Rx symbol 1 clock source
|
||||
- description: UFS Phy Tx symbol 0 clock source
|
||||
- description: USB3 Phy sec wrapper pipe clock source
|
||||
- description: USB3 Phy wrapper pipe clock source
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- '#power-domain-cells'
|
||||
|
||||
allOf:
|
||||
- $ref: qcom,gcc.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
clock-controller@8900000 {
|
||||
compatible = "qcom,nord-negcc";
|
||||
reg = <0x08900000 0xf4200>;
|
||||
clocks = <&rpmhcc RPMH_CXO_CLK>,
|
||||
<&sleep_clk>,
|
||||
<&ufs_phy_rx_symbol_0_clk>,
|
||||
<&ufs_phy_rx_symbol_1_clk>,
|
||||
<&ufs_phy_tx_symbol_0_clk>,
|
||||
<&usb3_phy_sec_pipe_clk>,
|
||||
<&usb3_phy_pipe_clk>;
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
#power-domain-cells = <1>;
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/qcom,nord-nwgcc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Global North West and South East Clock & Reset Controller
|
||||
on Nord SoC
|
||||
|
||||
maintainers:
|
||||
- Taniya Das <taniya.das@oss.qualcomm.com>
|
||||
|
||||
description: |
|
||||
Qualcomm global clock control (NW, SE) module provides the clocks, resets
|
||||
and power domains on Nord SoC.
|
||||
|
||||
See also:
|
||||
include/dt-bindings/clock/qcom,nord-nwgcc.h
|
||||
include/dt-bindings/clock/qcom,nord-segcc.h
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,nord-nwgcc
|
||||
- qcom,nord-segcc
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Board XO source
|
||||
- description: Sleep clock source
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
- '#power-domain-cells'
|
||||
|
||||
allOf:
|
||||
- $ref: qcom,gcc.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
clock-controller@8b00000 {
|
||||
compatible = "qcom,nord-nwgcc";
|
||||
reg = <0x08b00000 0xf4200>;
|
||||
clocks = <&rpmhcc RPMH_CXO_CLK>,
|
||||
<&sleep_clk>;
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
#power-domain-cells = <1>;
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -21,6 +21,7 @@ properties:
|
|||
- qcom,glymur-rpmh-clk
|
||||
- qcom,kaanapali-rpmh-clk
|
||||
- qcom,milos-rpmh-clk
|
||||
- qcom,nord-rpmh-clk
|
||||
- qcom,qcs615-rpmh-clk
|
||||
- qcom,qdu1000-rpmh-clk
|
||||
- qcom,sa8775p-rpmh-clk
|
||||
|
|
|
|||
|
|
@ -8,12 +8,14 @@ title: Qualcomm Graphics Clock & Reset Controller on SM8450
|
|||
|
||||
maintainers:
|
||||
- Konrad Dybcio <konradybcio@kernel.org>
|
||||
- Taniya Das <taniya.das@oss.qualcomm.com>
|
||||
|
||||
description: |
|
||||
Qualcomm graphics clock control module provides the clocks, resets and power
|
||||
domains on Qualcomm SoCs.
|
||||
|
||||
See also::
|
||||
See also:
|
||||
include/dt-bindings/clock/qcom,glymur-gpucc.h
|
||||
include/dt-bindings/clock/qcom,kaanapali-gpucc.h
|
||||
include/dt-bindings/clock/qcom,milos-gpucc.h
|
||||
include/dt-bindings/clock/qcom,sar2130p-gpucc.h
|
||||
|
|
@ -22,11 +24,13 @@ description: |
|
|||
include/dt-bindings/clock/qcom,sm8550-gpucc.h
|
||||
include/dt-bindings/reset/qcom,sm8450-gpucc.h
|
||||
include/dt-bindings/reset/qcom,sm8650-gpucc.h
|
||||
include/dt-bindings/reset/qcom,sm8750-gpucc.h
|
||||
include/dt-bindings/reset/qcom,x1e80100-gpucc.h
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,glymur-gpucc
|
||||
- qcom,kaanapali-gpucc
|
||||
- qcom,milos-gpucc
|
||||
- qcom,sar2130p-gpucc
|
||||
|
|
@ -35,6 +39,7 @@ properties:
|
|||
- qcom,sm8475-gpucc
|
||||
- qcom,sm8550-gpucc
|
||||
- qcom,sm8650-gpucc
|
||||
- qcom,sm8750-gpucc
|
||||
- qcom,x1e80100-gpucc
|
||||
- qcom,x1p42100-gpucc
|
||||
|
||||
|
|
@ -44,6 +49,16 @@ properties:
|
|||
- description: GPLL0 main branch source
|
||||
- description: GPLL0 div branch source
|
||||
|
||||
power-domains:
|
||||
items:
|
||||
- description: A phandle to the MX power-domain
|
||||
- description: A phandle to the CX power-domain
|
||||
|
||||
required-opps:
|
||||
items:
|
||||
- description: A phandle to an OPP node describing MX performance points
|
||||
- description: A phandle to an OPP node describing CX performance points
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- clocks
|
||||
|
|
@ -51,6 +66,16 @@ required:
|
|||
|
||||
allOf:
|
||||
- $ref: qcom,gcc.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sm8750-gpucc
|
||||
then:
|
||||
required:
|
||||
- power-domains
|
||||
- required-opps
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ description: |
|
|||
domains on SM8450.
|
||||
|
||||
See also:
|
||||
include/dt-bindings/clock/qcom,glymur-videocc.h
|
||||
include/dt-bindings/clock/qcom,kaanapali-videocc.h
|
||||
include/dt-bindings/clock/qcom,sm8450-videocc.h
|
||||
include/dt-bindings/clock/qcom,sm8650-videocc.h
|
||||
|
|
@ -23,6 +24,7 @@ description: |
|
|||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,glymur-videocc
|
||||
- qcom,kaanapali-videocc
|
||||
- qcom,sm8450-videocc
|
||||
- qcom,sm8475-videocc
|
||||
|
|
@ -63,6 +65,7 @@ allOf:
|
|||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,glymur-videocc
|
||||
- qcom,kaanapali-videocc
|
||||
- qcom,sm8450-videocc
|
||||
- qcom,sm8550-videocc
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ description: |
|
|||
See also:
|
||||
- include/dt-bindings/clock/qcom,eliza-tcsr.h
|
||||
- include/dt-bindings/clock/qcom,glymur-tcsr.h
|
||||
- include/dt-bindings/clock/qcom,nord-tcsrcc.h
|
||||
- include/dt-bindings/clock/qcom,sm8550-tcsr.h
|
||||
- include/dt-bindings/clock/qcom,sm8650-tcsr.h
|
||||
- include/dt-bindings/clock/qcom,sm8750-tcsr.h
|
||||
|
|
@ -29,6 +30,7 @@ properties:
|
|||
- qcom,glymur-tcsr
|
||||
- qcom,kaanapali-tcsr
|
||||
- qcom,milos-tcsr
|
||||
- qcom,nord-tcsrcc
|
||||
- qcom,sar2130p-tcsr
|
||||
- qcom,sm8550-tcsr
|
||||
- qcom,sm8650-tcsr
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ properties:
|
|||
- samsung,exynosautov920-cmu-cpucl0
|
||||
- samsung,exynosautov920-cmu-cpucl1
|
||||
- samsung,exynosautov920-cmu-cpucl2
|
||||
- samsung,exynosautov920-cmu-g3d
|
||||
- samsung,exynosautov920-cmu-hsi0
|
||||
- samsung,exynosautov920-cmu-hsi1
|
||||
- samsung,exynosautov920-cmu-hsi2
|
||||
|
|
@ -287,6 +288,26 @@ allOf:
|
|||
- const: oscclk
|
||||
- const: noc
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: samsung,exynosautov920-cmu-g3d
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
items:
|
||||
- description: External reference clock (38.4 MHz)
|
||||
- description: CMU_G3D SWITCH clock (from CMU_TOP)
|
||||
- description: CMU_G3D NOCP clock (from CMU_TOP)
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: oscclk
|
||||
- const: switch
|
||||
- const: nocp
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- "#clock-cells"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/clock/tenstorrent,atlantis-prcm-rcpu.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Tenstorrent Atlantis PRCM (Power, Reset, Clock Management) Module
|
||||
|
||||
maintainers:
|
||||
- Anirudh Srinivasan <asrinivasan@oss.tenstorrent.com>
|
||||
|
||||
description:
|
||||
Multifunctional register block found in Tenstorrent Atlantis SoC whose main
|
||||
function is to control clocks and resets. This block is instantiated multiple
|
||||
times in the SoC, each block controls clock and resets for a different
|
||||
subsystem. RCPU prcm serves low speed IO interfaces.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- tenstorrent,atlantis-prcm-rcpu
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
"#clock-cells":
|
||||
const: 1
|
||||
description:
|
||||
See <dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h> for valid indices.
|
||||
|
||||
"#reset-cells":
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- "#clock-cells"
|
||||
- "#reset-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
clock-controller@a8000000 {
|
||||
compatible = "tenstorrent,atlantis-prcm-rcpu";
|
||||
reg = <0xa8000000 0x10000>;
|
||||
clocks = <&osc_24m>;
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
};
|
||||
|
|
@ -61,6 +61,7 @@ select:
|
|||
- cirrus,ep7209-syscon2
|
||||
- cirrus,ep7209-syscon3
|
||||
- cnxt,cx92755-uc
|
||||
- econet,en751221-chip-scu
|
||||
- freecom,fsg-cs2-system-controller
|
||||
- fsl,imx93-aonmix-ns-syscfg
|
||||
- fsl,imx93-wakeupmix-syscfg
|
||||
|
|
@ -173,6 +174,7 @@ properties:
|
|||
- cirrus,ep7209-syscon2
|
||||
- cirrus,ep7209-syscon3
|
||||
- cnxt,cx92755-uc
|
||||
- econet,en751221-chip-scu
|
||||
- freecom,fsg-cs2-system-controller
|
||||
- fsl,imx93-aonmix-ns-syscfg
|
||||
- fsl,imx93-wakeupmix-syscfg
|
||||
|
|
|
|||
|
|
@ -77,9 +77,6 @@ the operations defined in clk-provider.h::
|
|||
void (*disable_unused)(struct clk_hw *hw);
|
||||
unsigned long (*recalc_rate)(struct clk_hw *hw,
|
||||
unsigned long parent_rate);
|
||||
long (*round_rate)(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long *parent_rate);
|
||||
int (*determine_rate)(struct clk_hw *hw,
|
||||
struct clk_rate_request *req);
|
||||
int (*set_parent)(struct clk_hw *hw, u8 index);
|
||||
|
|
@ -220,9 +217,7 @@ optional or must be evaluated on a case-by-case basis.
|
|||
+----------------+------+-------------+---------------+-------------+------+
|
||||
|.recalc_rate | | y | | | |
|
||||
+----------------+------+-------------+---------------+-------------+------+
|
||||
|.round_rate | | y [1]_ | | | |
|
||||
+----------------+------+-------------+---------------+-------------+------+
|
||||
|.determine_rate | | y [1]_ | | | |
|
||||
|.determine_rate | | y | | | |
|
||||
+----------------+------+-------------+---------------+-------------+------+
|
||||
|.set_rate | | y | | | |
|
||||
+----------------+------+-------------+---------------+-------------+------+
|
||||
|
|
@ -238,8 +233,6 @@ optional or must be evaluated on a case-by-case basis.
|
|||
|.init | | | | | |
|
||||
+----------------+------+-------------+---------------+-------------+------+
|
||||
|
||||
.. [1] either one of round_rate or determine_rate is required.
|
||||
|
||||
Finally, register your clock at run-time with a hardware-specific
|
||||
registration function. This function simply populates struct clk_foo's
|
||||
data and then passes the common struct clk parameters to the framework
|
||||
|
|
|
|||
14
MAINTAINERS
14
MAINTAINERS
|
|
@ -9139,6 +9139,8 @@ F: arch/mips/boot/dts/econet/
|
|||
F: arch/mips/econet/
|
||||
F: drivers/clocksource/timer-econet-en751221.c
|
||||
F: drivers/irqchip/irq-econet-en751221.c
|
||||
F: include/dt-bindings/clock/econet,en751221-scu.h
|
||||
F: include/dt-bindings/reset/econet,en751221-scu.h
|
||||
|
||||
ECRYPT FILE SYSTEM
|
||||
M: Tyler Hicks <code@tyhicks.com>
|
||||
|
|
@ -9538,6 +9540,14 @@ T: git https://github.com/eswincomputing/linux-next.git
|
|||
F: Documentation/devicetree/bindings/riscv/eswin.yaml
|
||||
F: arch/riscv/boot/dts/eswin/
|
||||
|
||||
ESWIN EIC7700 CLOCK DRIVER
|
||||
M: Yifeng Huang <huangyifeng@eswincomputing.com>
|
||||
M: Xuyang Dong <dongxuyang@eswincomputing.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/clock/eswin,eic7700-clock.yaml
|
||||
F: drivers/clk/eswin/
|
||||
F: include/dt-bindings/clock/eswin,eic7700-clock.h
|
||||
|
||||
ET131X NETWORK DRIVER
|
||||
M: Mark Einon <mark.einon@gmail.com>
|
||||
S: Odd Fixes
|
||||
|
|
@ -23012,8 +23022,12 @@ M: Joel Stanley <jms@oss.tenstorrent.com>
|
|||
L: linux-riscv@lists.infradead.org
|
||||
S: Maintained
|
||||
T: git https://github.com/tenstorrent/linux.git
|
||||
F: Documentation/devicetree/bindings/clock/tenstorrent,atlantis-prcm-rcpu.yaml
|
||||
F: Documentation/devicetree/bindings/riscv/tenstorrent.yaml
|
||||
F: arch/riscv/boot/dts/tenstorrent/
|
||||
F: drivers/clk/tenstorrent/
|
||||
F: drivers/reset/reset-tenstorrent-atlantis.c
|
||||
F: include/dt-bindings/clock/tenstorrent,atlantis-prcm-rcpu.h
|
||||
|
||||
RISC-V THEAD SoC SUPPORT
|
||||
M: Drew Fustini <fustini@kernel.org>
|
||||
|
|
|
|||
|
|
@ -218,13 +218,13 @@ config COMMON_CLK_CS2000_CP
|
|||
If you say yes here you get support for the CS2000 clock multiplier.
|
||||
|
||||
config COMMON_CLK_EN7523
|
||||
bool "Clock driver for Airoha EN7523 SoC system clocks"
|
||||
bool "Clock driver for Airoha/EcoNet SoC system clocks"
|
||||
depends on OF
|
||||
depends on ARCH_AIROHA || COMPILE_TEST
|
||||
depends on ARCH_AIROHA || ECONET || COMPILE_TEST
|
||||
default ARCH_AIROHA
|
||||
help
|
||||
This driver provides the fixed clocks and gates present on Airoha
|
||||
ARM silicon.
|
||||
and EcoNet silicon.
|
||||
|
||||
config COMMON_CLK_EP93XX
|
||||
tristate "Clock driver for Cirrus Logic ep93xx SoC"
|
||||
|
|
@ -255,7 +255,7 @@ config COMMON_CLK_FSL_FLEXSPI
|
|||
|
||||
config COMMON_CLK_FSL_SAI
|
||||
bool "Clock driver for BCLK of Freescale SAI cores"
|
||||
depends on ARCH_LAYERSCAPE || COMPILE_TEST
|
||||
depends on ARCH_LAYERSCAPE || ARCH_MXC || COMPILE_TEST
|
||||
help
|
||||
This driver supports the Freescale SAI (Synchronous Audio Interface)
|
||||
to be used as a generic clock output. Some SoCs have restrictions
|
||||
|
|
@ -502,8 +502,8 @@ config COMMON_CLK_RPMI
|
|||
source "drivers/clk/actions/Kconfig"
|
||||
source "drivers/clk/analogbits/Kconfig"
|
||||
source "drivers/clk/aspeed/Kconfig"
|
||||
source "drivers/clk/baikal-t1/Kconfig"
|
||||
source "drivers/clk/bcm/Kconfig"
|
||||
source "drivers/clk/eswin/Kconfig"
|
||||
source "drivers/clk/hisilicon/Kconfig"
|
||||
source "drivers/clk/imgtec/Kconfig"
|
||||
source "drivers/clk/imx/Kconfig"
|
||||
|
|
@ -531,6 +531,7 @@ source "drivers/clk/starfive/Kconfig"
|
|||
source "drivers/clk/sunxi/Kconfig"
|
||||
source "drivers/clk/sunxi-ng/Kconfig"
|
||||
source "drivers/clk/tegra/Kconfig"
|
||||
source "drivers/clk/tenstorrent/Kconfig"
|
||||
source "drivers/clk/thead/Kconfig"
|
||||
source "drivers/clk/stm32/Kconfig"
|
||||
source "drivers/clk/ti/Kconfig"
|
||||
|
|
|
|||
|
|
@ -116,10 +116,10 @@ obj-y += aspeed/
|
|||
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/
|
||||
obj-$(CONFIG_COMMON_CLK_ESWIN) += eswin/
|
||||
obj-$(CONFIG_ARCH_HISI) += hisilicon/
|
||||
obj-y += imgtec/
|
||||
obj-y += imx/
|
||||
|
|
@ -155,6 +155,7 @@ obj-y += starfive/
|
|||
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
|
||||
obj-y += sunxi-ng/
|
||||
obj-$(CONFIG_ARCH_TEGRA) += tegra/
|
||||
obj-y += tenstorrent/
|
||||
obj-$(CONFIG_ARCH_THEAD) += thead/
|
||||
obj-y += ti/
|
||||
obj-$(CONFIG_CLK_UNIPHIER) += uniphier/
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
# 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 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.
|
||||
|
||||
config CLK_BT1_CCU_RST
|
||||
bool "Baikal-T1 CCU Resets support"
|
||||
select RESET_CONTROLLER
|
||||
select MFD_SYSCON
|
||||
default MIPS_BAIKAL_T1
|
||||
help
|
||||
Enable this to support the CCU reset blocks responsible for the
|
||||
AXI-bus and some subsystems reset. These are mainly the
|
||||
self-deasserted reset controls but there are several lines which
|
||||
can be directly asserted/de-asserted (PCIe and DDR sub-domains).
|
||||
|
||||
endif
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
# 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
|
||||
obj-$(CONFIG_CLK_BT1_CCU_RST) += ccu-rst.o
|
||||
|
|
@ -1,653 +0,0 @@
|
|||
// 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_GATE_REF_BUF BIT(28)
|
||||
#define CCU_DIV_CTL_LOCK_NORMAL BIT(31)
|
||||
|
||||
#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 int ccu_div_buf_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_GATE_REF_BUF, 0);
|
||||
spin_unlock_irqrestore(&div->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ccu_div_buf_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_GATE_REF_BUF, CCU_DIV_CTL_GATE_REF_BUF);
|
||||
spin_unlock_irqrestore(&div->lock, flags);
|
||||
}
|
||||
|
||||
static int ccu_div_buf_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_GATE_REF_BUF);
|
||||
}
|
||||
|
||||
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 int ccu_div_var_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
unsigned long divider;
|
||||
|
||||
divider = ccu_div_var_calc_divider(req->rate, req->best_parent_rate,
|
||||
div->mask);
|
||||
|
||||
req->rate = ccu_div_calc_freq(req->best_parent_rate, divider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 int ccu_div_fixed_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct ccu_div *div = to_ccu_div(hw);
|
||||
|
||||
req->rate = ccu_div_calc_freq(req->best_parent_rate, div->divider);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ccu_div_fixed_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
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_buf", CCU_DIV_CTL_GATE_REF_BUF),
|
||||
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 = kzalloc_objs(*bits, num);
|
||||
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;
|
||||
}
|
||||
|
||||
if (!strcmp("div_buf", 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_obj(*bit);
|
||||
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_buf_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_obj(*bit);
|
||||
if (!bit)
|
||||
return;
|
||||
|
||||
*bit = ccu_div_bits[3];
|
||||
bit->div = div;
|
||||
debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit,
|
||||
&ccu_div_dbgfs_bit_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_buf_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,
|
||||
.determine_rate = ccu_div_var_determine_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,
|
||||
.determine_rate = ccu_div_var_determine_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,
|
||||
.determine_rate = ccu_div_fixed_determine_rate,
|
||||
.set_rate = ccu_div_fixed_set_rate,
|
||||
.debug_init = ccu_div_gate_debug_init
|
||||
};
|
||||
|
||||
static const struct clk_ops ccu_div_buf_ops = {
|
||||
.enable = ccu_div_buf_enable,
|
||||
.disable = ccu_div_buf_disable,
|
||||
.is_enabled = ccu_div_buf_is_enabled,
|
||||
.debug_init = ccu_div_buf_debug_init
|
||||
};
|
||||
|
||||
static const struct clk_ops ccu_div_fixed_ops = {
|
||||
.recalc_rate = ccu_div_fixed_recalc_rate,
|
||||
.determine_rate = ccu_div_fixed_determine_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_obj(*div);
|
||||
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_BUF) {
|
||||
hw_init.ops = &ccu_div_buf_ops;
|
||||
} 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;
|
||||
parent_data.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);
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
/* 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 clock IDs
|
||||
* @CCU_SYS_SATA_CLK: CCU SATA internal clock
|
||||
* @CCU_SYS_XGMAC_CLK: CCU XGMAC internal clock
|
||||
*/
|
||||
#define CCU_SYS_SATA_CLK -1
|
||||
#define CCU_SYS_XGMAC_CLK -2
|
||||
|
||||
/*
|
||||
* CCU Divider private flags
|
||||
* @CCU_DIV_BASIC: Basic divider clock required by the kernel as early as
|
||||
* possible.
|
||||
* @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: There is a clock domain reset handle.
|
||||
*/
|
||||
#define CCU_DIV_BASIC BIT(0)
|
||||
#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_BUF: Clock gate with no divider.
|
||||
* @CCU_DIV_FIXED: Ungateable clock with fixed divider.
|
||||
*/
|
||||
enum ccu_div_type {
|
||||
CCU_DIV_VAR,
|
||||
CCU_DIV_GATE,
|
||||
CCU_DIV_BUF,
|
||||
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);
|
||||
|
||||
#endif /* __CLK_BT1_CCU_DIV_H__ */
|
||||
|
|
@ -1,560 +0,0 @@
|
|||
// 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 int ccu_pll_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
unsigned long nr = 1, nf = 1, od = 1;
|
||||
|
||||
ccu_pll_calc_factors(req->rate, req->best_parent_rate, &nr, &nf, &od);
|
||||
|
||||
req->rate = ccu_pll_calc_freq(req->best_parent_rate, nr, nf, od);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 = kzalloc_objs(*bits, CCU_PLL_DBGFS_BIT_NUM);
|
||||
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 = kzalloc_objs(*flds, CCU_PLL_DBGFS_FLD_NUM);
|
||||
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,
|
||||
.determine_rate = ccu_pll_determine_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,
|
||||
.determine_rate = ccu_pll_determine_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_obj(*pll);
|
||||
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);
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
/* 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>
|
||||
|
||||
/*
|
||||
* CCU PLL private flags
|
||||
* @CCU_PLL_BASIC: Basic PLL required by the kernel as early as possible.
|
||||
*/
|
||||
#define CCU_PLL_BASIC BIT(0)
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* @features: PLL private features.
|
||||
*/
|
||||
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;
|
||||
unsigned long features;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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__ */
|
||||
|
|
@ -1,217 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
|
||||
*
|
||||
* Authors:
|
||||
* Serge Semin <Sergey.Semin@baikalelectronics.ru>
|
||||
*
|
||||
* Baikal-T1 CCU Resets interface driver
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "bt1-ccu-rst: " fmt
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/reset/bt1-ccu.h>
|
||||
|
||||
#include "ccu-rst.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_DDR_BASE 0x02c
|
||||
#define CCU_SYS_SATA_REF_BASE 0x060
|
||||
#define CCU_SYS_APB_BASE 0x064
|
||||
#define CCU_SYS_PCIE_BASE 0x144
|
||||
|
||||
#define CCU_RST_DELAY_US 1
|
||||
|
||||
#define CCU_RST_TRIG(_base, _ofs) \
|
||||
{ \
|
||||
.type = CCU_RST_TRIG, \
|
||||
.base = _base, \
|
||||
.mask = BIT(_ofs), \
|
||||
}
|
||||
|
||||
#define CCU_RST_DIR(_base, _ofs) \
|
||||
{ \
|
||||
.type = CCU_RST_DIR, \
|
||||
.base = _base, \
|
||||
.mask = BIT(_ofs), \
|
||||
}
|
||||
|
||||
struct ccu_rst_info {
|
||||
enum ccu_rst_type type;
|
||||
unsigned int base;
|
||||
unsigned int mask;
|
||||
};
|
||||
|
||||
/*
|
||||
* Each AXI-bus clock divider is equipped with the corresponding clock-consumer
|
||||
* domain reset (it's self-deasserted reset control).
|
||||
*/
|
||||
static const struct ccu_rst_info axi_rst_info[] = {
|
||||
[CCU_AXI_MAIN_RST] = CCU_RST_TRIG(CCU_AXI_MAIN_BASE, 1),
|
||||
[CCU_AXI_DDR_RST] = CCU_RST_TRIG(CCU_AXI_DDR_BASE, 1),
|
||||
[CCU_AXI_SATA_RST] = CCU_RST_TRIG(CCU_AXI_SATA_BASE, 1),
|
||||
[CCU_AXI_GMAC0_RST] = CCU_RST_TRIG(CCU_AXI_GMAC0_BASE, 1),
|
||||
[CCU_AXI_GMAC1_RST] = CCU_RST_TRIG(CCU_AXI_GMAC1_BASE, 1),
|
||||
[CCU_AXI_XGMAC_RST] = CCU_RST_TRIG(CCU_AXI_XGMAC_BASE, 1),
|
||||
[CCU_AXI_PCIE_M_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_M_BASE, 1),
|
||||
[CCU_AXI_PCIE_S_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_S_BASE, 1),
|
||||
[CCU_AXI_USB_RST] = CCU_RST_TRIG(CCU_AXI_USB_BASE, 1),
|
||||
[CCU_AXI_HWA_RST] = CCU_RST_TRIG(CCU_AXI_HWA_BASE, 1),
|
||||
[CCU_AXI_SRAM_RST] = CCU_RST_TRIG(CCU_AXI_SRAM_BASE, 1),
|
||||
};
|
||||
|
||||
/*
|
||||
* SATA reference clock domain and APB-bus domain are connected with the
|
||||
* sefl-deasserted reset control, which can be activated via the corresponding
|
||||
* clock divider register. DDR and PCIe sub-domains can be reset with directly
|
||||
* controlled reset signals. Resetting the DDR controller though won't end up
|
||||
* well while the Linux kernel is working.
|
||||
*/
|
||||
static const struct ccu_rst_info sys_rst_info[] = {
|
||||
[CCU_SYS_SATA_REF_RST] = CCU_RST_TRIG(CCU_SYS_SATA_REF_BASE, 1),
|
||||
[CCU_SYS_APB_RST] = CCU_RST_TRIG(CCU_SYS_APB_BASE, 1),
|
||||
[CCU_SYS_DDR_FULL_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 1),
|
||||
[CCU_SYS_DDR_INIT_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 2),
|
||||
[CCU_SYS_PCIE_PCS_PHY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 0),
|
||||
[CCU_SYS_PCIE_PIPE0_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 4),
|
||||
[CCU_SYS_PCIE_CORE_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 8),
|
||||
[CCU_SYS_PCIE_PWR_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 9),
|
||||
[CCU_SYS_PCIE_STICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 10),
|
||||
[CCU_SYS_PCIE_NSTICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 11),
|
||||
[CCU_SYS_PCIE_HOT_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 12),
|
||||
};
|
||||
|
||||
static int ccu_rst_reset(struct reset_controller_dev *rcdev, unsigned long idx)
|
||||
{
|
||||
struct ccu_rst *rst = to_ccu_rst(rcdev);
|
||||
const struct ccu_rst_info *info = &rst->rsts_info[idx];
|
||||
|
||||
if (info->type != CCU_RST_TRIG)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
regmap_update_bits(rst->sys_regs, info->base, info->mask, info->mask);
|
||||
|
||||
/* The next delay must be enough to cover all the resets. */
|
||||
udelay(CCU_RST_DELAY_US);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ccu_rst_set(struct reset_controller_dev *rcdev,
|
||||
unsigned long idx, bool high)
|
||||
{
|
||||
struct ccu_rst *rst = to_ccu_rst(rcdev);
|
||||
const struct ccu_rst_info *info = &rst->rsts_info[idx];
|
||||
|
||||
if (info->type != CCU_RST_DIR)
|
||||
return high ? -EOPNOTSUPP : 0;
|
||||
|
||||
return regmap_update_bits(rst->sys_regs, info->base,
|
||||
info->mask, high ? info->mask : 0);
|
||||
}
|
||||
|
||||
static int ccu_rst_assert(struct reset_controller_dev *rcdev,
|
||||
unsigned long idx)
|
||||
{
|
||||
return ccu_rst_set(rcdev, idx, true);
|
||||
}
|
||||
|
||||
static int ccu_rst_deassert(struct reset_controller_dev *rcdev,
|
||||
unsigned long idx)
|
||||
{
|
||||
return ccu_rst_set(rcdev, idx, false);
|
||||
}
|
||||
|
||||
static int ccu_rst_status(struct reset_controller_dev *rcdev,
|
||||
unsigned long idx)
|
||||
{
|
||||
struct ccu_rst *rst = to_ccu_rst(rcdev);
|
||||
const struct ccu_rst_info *info = &rst->rsts_info[idx];
|
||||
u32 val;
|
||||
|
||||
if (info->type != CCU_RST_DIR)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
regmap_read(rst->sys_regs, info->base, &val);
|
||||
|
||||
return !!(val & info->mask);
|
||||
}
|
||||
|
||||
static const struct reset_control_ops ccu_rst_ops = {
|
||||
.reset = ccu_rst_reset,
|
||||
.assert = ccu_rst_assert,
|
||||
.deassert = ccu_rst_deassert,
|
||||
.status = ccu_rst_status,
|
||||
};
|
||||
|
||||
struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *rst_init)
|
||||
{
|
||||
struct ccu_rst *rst;
|
||||
int ret;
|
||||
|
||||
if (!rst_init)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
rst = kzalloc_obj(*rst);
|
||||
if (!rst)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
rst->sys_regs = rst_init->sys_regs;
|
||||
if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-axi")) {
|
||||
rst->rcdev.nr_resets = ARRAY_SIZE(axi_rst_info);
|
||||
rst->rsts_info = axi_rst_info;
|
||||
} else if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-sys")) {
|
||||
rst->rcdev.nr_resets = ARRAY_SIZE(sys_rst_info);
|
||||
rst->rsts_info = sys_rst_info;
|
||||
} else {
|
||||
pr_err("Incompatible DT node '%s' specified\n",
|
||||
of_node_full_name(rst_init->np));
|
||||
ret = -EINVAL;
|
||||
goto err_kfree_rst;
|
||||
}
|
||||
|
||||
rst->rcdev.owner = THIS_MODULE;
|
||||
rst->rcdev.ops = &ccu_rst_ops;
|
||||
rst->rcdev.of_node = rst_init->np;
|
||||
|
||||
ret = reset_controller_register(&rst->rcdev);
|
||||
if (ret) {
|
||||
pr_err("Couldn't register '%s' reset controller\n",
|
||||
of_node_full_name(rst_init->np));
|
||||
goto err_kfree_rst;
|
||||
}
|
||||
|
||||
return rst;
|
||||
|
||||
err_kfree_rst:
|
||||
kfree(rst);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void ccu_rst_hw_unregister(struct ccu_rst *rst)
|
||||
{
|
||||
reset_controller_unregister(&rst->rcdev);
|
||||
|
||||
kfree(rst);
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
|
||||
*
|
||||
* Baikal-T1 CCU Resets interface driver
|
||||
*/
|
||||
#ifndef __CLK_BT1_CCU_RST_H__
|
||||
#define __CLK_BT1_CCU_RST_H__
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
struct ccu_rst_info;
|
||||
|
||||
/*
|
||||
* enum ccu_rst_type - CCU Reset types
|
||||
* @CCU_RST_TRIG: Self-deasserted reset signal.
|
||||
* @CCU_RST_DIR: Directly controlled reset signal.
|
||||
*/
|
||||
enum ccu_rst_type {
|
||||
CCU_RST_TRIG,
|
||||
CCU_RST_DIR,
|
||||
};
|
||||
|
||||
/*
|
||||
* struct ccu_rst_init_data - CCU Resets initialization data
|
||||
* @sys_regs: Baikal-T1 System Controller registers map.
|
||||
* @np: Pointer to the node with the System CCU block.
|
||||
*/
|
||||
struct ccu_rst_init_data {
|
||||
struct regmap *sys_regs;
|
||||
struct device_node *np;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct ccu_rst - CCU Reset descriptor
|
||||
* @rcdev: Reset controller descriptor.
|
||||
* @sys_regs: Baikal-T1 System Controller registers map.
|
||||
* @rsts_info: Reset flag info (base address and mask).
|
||||
*/
|
||||
struct ccu_rst {
|
||||
struct reset_controller_dev rcdev;
|
||||
struct regmap *sys_regs;
|
||||
const struct ccu_rst_info *rsts_info;
|
||||
};
|
||||
#define to_ccu_rst(_rcdev) container_of(_rcdev, struct ccu_rst, rcdev)
|
||||
|
||||
#ifdef CONFIG_CLK_BT1_CCU_RST
|
||||
|
||||
struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *init);
|
||||
|
||||
void ccu_rst_hw_unregister(struct ccu_rst *rst);
|
||||
|
||||
#else
|
||||
|
||||
static inline
|
||||
struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *init)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void ccu_rst_hw_unregister(struct ccu_rst *rst) {}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __CLK_BT1_CCU_RST_H__ */
|
||||
|
|
@ -1,520 +0,0 @@
|
|||
// 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/platform_device.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/ioport.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/clock/bt1-ccu.h>
|
||||
|
||||
#include "ccu-div.h"
|
||||
#include "ccu-rst.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_BUF_INFO(_id, _name, _pname, _base, _flags) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.base = _base, \
|
||||
.type = CCU_DIV_BUF, \
|
||||
.flags = _flags \
|
||||
}
|
||||
|
||||
#define CCU_DIV_FIXED_INFO(_id, _name, _pname, _divider) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.type = CCU_DIV_FIXED, \
|
||||
.divider = _divider \
|
||||
}
|
||||
|
||||
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_data {
|
||||
struct device_node *np;
|
||||
struct regmap *sys_regs;
|
||||
|
||||
unsigned int divs_num;
|
||||
const struct ccu_div_info *divs_info;
|
||||
struct ccu_div **divs;
|
||||
|
||||
struct ccu_rst *rsts;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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)
|
||||
};
|
||||
|
||||
/*
|
||||
* 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_CLK, "sys_sata_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_BUF_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk",
|
||||
"sys_sata_clk", CCU_SYS_SATA_REF_BASE,
|
||||
CLK_SET_RATE_PARENT),
|
||||
CCU_DIV_VAR_INFO(CCU_SYS_APB_CLK, "sys_apb_clk",
|
||||
"pcie_clk", CCU_SYS_APB_BASE, 5,
|
||||
CLK_IS_CRITICAL, CCU_DIV_BASIC | 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_CLK, "sys_xgmac_clk",
|
||||
"eth_clk", CCU_SYS_XGMAC_BASE, 1),
|
||||
CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_REF_CLK, "sys_xgmac_ref_clk",
|
||||
"sys_xgmac_clk", 8),
|
||||
CCU_DIV_FIXED_INFO(CCU_SYS_XGMAC_PTP_CLK, "sys_xgmac_ptp_clk",
|
||||
"sys_xgmac_clk", 8),
|
||||
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, CCU_DIV_BASIC),
|
||||
CCU_DIV_VAR_INFO(CCU_SYS_TIMER1_CLK, "sys_timer1_clk",
|
||||
"ref_clk", CCU_SYS_TIMER1_BASE, 17,
|
||||
CLK_SET_RATE_GATE, CCU_DIV_BASIC),
|
||||
CCU_DIV_VAR_INFO(CCU_SYS_TIMER2_CLK, "sys_timer2_clk",
|
||||
"ref_clk", CCU_SYS_TIMER2_BASE, 17,
|
||||
CLK_SET_RATE_GATE, CCU_DIV_BASIC),
|
||||
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 struct ccu_div_data *axi_data;
|
||||
static struct ccu_div_data *sys_data;
|
||||
|
||||
static void ccu_div_set_data(struct ccu_div_data *data)
|
||||
{
|
||||
struct device_node *np = data->np;
|
||||
|
||||
if (of_device_is_compatible(np, "baikal,bt1-ccu-axi"))
|
||||
axi_data = data;
|
||||
else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys"))
|
||||
sys_data = data;
|
||||
else
|
||||
pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np));
|
||||
}
|
||||
|
||||
static struct ccu_div_data *ccu_div_get_data(struct device_node *np)
|
||||
{
|
||||
if (of_device_is_compatible(np, "baikal,bt1-ccu-axi"))
|
||||
return axi_data;
|
||||
else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys"))
|
||||
return sys_data;
|
||||
|
||||
pr_err("Invalid DT node '%s' specified\n", of_node_full_name(np));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct ccu_div *ccu_div_find_desc(struct ccu_div_data *data,
|
||||
unsigned int clk_id)
|
||||
{
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < data->divs_num; ++idx) {
|
||||
if (data->divs_info[idx].id == clk_id)
|
||||
return data->divs[idx];
|
||||
}
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static struct ccu_div_data *ccu_div_create_data(struct device_node *np)
|
||||
{
|
||||
struct ccu_div_data *data;
|
||||
int ret;
|
||||
|
||||
data = kzalloc_obj(*data);
|
||||
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;
|
||||
} else if (of_device_is_compatible(np, "baikal,bt1-ccu-sys")) {
|
||||
data->divs_num = ARRAY_SIZE(sys_info);
|
||||
data->divs_info = sys_info;
|
||||
} else {
|
||||
pr_err("Incompatible DT node '%s' specified\n",
|
||||
of_node_full_name(np));
|
||||
ret = -EINVAL;
|
||||
goto err_kfree_data;
|
||||
}
|
||||
|
||||
data->divs = kzalloc_objs(*data->divs, data->divs_num);
|
||||
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)) {
|
||||
if (div != ERR_PTR(-EPROBE_DEFER))
|
||||
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, bool defer)
|
||||
{
|
||||
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};
|
||||
|
||||
if (!!(info->features & CCU_DIV_BASIC) ^ defer) {
|
||||
if (!data->divs[idx])
|
||||
data->divs[idx] = ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
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 if (init.type == CCU_DIV_BUF) {
|
||||
init.base = info->base;
|
||||
init.sys_regs = data->sys_regs;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_hw_unregister:
|
||||
for (--idx; idx >= 0; --idx) {
|
||||
if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer)
|
||||
continue;
|
||||
|
||||
ccu_div_hw_unregister(data->divs[idx]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ccu_div_clk_unregister(struct ccu_div_data *data, bool defer)
|
||||
{
|
||||
int idx;
|
||||
|
||||
/* Uninstall only the clocks registered on the specified stage */
|
||||
for (idx = 0; idx < data->divs_num; ++idx) {
|
||||
if (!!(data->divs_info[idx].features & CCU_DIV_BASIC) ^ defer)
|
||||
continue;
|
||||
|
||||
ccu_div_hw_unregister(data->divs[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
static int ccu_div_of_register(struct ccu_div_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ccu_div_rst_register(struct ccu_div_data *data)
|
||||
{
|
||||
struct ccu_rst_init_data init = {0};
|
||||
|
||||
init.sys_regs = data->sys_regs;
|
||||
init.np = data->np;
|
||||
|
||||
data->rsts = ccu_rst_hw_register(&init);
|
||||
if (IS_ERR(data->rsts)) {
|
||||
pr_err("Couldn't register divider '%s' reset controller\n",
|
||||
of_node_full_name(data->np));
|
||||
return PTR_ERR(data->rsts);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ccu_div_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ccu_div_data *data;
|
||||
int ret;
|
||||
|
||||
data = ccu_div_get_data(dev_of_node(&pdev->dev));
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ccu_div_clk_register(data, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ccu_div_rst_register(data);
|
||||
if (ret)
|
||||
goto err_clk_unregister;
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk_unregister:
|
||||
ccu_div_clk_unregister(data, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id ccu_div_of_match[] = {
|
||||
{ .compatible = "baikal,bt1-ccu-axi" },
|
||||
{ .compatible = "baikal,bt1-ccu-sys" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver ccu_div_driver = {
|
||||
.probe = ccu_div_probe,
|
||||
.driver = {
|
||||
.name = "clk-ccu-div",
|
||||
.of_match_table = ccu_div_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver(ccu_div_driver);
|
||||
|
||||
static __init 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, true);
|
||||
if (ret)
|
||||
goto err_free_data;
|
||||
|
||||
ret = ccu_div_of_register(data);
|
||||
if (ret)
|
||||
goto err_clk_unregister;
|
||||
|
||||
ccu_div_set_data(data);
|
||||
|
||||
return;
|
||||
|
||||
err_clk_unregister:
|
||||
ccu_div_clk_unregister(data, true);
|
||||
|
||||
err_free_data:
|
||||
ccu_div_free_data(data);
|
||||
}
|
||||
CLK_OF_DECLARE_DRIVER(ccu_axi, "baikal,bt1-ccu-axi", ccu_div_init);
|
||||
CLK_OF_DECLARE_DRIVER(ccu_sys, "baikal,bt1-ccu-sys", ccu_div_init);
|
||||
|
|
@ -1,277 +0,0 @@
|
|||
// 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/platform_device.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, _features) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _pname, \
|
||||
.base = _base, \
|
||||
.flags = _flags, \
|
||||
.features = _features, \
|
||||
}
|
||||
|
||||
#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;
|
||||
unsigned long features;
|
||||
};
|
||||
|
||||
/*
|
||||
* Alas we have to mark all PLLs as critical. 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. Moreover disabling SATA and Ethernet PLLs causes automatic reset
|
||||
* of the corresponding subsystems. So until we aren't ready to re-initialize
|
||||
* all the devices consuming those PLLs, they will be marked as critical too.
|
||||
*/
|
||||
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_BASIC),
|
||||
CCU_PLL_INFO(CCU_SATA_PLL, "sata_pll", "ref_clk", CCU_SATA_PLL_BASE,
|
||||
CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0),
|
||||
CCU_PLL_INFO(CCU_DDR_PLL, "ddr_pll", "ref_clk", CCU_DDR_PLL_BASE,
|
||||
CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0),
|
||||
CCU_PLL_INFO(CCU_PCIE_PLL, "pcie_pll", "ref_clk", CCU_PCIE_PLL_BASE,
|
||||
CLK_IS_CRITICAL, CCU_PLL_BASIC),
|
||||
CCU_PLL_INFO(CCU_ETH_PLL, "eth_pll", "ref_clk", CCU_ETH_PLL_BASE,
|
||||
CLK_IS_CRITICAL | CLK_SET_RATE_GATE, 0)
|
||||
};
|
||||
|
||||
struct ccu_pll_data {
|
||||
struct device_node *np;
|
||||
struct regmap *sys_regs;
|
||||
struct ccu_pll *plls[CCU_PLL_NUM];
|
||||
};
|
||||
|
||||
static struct ccu_pll_data *pll_data;
|
||||
|
||||
static struct ccu_pll *ccu_pll_find_desc(struct ccu_pll_data *data,
|
||||
unsigned int clk_id)
|
||||
{
|
||||
int idx;
|
||||
|
||||
for (idx = 0; idx < CCU_PLL_NUM; ++idx) {
|
||||
if (pll_info[idx].id == clk_id)
|
||||
return data->plls[idx];
|
||||
}
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static struct ccu_pll_data *ccu_pll_create_data(struct device_node *np)
|
||||
{
|
||||
struct ccu_pll_data *data;
|
||||
|
||||
data = kzalloc_obj(*data);
|
||||
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)) {
|
||||
if (pll != ERR_PTR(-EPROBE_DEFER))
|
||||
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, bool defer)
|
||||
{
|
||||
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};
|
||||
|
||||
/* Defer non-basic PLLs allocation for the probe stage */
|
||||
if (!!(info->features & CCU_PLL_BASIC) ^ defer) {
|
||||
if (!data->plls[idx])
|
||||
data->plls[idx] = ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
init.features = info->features;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_hw_unregister:
|
||||
for (--idx; idx >= 0; --idx) {
|
||||
if (!!(pll_info[idx].features & CCU_PLL_BASIC) ^ defer)
|
||||
continue;
|
||||
|
||||
ccu_pll_hw_unregister(data->plls[idx]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ccu_pll_clk_unregister(struct ccu_pll_data *data, bool defer)
|
||||
{
|
||||
int idx;
|
||||
|
||||
/* Uninstall only the clocks registered on the specified stage */
|
||||
for (idx = 0; idx < CCU_PLL_NUM; ++idx) {
|
||||
if (!!(pll_info[idx].features & CCU_PLL_BASIC) ^ defer)
|
||||
continue;
|
||||
|
||||
ccu_pll_hw_unregister(data->plls[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
static int ccu_pll_of_register(struct ccu_pll_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ccu_pll_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ccu_pll_data *data = pll_data;
|
||||
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
return ccu_pll_clk_register(data, false);
|
||||
}
|
||||
|
||||
static const struct of_device_id ccu_pll_of_match[] = {
|
||||
{ .compatible = "baikal,bt1-ccu-pll" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver ccu_pll_driver = {
|
||||
.probe = ccu_pll_probe,
|
||||
.driver = {
|
||||
.name = "clk-ccu-pll",
|
||||
.of_match_table = ccu_pll_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver(ccu_pll_driver);
|
||||
|
||||
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, true);
|
||||
if (ret)
|
||||
goto err_free_data;
|
||||
|
||||
ret = ccu_pll_of_register(data);
|
||||
if (ret)
|
||||
goto err_clk_unregister;
|
||||
|
||||
pll_data = data;
|
||||
|
||||
return;
|
||||
|
||||
err_clk_unregister:
|
||||
ccu_pll_clk_unregister(data, true);
|
||||
|
||||
err_free_data:
|
||||
ccu_pll_free_data(data);
|
||||
}
|
||||
CLK_OF_DECLARE_DRIVER(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init);
|
||||
|
|
@ -289,16 +289,31 @@ static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw,
|
|||
static int raspberrypi_fw_prepare(struct clk_hw *hw)
|
||||
{
|
||||
const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
|
||||
struct raspberrypi_clk_variant *variant = data->variant;
|
||||
struct raspberrypi_clk *rpi = data->rpi;
|
||||
u32 state = RPI_FIRMWARE_STATE_ENABLE_BIT;
|
||||
int ret;
|
||||
|
||||
ret = raspberrypi_clock_property(rpi->firmware, data,
|
||||
RPI_FIRMWARE_SET_CLOCK_STATE, &state);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err_ratelimited(rpi->dev,
|
||||
"Failed to set clock %s state to on: %d\n",
|
||||
clk_hw_get_name(hw), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* For clocks marked with 'maximize', restore the rate to the
|
||||
* maximum after enabling. This compensates for the rate being
|
||||
* set to minimum during unprepare (see raspberrypi_fw_unprepare).
|
||||
*/
|
||||
if (variant->maximize) {
|
||||
unsigned long min_rate, max_rate;
|
||||
|
||||
clk_hw_get_rate_range(hw, &min_rate, &max_rate);
|
||||
ret = raspberrypi_fw_set_rate(hw, max_rate, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -307,9 +322,27 @@ static void raspberrypi_fw_unprepare(struct clk_hw *hw)
|
|||
{
|
||||
const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
|
||||
struct raspberrypi_clk *rpi = data->rpi;
|
||||
unsigned long min_rate, max_rate;
|
||||
u32 state = 0;
|
||||
int ret;
|
||||
|
||||
clk_hw_get_rate_range(hw, &min_rate, &max_rate);
|
||||
|
||||
/*
|
||||
* Setting the rate in unprepare is a deviation from the usual CCF
|
||||
* behavior, where unprepare only gates the clock. However, this is
|
||||
* needed, as RPI_FIRMWARE_SET_CLOCK_STATE doesn't actually power off
|
||||
* the clock on current firmware versions. Setting the rate to minimum
|
||||
* before disabling the clock is the only way to achieve meaningful
|
||||
* power savings.
|
||||
*
|
||||
* This is safe because no consumer should rely on the rate of an
|
||||
* unprepared clock. Any consumer must call clk_prepare() before use,
|
||||
* at which point the rate is either restored to maximum (for clocks
|
||||
* with the 'maximize' flag) or re-established by the consumer.
|
||||
*/
|
||||
raspberrypi_fw_set_rate(hw, min_rate, 0);
|
||||
|
||||
ret = raspberrypi_clock_property(rpi->firmware, data,
|
||||
RPI_FIRMWARE_SET_CLOCK_STATE, &state);
|
||||
if (ret)
|
||||
|
|
@ -387,9 +420,6 @@ static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi,
|
|||
}
|
||||
}
|
||||
|
||||
if (variant->maximize)
|
||||
variant->min_rate = max_rate;
|
||||
|
||||
if (variant->min_rate) {
|
||||
unsigned long rate;
|
||||
|
||||
|
|
|
|||
|
|
@ -47,22 +47,10 @@ static int clk_composite_determine_rate_for_parent(struct clk_hw *rate_hw,
|
|||
struct clk_hw *parent_hw,
|
||||
const struct clk_ops *rate_ops)
|
||||
{
|
||||
long rate;
|
||||
|
||||
req->best_parent_hw = parent_hw;
|
||||
req->best_parent_rate = clk_hw_get_rate(parent_hw);
|
||||
|
||||
if (rate_ops->determine_rate)
|
||||
return rate_ops->determine_rate(rate_hw, req);
|
||||
|
||||
rate = rate_ops->round_rate(rate_hw, req->rate,
|
||||
&req->best_parent_rate);
|
||||
if (rate < 0)
|
||||
return rate;
|
||||
|
||||
req->rate = rate;
|
||||
|
||||
return 0;
|
||||
return rate_ops->determine_rate(rate_hw, req);
|
||||
}
|
||||
|
||||
static int clk_composite_determine_rate(struct clk_hw *hw,
|
||||
|
|
@ -79,8 +67,7 @@ static int clk_composite_determine_rate(struct clk_hw *hw,
|
|||
unsigned long best_rate = 0;
|
||||
int i, ret;
|
||||
|
||||
if (rate_hw && rate_ops &&
|
||||
(rate_ops->determine_rate || rate_ops->round_rate) &&
|
||||
if (rate_hw && rate_ops && rate_ops->determine_rate &&
|
||||
mux_hw && mux_ops && mux_ops->set_parent) {
|
||||
req->best_parent_hw = NULL;
|
||||
|
||||
|
|
@ -150,18 +137,6 @@ static int clk_composite_determine_rate(struct clk_hw *hw,
|
|||
}
|
||||
}
|
||||
|
||||
static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct clk_composite *composite = to_clk_composite(hw);
|
||||
const struct clk_ops *rate_ops = composite->rate_ops;
|
||||
struct clk_hw *rate_hw = composite->rate_hw;
|
||||
|
||||
__clk_hw_set_clk(rate_hw, hw);
|
||||
|
||||
return rate_ops->round_rate(rate_hw, rate, prate);
|
||||
}
|
||||
|
||||
static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
|
|
@ -288,17 +263,14 @@ static struct clk_hw *__clk_hw_register_composite(struct device *dev,
|
|||
if (rate_ops->determine_rate)
|
||||
clk_composite_ops->determine_rate =
|
||||
clk_composite_determine_rate;
|
||||
else if (rate_ops->round_rate)
|
||||
clk_composite_ops->round_rate =
|
||||
clk_composite_round_rate;
|
||||
|
||||
/* .set_rate requires either .round_rate or .determine_rate */
|
||||
/* .set_rate requires .determine_rate */
|
||||
if (rate_ops->set_rate) {
|
||||
if (rate_ops->determine_rate || rate_ops->round_rate)
|
||||
if (rate_ops->determine_rate)
|
||||
clk_composite_ops->set_rate =
|
||||
clk_composite_set_rate;
|
||||
else
|
||||
WARN(1, "%s: missing round_rate op is required\n",
|
||||
WARN(1, "%s: missing determine_rate op is required\n",
|
||||
__func__);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -387,50 +387,6 @@ int divider_ro_determine_rate(struct clk_hw *hw, struct clk_rate_request *req,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(divider_ro_determine_rate);
|
||||
|
||||
long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
|
||||
unsigned long rate, unsigned long *prate,
|
||||
const struct clk_div_table *table,
|
||||
u8 width, unsigned long flags)
|
||||
{
|
||||
struct clk_rate_request req;
|
||||
int ret;
|
||||
|
||||
clk_hw_init_rate_request(hw, &req, rate);
|
||||
req.best_parent_rate = *prate;
|
||||
req.best_parent_hw = parent;
|
||||
|
||||
ret = divider_determine_rate(hw, &req, table, width, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*prate = req.best_parent_rate;
|
||||
|
||||
return req.rate;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(divider_round_rate_parent);
|
||||
|
||||
long divider_ro_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
|
||||
unsigned long rate, unsigned long *prate,
|
||||
const struct clk_div_table *table, u8 width,
|
||||
unsigned long flags, unsigned int val)
|
||||
{
|
||||
struct clk_rate_request req;
|
||||
int ret;
|
||||
|
||||
clk_hw_init_rate_request(hw, &req, rate);
|
||||
req.best_parent_rate = *prate;
|
||||
req.best_parent_hw = parent;
|
||||
|
||||
ret = divider_ro_determine_rate(hw, &req, table, width, flags, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*prate = req.best_parent_rate;
|
||||
|
||||
return req.rate;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(divider_ro_round_rate_parent);
|
||||
|
||||
static int clk_divider_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
|
|
@ -11,6 +12,8 @@
|
|||
#include <dt-bindings/clock/en7523-clk.h>
|
||||
#include <dt-bindings/reset/airoha,en7523-reset.h>
|
||||
#include <dt-bindings/reset/airoha,en7581-reset.h>
|
||||
#include <dt-bindings/clock/econet,en751221-scu.h>
|
||||
#include <dt-bindings/reset/econet,en751221-scu.h>
|
||||
|
||||
#define RST_NR_PER_BANK 32
|
||||
|
||||
|
|
@ -33,15 +36,50 @@
|
|||
#define REG_RESET_CONTROL_PCIEHB BIT(29)
|
||||
#define REG_RESET_CONTROL_PCIE1 BIT(27)
|
||||
#define REG_RESET_CONTROL_PCIE2 BIT(26)
|
||||
#define REG_HIR 0x064
|
||||
#define REG_HIR_MASK GENMASK(31, 16)
|
||||
/* EN7581 */
|
||||
#define REG_NP_SCU_PCIC 0x88
|
||||
#define REG_NP_SCU_SSTR 0x9c
|
||||
#define REG_PCIE_XSI0_SEL_MASK GENMASK(14, 13)
|
||||
#define REG_PCIE_XSI1_SEL_MASK GENMASK(12, 11)
|
||||
#define REG_CRYPTO_CLKSRC2 0x20c
|
||||
/* EN751221 */
|
||||
#define EN751221_REG_SPI_DIV 0x0cc
|
||||
#define EN751221_REG_SPI_DIV_MASK GENMASK(31, 8)
|
||||
#define EN751221_SPI_BASE 500000000
|
||||
#define EN751221_SPI_BASE_EN7526C 400000000
|
||||
#define EN751221_SPI_DIV_DEFAULT 40
|
||||
#define EN751221_REG_BUS 0x284
|
||||
#define EN751221_REG_BUS_MASK GENMASK(21, 12)
|
||||
#define EN751221_REG_SSR3 0x094
|
||||
#define EN751221_REG_SSR3_GSW_MASK GENMASK(9, 8)
|
||||
|
||||
#define REG_RST_CTRL2 0x830
|
||||
#define REG_RST_CTRL1 0x834
|
||||
#define EN751221_REG_RST_DMT 0x84
|
||||
#define EN751221_REG_RST_USB 0xec
|
||||
|
||||
#define EN751221_MAX_CLKS 5
|
||||
|
||||
enum en_hir {
|
||||
HIR_UNKNOWN = -1,
|
||||
HIR_TC3169 = 0,
|
||||
HIR_TC3182 = 1,
|
||||
HIR_RT65168 = 2,
|
||||
HIR_RT63165 = 3,
|
||||
HIR_RT63365 = 4,
|
||||
HIR_MT751020 = 5,
|
||||
HIR_MT7505 = 6,
|
||||
HIR_EN751221 = 7,
|
||||
HIR_EN7526C = 8,
|
||||
HIR_EN751627 = 9,
|
||||
HIR_EN7580 = 10,
|
||||
HIR_EN7528 = 11,
|
||||
HIR_EN7523 = 12,
|
||||
HIR_EN7581 = 13,
|
||||
HIR_MAX = 14,
|
||||
};
|
||||
|
||||
struct en_clk_desc {
|
||||
int id;
|
||||
|
|
@ -93,6 +131,8 @@ static const u32 bus7581_base[] = { 600000000, 540000000 };
|
|||
static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 };
|
||||
static const u32 crypto_base[] = { 540000000, 480000000 };
|
||||
static const u32 emmc7581_base[] = { 200000000, 150000000 };
|
||||
/* EN751221 */
|
||||
static const u32 gsw751221_base[] = { 500000000, 250000000, 400000000, 200000000 };
|
||||
|
||||
static const struct en_clk_desc en7523_base_clks[] = {
|
||||
{
|
||||
|
|
@ -300,6 +340,13 @@ static const u16 en7581_rst_ofs[] = {
|
|||
REG_RST_CTRL1,
|
||||
};
|
||||
|
||||
static const u16 en751221_rst_ofs[] = {
|
||||
REG_RST_CTRL2,
|
||||
REG_RST_CTRL1,
|
||||
EN751221_REG_RST_DMT,
|
||||
EN751221_REG_RST_USB,
|
||||
};
|
||||
|
||||
static const u16 en7523_rst_map[] = {
|
||||
/* RST_CTRL2 */
|
||||
[EN7523_XPON_PHY_RST] = 0,
|
||||
|
|
@ -405,8 +452,61 @@ static const u16 en7581_rst_map[] = {
|
|||
[EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31,
|
||||
};
|
||||
|
||||
static const u16 en751221_rst_map[] = {
|
||||
/* RST_CTRL2 */
|
||||
[EN751221_XPON_PHY_RST] = 0,
|
||||
[EN751221_GFAST_RST] = 1,
|
||||
[EN751221_CPU_TIMER2_RST] = 2,
|
||||
[EN751221_UART3_RST] = 3,
|
||||
[EN751221_UART4_RST] = 4,
|
||||
[EN751221_UART5_RST] = 5,
|
||||
[EN751221_I2C2_RST] = 6,
|
||||
[EN751221_XSI_MAC_RST] = 7,
|
||||
[EN751221_XSI_PHY_RST] = 8,
|
||||
|
||||
/* RST_CTRL1 */
|
||||
[EN751221_PCM1_ZSI_ISI_RST] = RST_NR_PER_BANK + 0,
|
||||
[EN751221_FE_QDMA1_RST] = RST_NR_PER_BANK + 1,
|
||||
[EN751221_FE_QDMA2_RST] = RST_NR_PER_BANK + 2,
|
||||
[EN751221_FE_UNZIP_RST] = RST_NR_PER_BANK + 3,
|
||||
[EN751221_PCM2_RST] = RST_NR_PER_BANK + 4,
|
||||
[EN751221_PTM_MAC_RST] = RST_NR_PER_BANK + 5,
|
||||
[EN751221_CRYPTO_RST] = RST_NR_PER_BANK + 6,
|
||||
[EN751221_SAR_RST] = RST_NR_PER_BANK + 7,
|
||||
[EN751221_TIMER_RST] = RST_NR_PER_BANK + 8,
|
||||
[EN751221_INTC_RST] = RST_NR_PER_BANK + 9,
|
||||
[EN751221_BONDING_RST] = RST_NR_PER_BANK + 10,
|
||||
[EN751221_PCM1_RST] = RST_NR_PER_BANK + 11,
|
||||
[EN751221_UART_RST] = RST_NR_PER_BANK + 12,
|
||||
[EN751221_GPIO_RST] = RST_NR_PER_BANK + 13,
|
||||
[EN751221_GDMA_RST] = RST_NR_PER_BANK + 14,
|
||||
[EN751221_I2C_MASTER_RST] = RST_NR_PER_BANK + 16,
|
||||
[EN751221_PCM2_ZSI_ISI_RST] = RST_NR_PER_BANK + 17,
|
||||
[EN751221_SFC_RST] = RST_NR_PER_BANK + 18,
|
||||
[EN751221_UART2_RST] = RST_NR_PER_BANK + 19,
|
||||
[EN751221_GDMP_RST] = RST_NR_PER_BANK + 20,
|
||||
[EN751221_FE_RST] = RST_NR_PER_BANK + 21,
|
||||
[EN751221_USB_HOST_P0_RST] = RST_NR_PER_BANK + 22,
|
||||
[EN751221_GSW_RST] = RST_NR_PER_BANK + 23,
|
||||
[EN751221_SFC2_PCM_RST] = RST_NR_PER_BANK + 25,
|
||||
[EN751221_PCIE0_RST] = RST_NR_PER_BANK + 26,
|
||||
[EN751221_PCIE1_RST] = RST_NR_PER_BANK + 27,
|
||||
[EN751221_CPU_TIMER_RST] = RST_NR_PER_BANK + 28,
|
||||
[EN751221_PCIE_HB_RST] = RST_NR_PER_BANK + 29,
|
||||
[EN751221_SIMIF_RST] = RST_NR_PER_BANK + 30,
|
||||
[EN751221_XPON_MAC_RST] = RST_NR_PER_BANK + 31,
|
||||
|
||||
/* RST_DMT */
|
||||
[EN751221_DMT_RST] = 2 * RST_NR_PER_BANK + 0,
|
||||
|
||||
/* RST_USB */
|
||||
[EN751221_USB_PHY_P0_RST] = 3 * RST_NR_PER_BANK + 6,
|
||||
[EN751221_USB_PHY_P1_RST] = 3 * RST_NR_PER_BANK + 7,
|
||||
};
|
||||
|
||||
static int en7581_reset_register(struct device *dev, void __iomem *base,
|
||||
const u16 *rst_map, int nr_resets);
|
||||
const u16 *rst_map, int nr_resets,
|
||||
const u16 *rst_reg_ofs);
|
||||
|
||||
static u32 en7523_get_base_rate(const struct en_clk_desc *desc, u32 val)
|
||||
{
|
||||
|
|
@ -604,7 +704,8 @@ static int en7523_clk_hw_init(struct platform_device *pdev,
|
|||
en7523_register_clocks(&pdev->dev, clk_data, base, np_base);
|
||||
|
||||
return en7581_reset_register(&pdev->dev, np_base, en7523_rst_map,
|
||||
ARRAY_SIZE(en7523_rst_map));
|
||||
ARRAY_SIZE(en7523_rst_map),
|
||||
en7581_rst_ofs);
|
||||
}
|
||||
|
||||
static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data,
|
||||
|
|
@ -705,7 +806,8 @@ static const struct reset_control_ops en7581_reset_ops = {
|
|||
};
|
||||
|
||||
static int en7581_reset_register(struct device *dev, void __iomem *base,
|
||||
const u16 *rst_map, int nr_resets)
|
||||
const u16 *rst_map, int nr_resets,
|
||||
const u16 *rst_reg_ofs)
|
||||
{
|
||||
struct en_rst_data *rst_data;
|
||||
|
||||
|
|
@ -713,7 +815,7 @@ static int en7581_reset_register(struct device *dev, void __iomem *base,
|
|||
if (!rst_data)
|
||||
return -ENOMEM;
|
||||
|
||||
rst_data->bank_ofs = en7581_rst_ofs;
|
||||
rst_data->bank_ofs = rst_reg_ofs;
|
||||
rst_data->idx_map = rst_map;
|
||||
rst_data->base = base;
|
||||
|
||||
|
|
@ -752,7 +854,107 @@ static int en7581_clk_hw_init(struct platform_device *pdev,
|
|||
writel(val | 3, base + REG_NP_SCU_PCIC);
|
||||
|
||||
return en7581_reset_register(&pdev->dev, base, en7581_rst_map,
|
||||
ARRAY_SIZE(en7581_rst_map));
|
||||
ARRAY_SIZE(en7581_rst_map),
|
||||
en7581_rst_ofs);
|
||||
}
|
||||
|
||||
static enum en_hir get_hw_id(void __iomem *np_base)
|
||||
{
|
||||
u32 val = FIELD_GET(REG_HIR_MASK, readl(np_base + REG_HIR));
|
||||
|
||||
if (val < HIR_MAX)
|
||||
return (enum en_hir)val;
|
||||
|
||||
pr_warn("Unable to determine EcoNet SoC\n");
|
||||
|
||||
return HIR_UNKNOWN;
|
||||
}
|
||||
|
||||
static void en751221_try_register_clk(struct device *dev, int key,
|
||||
struct clk_hw_onecell_data *clk_data,
|
||||
const char *name, u32 rate)
|
||||
{
|
||||
struct clk_hw *hw;
|
||||
|
||||
if (WARN_ON_ONCE(key >= EN751221_MAX_CLKS))
|
||||
return;
|
||||
|
||||
hw = clk_hw_register_fixed_rate(dev, name, NULL, 0, rate);
|
||||
if (IS_ERR(hw))
|
||||
pr_err("Failed to register clk %s: %pe\n", name, hw);
|
||||
else
|
||||
clk_data->hws[key] = hw;
|
||||
}
|
||||
|
||||
static void en751221_register_clocks(struct device *dev,
|
||||
struct clk_hw_onecell_data *clk_data,
|
||||
struct regmap *map, void __iomem *np_base)
|
||||
{
|
||||
enum en_hir hid = get_hw_id(np_base);
|
||||
struct clk_hw *hw;
|
||||
u32 rate;
|
||||
u32 div;
|
||||
int err;
|
||||
|
||||
/* PCI */
|
||||
hw = en7523_register_pcie_clk(dev, np_base);
|
||||
clk_data->hws[EN751221_CLK_PCIE] = hw;
|
||||
|
||||
/* SPI */
|
||||
rate = EN751221_SPI_BASE;
|
||||
if (hid == HIR_EN7526C)
|
||||
rate = EN751221_SPI_BASE_EN7526C;
|
||||
|
||||
err = regmap_read(map, EN751221_REG_SPI_DIV, &div);
|
||||
if (err) {
|
||||
pr_err("Failed reading fixed clk div %s: %d\n",
|
||||
"spi", err);
|
||||
} else {
|
||||
div = FIELD_GET(EN751221_REG_SPI_DIV_MASK, div) * 2;
|
||||
if (!div)
|
||||
div = EN751221_SPI_DIV_DEFAULT;
|
||||
|
||||
en751221_try_register_clk(dev, EN751221_CLK_SPI, clk_data,
|
||||
"spi", rate / div);
|
||||
}
|
||||
|
||||
/* BUS */
|
||||
rate = FIELD_GET(EN751221_REG_BUS_MASK,
|
||||
readl(np_base + EN751221_REG_BUS));
|
||||
rate *= 1000000;
|
||||
en751221_try_register_clk(dev, EN751221_CLK_BUS, clk_data, "bus",
|
||||
rate);
|
||||
|
||||
/* CPU */
|
||||
en751221_try_register_clk(dev, EN751221_CLK_CPU, clk_data, "cpu",
|
||||
rate * 4);
|
||||
|
||||
/* GSW */
|
||||
rate = FIELD_GET(EN751221_REG_SSR3_GSW_MASK,
|
||||
readl(np_base + EN751221_REG_SSR3));
|
||||
en751221_try_register_clk(dev, EN751221_CLK_GSW, clk_data, "gsw",
|
||||
gsw751221_base[rate]);
|
||||
}
|
||||
|
||||
static int en751221_clk_hw_init(struct platform_device *pdev,
|
||||
struct clk_hw_onecell_data *clk_data)
|
||||
{
|
||||
struct regmap *map;
|
||||
void __iomem *base;
|
||||
|
||||
map = syscon_regmap_lookup_by_compatible("econet,en751221-chip-scu");
|
||||
if (IS_ERR(map))
|
||||
return PTR_ERR(map);
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
en751221_register_clocks(&pdev->dev, clk_data, map, base);
|
||||
|
||||
return en7581_reset_register(&pdev->dev, base, en751221_rst_map,
|
||||
ARRAY_SIZE(en751221_rst_map),
|
||||
en751221_rst_ofs);
|
||||
}
|
||||
|
||||
static int en7523_clk_probe(struct platform_device *pdev)
|
||||
|
|
@ -799,9 +1001,20 @@ static const struct en_clk_soc_data en7581_data = {
|
|||
.hw_init = en7581_clk_hw_init,
|
||||
};
|
||||
|
||||
static const struct en_clk_soc_data en751221_data = {
|
||||
.num_clocks = EN751221_MAX_CLKS,
|
||||
.pcie_ops = {
|
||||
.is_enabled = en7523_pci_is_enabled,
|
||||
.prepare = en7523_pci_prepare,
|
||||
.unprepare = en7523_pci_unprepare,
|
||||
},
|
||||
.hw_init = en751221_clk_hw_init,
|
||||
};
|
||||
|
||||
static const struct of_device_id of_match_clk_en7523[] = {
|
||||
{ .compatible = "airoha,en7523-scu", .data = &en7523_data },
|
||||
{ .compatible = "airoha,en7581-scu", .data = &en7581_data },
|
||||
{ .compatible = "econet,en751221-scu", .data = &en751221_data },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,34 +5,110 @@
|
|||
* Copyright 2020 Michael Walle <michael@walle.cc>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define I2S_CSR 0x00
|
||||
#define I2S_CR2 0x08
|
||||
#define I2S_MCR 0x100
|
||||
#define CSR_BCE_BIT 28
|
||||
#define CSR_TE_BIT 31
|
||||
#define CR2_BCD BIT(24)
|
||||
#define CR2_DIV_SHIFT 0
|
||||
#define CR2_DIV_WIDTH 8
|
||||
#define MCR_MOE BIT(30)
|
||||
|
||||
struct fsl_sai_data {
|
||||
unsigned int offset; /* Register offset */
|
||||
bool have_mclk; /* Have MCLK control */
|
||||
};
|
||||
|
||||
struct fsl_sai_clk {
|
||||
struct clk_divider div;
|
||||
struct clk_gate gate;
|
||||
const struct fsl_sai_data *data;
|
||||
struct clk_divider bclk_div;
|
||||
struct clk_divider mclk_div;
|
||||
struct clk_gate bclk_gate;
|
||||
struct clk_gate mclk_gate;
|
||||
struct clk_hw *bclk_hw;
|
||||
struct clk_hw *mclk_hw;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static struct clk_hw *
|
||||
fsl_sai_of_clk_get(struct of_phandle_args *clkspec, void *data)
|
||||
{
|
||||
struct fsl_sai_clk *sai_clk = data;
|
||||
|
||||
if (clkspec->args_count == 0)
|
||||
return sai_clk->bclk_hw;
|
||||
|
||||
if (clkspec->args_count == 1) {
|
||||
if (clkspec->args[0] == 0)
|
||||
return sai_clk->bclk_hw;
|
||||
if (sai_clk->data->have_mclk && clkspec->args[0] == 1)
|
||||
return sai_clk->mclk_hw;
|
||||
}
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static int fsl_sai_clk_register(struct device *dev, void __iomem *base,
|
||||
spinlock_t *lock, struct clk_divider *div,
|
||||
struct clk_gate *gate, struct clk_hw **hw,
|
||||
const int gate_bit, const int dir_bit,
|
||||
const int div_reg, char *name)
|
||||
{
|
||||
const struct fsl_sai_data *data = device_get_match_data(dev);
|
||||
struct clk_parent_data pdata = { .index = 0 };
|
||||
struct clk_hw *chw;
|
||||
char *cname;
|
||||
|
||||
gate->reg = base + data->offset + I2S_CSR;
|
||||
gate->bit_idx = gate_bit;
|
||||
gate->lock = lock;
|
||||
|
||||
div->reg = base + div_reg;
|
||||
div->shift = CR2_DIV_SHIFT;
|
||||
div->width = CR2_DIV_WIDTH;
|
||||
div->lock = lock;
|
||||
|
||||
cname = devm_kasprintf(dev, GFP_KERNEL, "%s.%s",
|
||||
of_node_full_name(dev->of_node), name);
|
||||
if (!cname)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Set clock direction */
|
||||
writel(dir_bit, base + div_reg);
|
||||
|
||||
chw = devm_clk_hw_register_composite_pdata(dev, cname,
|
||||
&pdata, 1, NULL, NULL,
|
||||
&div->hw,
|
||||
&clk_divider_ops,
|
||||
&gate->hw,
|
||||
&clk_gate_ops,
|
||||
CLK_SET_RATE_GATE);
|
||||
if (IS_ERR(chw))
|
||||
return PTR_ERR(chw);
|
||||
|
||||
*hw = chw;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_sai_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct fsl_sai_data *data = device_get_match_data(dev);
|
||||
struct fsl_sai_clk *sai_clk;
|
||||
struct clk_parent_data pdata = { .index = 0 };
|
||||
struct clk *clk_bus;
|
||||
void __iomem *base;
|
||||
struct clk_hw *hw;
|
||||
int ret;
|
||||
|
||||
sai_clk = devm_kzalloc(dev, sizeof(*sai_clk), GFP_KERNEL);
|
||||
if (!sai_clk)
|
||||
|
|
@ -42,35 +118,47 @@ static int fsl_sai_clk_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
clk_bus = devm_clk_get_optional_enabled(dev, "bus");
|
||||
if (IS_ERR(clk_bus))
|
||||
return PTR_ERR(clk_bus);
|
||||
|
||||
sai_clk->data = data;
|
||||
spin_lock_init(&sai_clk->lock);
|
||||
|
||||
sai_clk->gate.reg = base + I2S_CSR;
|
||||
sai_clk->gate.bit_idx = CSR_BCE_BIT;
|
||||
sai_clk->gate.lock = &sai_clk->lock;
|
||||
ret = fsl_sai_clk_register(dev, base, &sai_clk->lock,
|
||||
&sai_clk->bclk_div, &sai_clk->bclk_gate,
|
||||
&sai_clk->bclk_hw, CSR_BCE_BIT, CR2_BCD,
|
||||
data->offset + I2S_CR2, "BCLK");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sai_clk->div.reg = base + I2S_CR2;
|
||||
sai_clk->div.shift = CR2_DIV_SHIFT;
|
||||
sai_clk->div.width = CR2_DIV_WIDTH;
|
||||
sai_clk->div.lock = &sai_clk->lock;
|
||||
if (data->have_mclk) {
|
||||
ret = fsl_sai_clk_register(dev, base, &sai_clk->lock,
|
||||
&sai_clk->mclk_div,
|
||||
&sai_clk->mclk_gate,
|
||||
&sai_clk->mclk_hw,
|
||||
CSR_TE_BIT, MCR_MOE, I2S_MCR,
|
||||
"MCLK");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* set clock direction, we are the BCLK master */
|
||||
writel(CR2_BCD, base + I2S_CR2);
|
||||
|
||||
hw = devm_clk_hw_register_composite_pdata(dev, dev->of_node->name,
|
||||
&pdata, 1, NULL, NULL,
|
||||
&sai_clk->div.hw,
|
||||
&clk_divider_ops,
|
||||
&sai_clk->gate.hw,
|
||||
&clk_gate_ops,
|
||||
CLK_SET_RATE_GATE);
|
||||
if (IS_ERR(hw))
|
||||
return PTR_ERR(hw);
|
||||
|
||||
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
|
||||
return devm_of_clk_add_hw_provider(dev, fsl_sai_of_clk_get, sai_clk);
|
||||
}
|
||||
|
||||
static const struct fsl_sai_data fsl_sai_vf610_data = {
|
||||
.offset = 0,
|
||||
.have_mclk = false,
|
||||
};
|
||||
|
||||
static const struct fsl_sai_data fsl_sai_imx8mq_data = {
|
||||
.offset = 8,
|
||||
.have_mclk = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id of_fsl_sai_clk_ids[] = {
|
||||
{ .compatible = "fsl,vf610-sai-clock" },
|
||||
{ .compatible = "fsl,vf610-sai-clock", .data = &fsl_sai_vf610_data },
|
||||
{ .compatible = "fsl,imx8mq-sai-clock", .data = &fsl_sai_imx8mq_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_fsl_sai_clk_ids);
|
||||
|
|
|
|||
|
|
@ -907,13 +907,11 @@ static const struct clockgen_pll_div *get_pll_div(struct clockgen *cg,
|
|||
return &cg->pll[pll].div[div];
|
||||
}
|
||||
|
||||
static struct clk * __init create_mux_common(struct clockgen *cg,
|
||||
struct mux_hwclock *hwc,
|
||||
const struct clk_ops *ops,
|
||||
unsigned long min_rate,
|
||||
unsigned long max_rate,
|
||||
unsigned long pct80_rate,
|
||||
const char *fmt, int idx)
|
||||
static struct clk * __init __printf(7, 8)
|
||||
create_mux_common(struct clockgen *cg, struct mux_hwclock *hwc,
|
||||
const struct clk_ops *ops, unsigned long min_rate,
|
||||
unsigned long max_rate, unsigned long pct80_rate,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
struct clk_init_data init = {};
|
||||
struct clk *clk;
|
||||
|
|
@ -921,8 +919,11 @@ static struct clk * __init create_mux_common(struct clockgen *cg,
|
|||
const char *parent_names[NUM_MUX_PARENTS];
|
||||
char name[32];
|
||||
int i, j;
|
||||
va_list args;
|
||||
|
||||
snprintf(name, sizeof(name), fmt, idx);
|
||||
va_start(args, fmt);
|
||||
vsnprintf(name, sizeof(name), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
for (i = 0, j = 0; i < NUM_MUX_PARENTS; i++) {
|
||||
unsigned long rate;
|
||||
|
|
|
|||
|
|
@ -188,6 +188,8 @@ static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_ty
|
|||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
clk_register_clkdev(clk, clk_name, NULL);
|
||||
pr_debug("Add %s clock PLL\n", clk_name);
|
||||
} else {
|
||||
iounmap(reg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1560,8 +1560,6 @@ late_initcall_sync(clk_disable_unused);
|
|||
static int clk_core_determine_round_nolock(struct clk_core *core,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
long rate;
|
||||
|
||||
lockdep_assert_held(&prepare_lock);
|
||||
|
||||
if (!core)
|
||||
|
|
@ -1591,13 +1589,6 @@ static int clk_core_determine_round_nolock(struct clk_core *core,
|
|||
req->rate = core->rate;
|
||||
} else if (core->ops->determine_rate) {
|
||||
return core->ops->determine_rate(core->hw, req);
|
||||
} else if (core->ops->round_rate) {
|
||||
rate = core->ops->round_rate(core->hw, req->rate,
|
||||
&req->best_parent_rate);
|
||||
if (rate < 0)
|
||||
return rate;
|
||||
|
||||
req->rate = rate;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -1682,7 +1673,7 @@ EXPORT_SYMBOL_GPL(clk_hw_forward_rate_request);
|
|||
|
||||
static bool clk_core_can_round(struct clk_core * const core)
|
||||
{
|
||||
return core->ops->determine_rate || core->ops->round_rate;
|
||||
return core->ops->determine_rate;
|
||||
}
|
||||
|
||||
static int clk_core_round_rate_nolock(struct clk_core *core,
|
||||
|
|
@ -1750,11 +1741,11 @@ EXPORT_SYMBOL_GPL(__clk_determine_rate);
|
|||
* use.
|
||||
*
|
||||
* Context: prepare_lock must be held.
|
||||
* For clk providers to call from within clk_ops such as .round_rate,
|
||||
* For clk providers to call from within clk_ops such as
|
||||
* .determine_rate.
|
||||
*
|
||||
* Return: returns rounded rate of hw clk if clk supports round_rate operation
|
||||
* else returns the parent rate.
|
||||
* Return: returns rounded rate of hw clk if clk supports determine_rate
|
||||
* operation; else returns the parent rate.
|
||||
*/
|
||||
unsigned long clk_hw_round_rate(struct clk_hw *hw, unsigned long rate)
|
||||
{
|
||||
|
|
@ -2569,12 +2560,13 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
|
|||
*
|
||||
* Setting the CLK_SET_RATE_PARENT flag allows the rate change operation to
|
||||
* propagate up to clk's parent; whether or not this happens depends on the
|
||||
* outcome of clk's .round_rate implementation. If *parent_rate is unchanged
|
||||
* after calling .round_rate then upstream parent propagation is ignored. If
|
||||
* *parent_rate comes back with a new rate for clk's parent then we propagate
|
||||
* up to clk's parent and set its rate. Upward propagation will continue
|
||||
* until either a clk does not support the CLK_SET_RATE_PARENT flag or
|
||||
* .round_rate stops requesting changes to clk's parent_rate.
|
||||
* outcome of clk's .determine_rate implementation. If req->best_parent_rate
|
||||
* is unchanged after calling .determine_rate then upstream parent propagation
|
||||
* is ignored. If req->best_parent_rate comes back with a new rate for clk's
|
||||
* parent then we propagate up to clk's parent and set its rate. Upward
|
||||
* propagation will continue until either a clk does not support the
|
||||
* CLK_SET_RATE_PARENT flag or .determine_rate stops requesting changes to
|
||||
* clk's parent_rate.
|
||||
*
|
||||
* Rate changes are accomplished via tree traversal that also recalculates the
|
||||
* rates for the clocks and fires off POST_RATE_CHANGE notifiers.
|
||||
|
|
@ -2703,8 +2695,6 @@ static int clk_set_rate_range_nolock(struct clk *clk,
|
|||
* FIXME:
|
||||
* There is a catch. It may fail for the usual reason (clock
|
||||
* broken, clock protected, etc) but also because:
|
||||
* - round_rate() was not favorable and fell on the wrong
|
||||
* side of the boundary
|
||||
* - the determine_rate() callback does not really check for
|
||||
* this corner case when determining the rate
|
||||
*/
|
||||
|
|
@ -3259,11 +3249,10 @@ bool clk_is_match(const struct clk *p, const struct clk *q)
|
|||
return true;
|
||||
|
||||
/* true if clk->core pointers match. Avoid dereferencing garbage */
|
||||
if (!IS_ERR_OR_NULL(p) && !IS_ERR_OR_NULL(q))
|
||||
if (p->core == q->core)
|
||||
return true;
|
||||
if (IS_ERR_OR_NULL(p) || IS_ERR_OR_NULL(q))
|
||||
return false;
|
||||
|
||||
return false;
|
||||
return p->core == q->core;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_is_match);
|
||||
|
||||
|
|
@ -3915,10 +3904,9 @@ static int __clk_core_init(struct clk_core *core)
|
|||
}
|
||||
|
||||
/* check that clk_ops are sane. See Documentation/driver-api/clk.rst */
|
||||
if (core->ops->set_rate &&
|
||||
!((core->ops->round_rate || core->ops->determine_rate) &&
|
||||
core->ops->recalc_rate)) {
|
||||
pr_err("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n",
|
||||
if (core->ops->set_rate && !core->ops->determine_rate &&
|
||||
core->ops->recalc_rate) {
|
||||
pr_err("%s: %s must implement .determine_rate in addition to .recalc_rate\n",
|
||||
__func__, core->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
|
|
|
|||
|
|
@ -241,8 +241,8 @@ static void clk_test_get_rate(struct kunit *test)
|
|||
* Test that, after a call to clk_set_rate(), the rate returned by
|
||||
* clk_get_rate() matches.
|
||||
*
|
||||
* This assumes that clk_ops.determine_rate or clk_ops.round_rate won't
|
||||
* modify the requested rate, which is our case in clk_dummy_rate_ops.
|
||||
* This assumes that clk_ops.determine_rate won't modify the requested rate,
|
||||
* which is our case in clk_dummy_rate_ops.
|
||||
*/
|
||||
static void clk_test_set_get_rate(struct kunit *test)
|
||||
{
|
||||
|
|
@ -266,8 +266,8 @@ static void clk_test_set_get_rate(struct kunit *test)
|
|||
* Test that, after several calls to clk_set_rate(), the rate returned
|
||||
* by clk_get_rate() matches the last one.
|
||||
*
|
||||
* This assumes that clk_ops.determine_rate or clk_ops.round_rate won't
|
||||
* modify the requested rate, which is our case in clk_dummy_rate_ops.
|
||||
* This assumes that clk_ops.determine_rate won't modify the requested rate,
|
||||
* which is our case in clk_dummy_rate_ops.
|
||||
*/
|
||||
static void clk_test_set_set_get_rate(struct kunit *test)
|
||||
{
|
||||
|
|
@ -1675,8 +1675,8 @@ static void clk_range_test_set_range_set_round_rate_consistent_higher(struct kun
|
|||
* call to clk_set_rate_range(), the rate will be raised to match the
|
||||
* new minimum.
|
||||
*
|
||||
* This assumes that clk_ops.determine_rate or clk_ops.round_rate won't
|
||||
* modify the requested rate, which is our case in clk_dummy_rate_ops.
|
||||
* This assumes that clk_ops.determine_rate won't modify the requested rate,
|
||||
* which is our case in clk_dummy_rate_ops.
|
||||
*/
|
||||
static void clk_range_test_set_range_get_rate_raised(struct kunit *test)
|
||||
{
|
||||
|
|
@ -1707,8 +1707,8 @@ static void clk_range_test_set_range_get_rate_raised(struct kunit *test)
|
|||
* call to clk_set_rate_range(), the rate will be lowered to match the
|
||||
* new maximum.
|
||||
*
|
||||
* This assumes that clk_ops.determine_rate or clk_ops.round_rate won't
|
||||
* modify the requested rate, which is our case in clk_dummy_rate_ops.
|
||||
* This assumes that clk_ops.determine_rate won't modify the requested rate,
|
||||
* which is our case in clk_dummy_rate_ops.
|
||||
*/
|
||||
static void clk_range_test_set_range_get_rate_lowered(struct kunit *test)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
config COMMON_CLK_ESWIN
|
||||
bool
|
||||
|
||||
config COMMON_CLK_EIC7700
|
||||
tristate "EIC7700 Clock Driver"
|
||||
depends on ARCH_ESWIN || COMPILE_TEST
|
||||
select COMMON_CLK_ESWIN
|
||||
default ARCH_ESWIN
|
||||
help
|
||||
This driver provides support for clock controller on ESWIN EIC7700
|
||||
SoC. The clock controller generates and supplies clocks to various
|
||||
peripherals within the SoC.
|
||||
Say yes here to support the clock controller on the EIC7700 SoC.
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Eswin Clock specific Makefile
|
||||
#
|
||||
|
||||
obj-$(CONFIG_COMMON_CLK_ESWIN) += clk.o
|
||||
|
||||
obj-$(CONFIG_COMMON_CLK_EIC7700) += clk-eic7700.o
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,586 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd..
|
||||
* All rights reserved.
|
||||
*
|
||||
* Authors:
|
||||
* Yifeng Huang <huangyifeng@eswincomputing.com>
|
||||
* Xuyang Dong <dongxuyang@eswincomputing.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define PLL_EN_MASK GENMASK(1, 0)
|
||||
#define PLL_REFDIV_MASK GENMASK(17, 12)
|
||||
#define PLL_FBDIV_MASK GENMASK(31, 20)
|
||||
#define PLL_FRAC_MASK GENMASK(27, 4)
|
||||
#define PLL_POSTDIV1_MASK GENMASK(10, 8)
|
||||
#define PLL_POSTDIV2_MASK GENMASK(18, 16)
|
||||
|
||||
struct eswin_clock_data *eswin_clk_init(struct platform_device *pdev,
|
||||
size_t nr_clks)
|
||||
{
|
||||
struct eswin_clock_data *eclk_data;
|
||||
|
||||
eclk_data = devm_kzalloc(&pdev->dev,
|
||||
struct_size(eclk_data, clk_data.hws, nr_clks),
|
||||
GFP_KERNEL);
|
||||
if (!eclk_data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
eclk_data->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(eclk_data->base))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
eclk_data->clk_data.num = nr_clks;
|
||||
spin_lock_init(&eclk_data->lock);
|
||||
|
||||
return eclk_data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(eswin_clk_init);
|
||||
|
||||
/**
|
||||
* eswin_calc_pll - calculate PLL values
|
||||
* @frac_val: fractional divider
|
||||
* @fbdiv_val: feedback divider
|
||||
* @rate: reference rate
|
||||
* @parent_rate: parent rate
|
||||
*
|
||||
* Calculate PLL values for frac and fbdiv:
|
||||
* fbdiv = rate * 4 / parent_rate
|
||||
* frac = (rate * 4 % parent_rate * (2 ^ 24)) / parent_rate
|
||||
*/
|
||||
static void eswin_calc_pll(u32 *frac_val, u32 *fbdiv_val, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 rem;
|
||||
u64 tmp;
|
||||
|
||||
/* step 1: rate * 4 */
|
||||
tmp = rate * 4;
|
||||
/* step 2: use do_div() to get the quotient(tmp) and remainder(rem) */
|
||||
rem = do_div(tmp, (u32)parent_rate);
|
||||
/* fbdiv = rate * 4 / parent_rate */
|
||||
*fbdiv_val = (u32)tmp;
|
||||
/*
|
||||
* step 3: rem << 24
|
||||
* 24: 24-bit fractional accuracy
|
||||
*/
|
||||
tmp = (u64)rem << 24;
|
||||
/* step 4: use do_div() to get the quotient(tmp) */
|
||||
do_div(tmp, (u32)parent_rate);
|
||||
/* frac = (rate * 4 % parent_rate * (2 ^ 24)) / parent_rate */
|
||||
*frac_val = (u32)tmp;
|
||||
}
|
||||
|
||||
static inline struct eswin_clk_pll *to_pll_clk(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct eswin_clk_pll, hw);
|
||||
}
|
||||
|
||||
static int clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct eswin_clk_pll *clk = to_pll_clk(hw);
|
||||
u32 frac_val, fbdiv_val, val, mask;
|
||||
int ret;
|
||||
|
||||
eswin_calc_pll(&frac_val, &fbdiv_val, rate, parent_rate);
|
||||
|
||||
/* First, disable pll */
|
||||
val = readl_relaxed(clk->ctrl_reg0);
|
||||
val &= ~PLL_EN_MASK;
|
||||
val |= FIELD_PREP(PLL_EN_MASK, 0);
|
||||
writel_relaxed(val, clk->ctrl_reg0);
|
||||
|
||||
val = readl_relaxed(clk->ctrl_reg0);
|
||||
val &= ~(PLL_REFDIV_MASK | PLL_FBDIV_MASK);
|
||||
val |= FIELD_PREP(PLL_FBDIV_MASK, fbdiv_val);
|
||||
val |= FIELD_PREP(PLL_REFDIV_MASK, 1);
|
||||
writel_relaxed(val, clk->ctrl_reg0);
|
||||
|
||||
val = readl_relaxed(clk->ctrl_reg1);
|
||||
val &= ~PLL_FRAC_MASK;
|
||||
val |= FIELD_PREP(PLL_FRAC_MASK, frac_val);
|
||||
writel_relaxed(val, clk->ctrl_reg1);
|
||||
|
||||
val = readl_relaxed(clk->ctrl_reg2);
|
||||
val &= ~(PLL_POSTDIV1_MASK | PLL_POSTDIV2_MASK);
|
||||
val |= FIELD_PREP(PLL_POSTDIV1_MASK, 1);
|
||||
val |= FIELD_PREP(PLL_POSTDIV2_MASK, 1);
|
||||
writel_relaxed(val, clk->ctrl_reg2);
|
||||
|
||||
/* Last, enable pll */
|
||||
val = readl_relaxed(clk->ctrl_reg0);
|
||||
val &= ~PLL_EN_MASK;
|
||||
val |= FIELD_PREP(PLL_EN_MASK, 1);
|
||||
writel_relaxed(val, clk->ctrl_reg0);
|
||||
|
||||
/* Usually the pll will lock in 50us */
|
||||
mask = GENMASK(clk->lock_shift + clk->lock_width - 1, clk->lock_shift);
|
||||
ret = readl_poll_timeout(clk->status_reg, val, val & mask, 1, 50 * 2);
|
||||
if (ret)
|
||||
pr_err("failed to lock the pll!\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct eswin_clk_pll *clk = to_pll_clk(hw);
|
||||
u64 fbdiv_val, frac_val, tmp;
|
||||
u32 rem, val;
|
||||
|
||||
val = readl_relaxed(clk->ctrl_reg0);
|
||||
val &= PLL_FBDIV_MASK;
|
||||
fbdiv_val = (val >> clk->fbdiv_shift);
|
||||
|
||||
val = readl_relaxed(clk->ctrl_reg1);
|
||||
val &= PLL_FRAC_MASK;
|
||||
frac_val = (val >> clk->frac_shift);
|
||||
|
||||
/* rate = 24000000 * (fbdiv + frac / (2 ^ 24)) / 4 */
|
||||
tmp = parent_rate * frac_val;
|
||||
rem = do_div(tmp, BIT(24));
|
||||
if (rem)
|
||||
tmp = parent_rate * fbdiv_val + tmp + 1;
|
||||
else
|
||||
tmp = parent_rate * fbdiv_val + tmp;
|
||||
|
||||
do_div(tmp, 4);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static int clk_pll_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct eswin_clk_pll *clk = to_pll_clk(hw);
|
||||
|
||||
req->rate = clamp(req->rate, clk->min_rate, clk->max_rate);
|
||||
req->min_rate = clk->min_rate;
|
||||
req->max_rate = clk->max_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eswin_clk_register_fixed_rate(struct device *dev,
|
||||
struct eswin_fixed_rate_clock *clks,
|
||||
int nums, struct eswin_clock_data *data)
|
||||
{
|
||||
struct clk_hw *clk_hw;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
clk_hw = devm_clk_hw_register_fixed_rate(dev, clks[i].name,
|
||||
NULL, clks[i].flags,
|
||||
clks[i].rate);
|
||||
if (IS_ERR(clk_hw))
|
||||
return PTR_ERR(clk_hw);
|
||||
|
||||
clks[i].hw = *clk_hw;
|
||||
data->clk_data.hws[clks[i].id] = clk_hw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(eswin_clk_register_fixed_rate);
|
||||
|
||||
static const struct clk_ops eswin_clk_pll_ops = {
|
||||
.set_rate = clk_pll_set_rate,
|
||||
.recalc_rate = clk_pll_recalc_rate,
|
||||
.determine_rate = clk_pll_determine_rate,
|
||||
};
|
||||
|
||||
int eswin_clk_register_pll(struct device *dev, struct eswin_pll_clock *clks,
|
||||
int nums, struct eswin_clock_data *data)
|
||||
{
|
||||
struct eswin_clk_pll *p_clk = NULL;
|
||||
struct clk_init_data init;
|
||||
struct clk_hw *clk_hw;
|
||||
int i, ret;
|
||||
|
||||
p_clk = devm_kzalloc(dev, sizeof(*p_clk) * nums, GFP_KERNEL);
|
||||
if (!p_clk)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
p_clk->id = clks[i].id;
|
||||
p_clk->ctrl_reg0 = data->base + clks[i].ctrl_reg0;
|
||||
p_clk->fbdiv_shift = clks[i].fbdiv_shift;
|
||||
|
||||
p_clk->ctrl_reg1 = data->base + clks[i].ctrl_reg1;
|
||||
p_clk->frac_shift = clks[i].frac_shift;
|
||||
|
||||
p_clk->ctrl_reg2 = data->base + clks[i].ctrl_reg2;
|
||||
|
||||
p_clk->status_reg = data->base + clks[i].status_reg;
|
||||
p_clk->lock_shift = clks[i].lock_shift;
|
||||
p_clk->lock_width = clks[i].lock_width;
|
||||
|
||||
p_clk->max_rate = clks[i].max_rate;
|
||||
p_clk->min_rate = clks[i].min_rate;
|
||||
|
||||
init.name = clks[i].name;
|
||||
init.flags = 0;
|
||||
init.parent_data = clks[i].parent_data;
|
||||
init.num_parents = 1;
|
||||
init.ops = &eswin_clk_pll_ops;
|
||||
p_clk->hw.init = &init;
|
||||
|
||||
clk_hw = &p_clk->hw;
|
||||
|
||||
ret = devm_clk_hw_register(dev, clk_hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clks[i].hw = *clk_hw;
|
||||
data->clk_data.hws[clks[i].id] = clk_hw;
|
||||
p_clk++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(eswin_clk_register_pll);
|
||||
|
||||
int eswin_clk_register_fixed_factor(struct device *dev,
|
||||
struct eswin_fixed_factor_clock *clks,
|
||||
int nums, struct eswin_clock_data *data)
|
||||
{
|
||||
struct clk_hw *clk_hw;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
clk_hw = devm_clk_hw_register_fixed_factor_index(dev, clks[i].name,
|
||||
clks[i].parent_data->index,
|
||||
clks[i].flags, clks[i].mult,
|
||||
clks[i].div);
|
||||
|
||||
if (IS_ERR(clk_hw))
|
||||
return PTR_ERR(clk_hw);
|
||||
|
||||
clks[i].hw = *clk_hw;
|
||||
data->clk_data.hws[clks[i].id] = clk_hw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(eswin_clk_register_fixed_factor);
|
||||
|
||||
int eswin_clk_register_mux(struct device *dev, struct eswin_mux_clock *clks,
|
||||
int nums, struct eswin_clock_data *data)
|
||||
{
|
||||
struct clk_hw *clk_hw;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
clk_hw = devm_clk_hw_register_mux_parent_data_table(dev, clks[i].name,
|
||||
clks[i].parent_data,
|
||||
clks[i].num_parents,
|
||||
clks[i].flags,
|
||||
data->base + clks[i].reg,
|
||||
clks[i].shift, clks[i].width,
|
||||
clks[i].mux_flags,
|
||||
clks[i].table, &data->lock);
|
||||
|
||||
if (IS_ERR(clk_hw))
|
||||
return PTR_ERR(clk_hw);
|
||||
|
||||
clks[i].hw = *clk_hw;
|
||||
data->clk_data.hws[clks[i].id] = clk_hw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(eswin_clk_register_mux);
|
||||
|
||||
static unsigned int _eswin_get_val(unsigned int div, unsigned long flags,
|
||||
u8 width)
|
||||
{
|
||||
unsigned int maxdiv;
|
||||
|
||||
maxdiv = clk_div_mask(width);
|
||||
div = div > maxdiv ? maxdiv : div;
|
||||
|
||||
if (flags & ESWIN_PRIV_DIV_MIN_2)
|
||||
return (div < 2) ? 2 : div;
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
static unsigned int eswin_div_get_val(unsigned long rate,
|
||||
unsigned long parent_rate, u8 width,
|
||||
unsigned long flags)
|
||||
{
|
||||
unsigned int div;
|
||||
|
||||
div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
|
||||
|
||||
return _eswin_get_val(div, flags, width);
|
||||
}
|
||||
|
||||
static inline struct eswin_divider_clock *to_div_clk(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct eswin_divider_clock, hw);
|
||||
}
|
||||
|
||||
static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct eswin_divider_clock *dclk = to_div_clk(hw);
|
||||
unsigned long flags;
|
||||
unsigned int value;
|
||||
u32 val;
|
||||
|
||||
value = eswin_div_get_val(rate, parent_rate, dclk->width,
|
||||
dclk->priv_flag);
|
||||
|
||||
spin_lock_irqsave(dclk->lock, flags);
|
||||
|
||||
val = readl_relaxed(dclk->ctrl_reg);
|
||||
val &= ~(clk_div_mask(dclk->width) << dclk->shift);
|
||||
val |= (u32)value << dclk->shift;
|
||||
writel_relaxed(val, dclk->ctrl_reg);
|
||||
|
||||
spin_unlock_irqrestore(dclk->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long clk_div_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct eswin_divider_clock *dclk = to_div_clk(hw);
|
||||
unsigned int div, val;
|
||||
|
||||
val = readl_relaxed(dclk->ctrl_reg) >> dclk->shift;
|
||||
val &= clk_div_mask(dclk->width);
|
||||
div = _eswin_get_val(val, dclk->priv_flag, dclk->width);
|
||||
|
||||
return DIV_ROUND_UP_ULL((u64)parent_rate, div);
|
||||
}
|
||||
|
||||
static int eswin_clk_bestdiv(unsigned long rate,
|
||||
unsigned long best_parent_rate, u8 width,
|
||||
unsigned long flags)
|
||||
{
|
||||
unsigned long bestdiv, up_rate, down_rate;
|
||||
int up, down;
|
||||
|
||||
if (!rate)
|
||||
rate = 1;
|
||||
|
||||
/* closest round */
|
||||
up = DIV_ROUND_UP_ULL((u64)best_parent_rate, rate);
|
||||
down = best_parent_rate / rate;
|
||||
|
||||
up_rate = DIV_ROUND_UP_ULL((u64)best_parent_rate, up);
|
||||
down_rate = DIV_ROUND_UP_ULL((u64)best_parent_rate, down);
|
||||
|
||||
bestdiv = (rate - up_rate) <= (down_rate - rate) ? up : down;
|
||||
|
||||
return bestdiv;
|
||||
}
|
||||
|
||||
static int clk_div_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct eswin_divider_clock *dclk = to_div_clk(hw);
|
||||
int div;
|
||||
|
||||
div = eswin_clk_bestdiv(req->rate, req->best_parent_rate, dclk->width,
|
||||
dclk->priv_flag);
|
||||
div = _eswin_get_val(div, dclk->priv_flag, dclk->width);
|
||||
req->rate = DIV_ROUND_UP_ULL((u64)req->best_parent_rate, div);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops eswin_clk_div_ops = {
|
||||
.set_rate = clk_div_set_rate,
|
||||
.recalc_rate = clk_div_recalc_rate,
|
||||
.determine_rate = clk_div_determine_rate,
|
||||
};
|
||||
|
||||
struct clk_hw *eswin_register_clkdiv(struct device *dev, unsigned int id,
|
||||
const char *name,
|
||||
const struct clk_hw *parent_hw,
|
||||
unsigned long flags, void __iomem *reg,
|
||||
u8 shift, u8 width,
|
||||
unsigned long clk_divider_flags,
|
||||
unsigned long priv_flag, spinlock_t *lock)
|
||||
{
|
||||
struct eswin_divider_clock *dclk;
|
||||
struct clk_init_data init;
|
||||
struct clk_hw *clk_hw;
|
||||
int ret;
|
||||
|
||||
dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL);
|
||||
if (!dclk)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &eswin_clk_div_ops;
|
||||
init.flags = flags;
|
||||
init.parent_hws = &parent_hw;
|
||||
init.num_parents = 1;
|
||||
|
||||
/* struct clk_divider assignments */
|
||||
dclk->id = id;
|
||||
dclk->ctrl_reg = reg;
|
||||
dclk->shift = shift;
|
||||
dclk->width = width;
|
||||
dclk->div_flags = clk_divider_flags;
|
||||
dclk->priv_flag = priv_flag;
|
||||
dclk->lock = lock;
|
||||
dclk->hw.init = &init;
|
||||
|
||||
/* register the clock */
|
||||
clk_hw = &dclk->hw;
|
||||
ret = devm_clk_hw_register(dev, clk_hw);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to register divider clock!\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return clk_hw;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(eswin_register_clkdiv);
|
||||
|
||||
int eswin_clk_register_divider(struct device *dev,
|
||||
struct eswin_divider_clock *clks,
|
||||
int nums, struct eswin_clock_data *data)
|
||||
{
|
||||
struct clk_hw *clk_hw;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
clk_hw = devm_clk_hw_register_divider_parent_data(dev, clks[i].name,
|
||||
clks[i].parent_data,
|
||||
clks[i].flags,
|
||||
data->base + clks[i].reg,
|
||||
clks[i].shift, clks[i].width,
|
||||
clks[i].div_flags, &data->lock);
|
||||
|
||||
if (IS_ERR(clk_hw))
|
||||
return PTR_ERR(clk_hw);
|
||||
|
||||
clks[i].hw = *clk_hw;
|
||||
data->clk_data.hws[clks[i].id] = clk_hw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(eswin_clk_register_divider);
|
||||
|
||||
int eswin_clk_register_gate(struct device *dev, struct eswin_gate_clock *clks,
|
||||
int nums, struct eswin_clock_data *data)
|
||||
{
|
||||
struct clk_hw *clk_hw;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
clk_hw = devm_clk_hw_register_gate_parent_data(dev, clks[i].name,
|
||||
clks[i].parent_data,
|
||||
clks[i].flags,
|
||||
data->base + clks[i].reg,
|
||||
clks[i].bit_idx, clks[i].gate_flags,
|
||||
&data->lock);
|
||||
|
||||
if (IS_ERR(clk_hw))
|
||||
return PTR_ERR(clk_hw);
|
||||
|
||||
clks[i].hw = *clk_hw;
|
||||
data->clk_data.hws[clks[i].id] = clk_hw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(eswin_clk_register_gate);
|
||||
|
||||
int eswin_clk_register_clks(struct device *dev, struct eswin_clk_info *clks,
|
||||
int nums, struct eswin_clock_data *data)
|
||||
{
|
||||
struct eswin_clk_info *info;
|
||||
const struct clk_hw *phw = NULL;
|
||||
struct clk_hw *hw;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nums; i++) {
|
||||
info = &clks[i];
|
||||
switch (info->type) {
|
||||
case CLK_FIXED_FACTOR: {
|
||||
const struct eswin_fixed_factor_clock *factor;
|
||||
|
||||
factor = &info->data.factor;
|
||||
phw = data->clk_data.hws[info->pid];
|
||||
hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, factor->name, phw,
|
||||
factor->flags,
|
||||
factor->mult,
|
||||
factor->div);
|
||||
break;
|
||||
}
|
||||
case CLK_MUX: {
|
||||
const struct eswin_mux_clock *mux = &info->data.mux;
|
||||
|
||||
hw = devm_clk_hw_register_mux_parent_data_table(dev, mux->name,
|
||||
mux->parent_data,
|
||||
mux->num_parents,
|
||||
mux->flags,
|
||||
data->base + mux->reg,
|
||||
mux->shift, mux->width,
|
||||
mux->mux_flags,
|
||||
mux->table, &data->lock);
|
||||
break;
|
||||
}
|
||||
case CLK_DIVIDER: {
|
||||
const struct eswin_divider_clock *div = &info->data.div;
|
||||
|
||||
phw = data->clk_data.hws[info->pid];
|
||||
if (div->priv_flag)
|
||||
hw = eswin_register_clkdiv(dev, div->id, div->name, phw,
|
||||
div->flags, data->base + div->reg,
|
||||
div->shift, div->width, div->div_flags,
|
||||
div->priv_flag, &data->lock);
|
||||
else
|
||||
hw = devm_clk_hw_register_divider_parent_hw(dev, div->name, phw,
|
||||
div->flags,
|
||||
data->base + div->reg,
|
||||
div->shift, div->width,
|
||||
div->div_flags,
|
||||
&data->lock);
|
||||
break;
|
||||
}
|
||||
case CLK_GATE: {
|
||||
const struct eswin_gate_clock *gate = &info->data.gate;
|
||||
|
||||
phw = data->clk_data.hws[info->pid];
|
||||
hw = devm_clk_hw_register_gate_parent_hw(dev, gate->name, phw,
|
||||
gate->flags,
|
||||
data->base + gate->reg,
|
||||
gate->bit_idx, gate->gate_flags,
|
||||
&data->lock);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
dev_err(dev, "Unidentifiable clock type!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (IS_ERR(hw))
|
||||
return PTR_ERR(hw);
|
||||
|
||||
info->hw = *hw;
|
||||
data->clk_data.hws[info->id] = hw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(eswin_clk_register_clks);
|
||||
|
|
@ -0,0 +1,340 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2026, Beijing ESWIN Computing Technology Co., Ltd..
|
||||
* All rights reserved.
|
||||
*
|
||||
* Authors:
|
||||
* Yifeng Huang <huangyifeng@eswincomputing.com>
|
||||
* Xuyang Dong <dongxuyang@eswincomputing.com>
|
||||
*/
|
||||
|
||||
#ifndef __ESWIN_COMMON_H__
|
||||
#define __ESWIN_COMMON_H__
|
||||
|
||||
#define APLL_HIGH_FREQ 983040000
|
||||
#define APLL_LOW_FREQ 225792000
|
||||
#define PLL_HIGH_FREQ 1800000000
|
||||
#define PLL_LOW_FREQ 24000000
|
||||
|
||||
/*
|
||||
* ESWIN_PRIV_DIV_MIN_2: If ESWIN_PRIV_DIV_MIN_2 is set, the minimum value of
|
||||
* the register is 2, i.e. the minimum division ratio is 2.
|
||||
*/
|
||||
#define ESWIN_PRIV_DIV_MIN_2 BIT(0)
|
||||
|
||||
enum eswin_clk_type {
|
||||
CLK_FIXED_FACTOR,
|
||||
CLK_MUX,
|
||||
CLK_DIVIDER,
|
||||
CLK_GATE,
|
||||
};
|
||||
|
||||
struct eswin_clock_data {
|
||||
void __iomem *base;
|
||||
struct clk_hw *original_clk;
|
||||
struct notifier_block pll_nb;
|
||||
spinlock_t lock; /* protect register read-modify-write cycle */
|
||||
struct clk_hw_onecell_data clk_data;
|
||||
};
|
||||
|
||||
struct eswin_divider_clock {
|
||||
struct clk_hw hw;
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const struct clk_parent_data *parent_data;
|
||||
void __iomem *ctrl_reg; /* register address of the divider clock */
|
||||
unsigned long flags;
|
||||
unsigned long reg; /* register offset */
|
||||
u8 shift;
|
||||
u8 width;
|
||||
unsigned long div_flags;
|
||||
unsigned long priv_flag;
|
||||
spinlock_t *lock; /* protect register read-modify-write cycle */
|
||||
};
|
||||
|
||||
struct eswin_fixed_rate_clock {
|
||||
struct clk_hw hw;
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
unsigned long flags;
|
||||
unsigned long rate;
|
||||
};
|
||||
|
||||
struct eswin_fixed_factor_clock {
|
||||
struct clk_hw hw;
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const struct clk_parent_data *parent_data;
|
||||
unsigned long mult;
|
||||
unsigned long div;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
struct eswin_gate_clock {
|
||||
struct clk_hw hw;
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const struct clk_parent_data *parent_data;
|
||||
unsigned long flags;
|
||||
unsigned long reg;
|
||||
u8 bit_idx;
|
||||
u8 gate_flags;
|
||||
};
|
||||
|
||||
struct eswin_mux_clock {
|
||||
struct clk_hw hw;
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
const struct clk_parent_data *parent_data;
|
||||
u8 num_parents;
|
||||
unsigned long flags;
|
||||
unsigned long reg;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
u8 mux_flags;
|
||||
u32 *table;
|
||||
};
|
||||
|
||||
struct eswin_pll_clock {
|
||||
struct clk_hw hw;
|
||||
u32 id;
|
||||
const char *name;
|
||||
const struct clk_parent_data *parent_data;
|
||||
const u32 ctrl_reg0;
|
||||
const u8 fbdiv_shift;
|
||||
|
||||
const u32 ctrl_reg1;
|
||||
const u8 frac_shift;
|
||||
|
||||
const u32 ctrl_reg2;
|
||||
|
||||
const u32 status_reg;
|
||||
const u8 lock_shift;
|
||||
const u8 lock_width;
|
||||
|
||||
const u64 max_rate;
|
||||
const u64 min_rate;
|
||||
};
|
||||
|
||||
struct eswin_clk_pll {
|
||||
struct clk_hw hw;
|
||||
u32 id;
|
||||
void __iomem *ctrl_reg0;
|
||||
u8 fbdiv_shift;
|
||||
|
||||
void __iomem *ctrl_reg1;
|
||||
u8 frac_shift;
|
||||
|
||||
void __iomem *ctrl_reg2;
|
||||
|
||||
void __iomem *status_reg;
|
||||
u8 lock_shift;
|
||||
u8 lock_width;
|
||||
|
||||
u64 max_rate;
|
||||
u64 min_rate;
|
||||
};
|
||||
|
||||
struct eswin_clk_info {
|
||||
unsigned int type;
|
||||
unsigned int pid;
|
||||
unsigned int id;
|
||||
struct clk_hw hw;
|
||||
union {
|
||||
struct eswin_divider_clock div;
|
||||
struct eswin_fixed_factor_clock factor;
|
||||
struct eswin_gate_clock gate;
|
||||
struct eswin_mux_clock mux;
|
||||
} data;
|
||||
};
|
||||
|
||||
struct eswin_clock_data *eswin_clk_init(struct platform_device *pdev,
|
||||
size_t nr_clks);
|
||||
int eswin_clk_register_fixed_rate(struct device *dev,
|
||||
struct eswin_fixed_rate_clock *clks,
|
||||
int nums, struct eswin_clock_data *data);
|
||||
int eswin_clk_register_pll(struct device *dev, struct eswin_pll_clock *clks,
|
||||
int nums, struct eswin_clock_data *data);
|
||||
int eswin_clk_register_fixed_factor(struct device *dev,
|
||||
struct eswin_fixed_factor_clock *clks,
|
||||
int nums, struct eswin_clock_data *data);
|
||||
int eswin_clk_register_mux(struct device *dev, struct eswin_mux_clock *clks,
|
||||
int nums, struct eswin_clock_data *data);
|
||||
int eswin_clk_register_divider(struct device *dev,
|
||||
struct eswin_divider_clock *clks,
|
||||
int nums, struct eswin_clock_data *data);
|
||||
int eswin_clk_register_gate(struct device *dev, struct eswin_gate_clock *clks,
|
||||
int nums, struct eswin_clock_data *data);
|
||||
int eswin_clk_register_clks(struct device *dev, struct eswin_clk_info *clks,
|
||||
int nums, struct eswin_clock_data *data);
|
||||
struct clk_hw *eswin_register_clkdiv(struct device *dev, unsigned int id,
|
||||
const char *name,
|
||||
const struct clk_hw *parent_hw,
|
||||
unsigned long flags, void __iomem *reg,
|
||||
u8 shift, u8 width,
|
||||
unsigned long clk_divider_flags,
|
||||
unsigned long priv_flag, spinlock_t *lock);
|
||||
|
||||
#define ESWIN_DIV(_id, _name, _pdata, _flags, _reg, _shift, _width, \
|
||||
_dflags, _pflag) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_data = _pdata, \
|
||||
.flags = _flags, \
|
||||
.reg = _reg, \
|
||||
.shift = _shift, \
|
||||
.width = _width, \
|
||||
.div_flags = _dflags, \
|
||||
.priv_flag = _pflag, \
|
||||
}
|
||||
|
||||
#define ESWIN_DIV_TYPE(_id, _name, _pid, _flags, _reg, _shift, _width, \
|
||||
_dflags, _pflag) \
|
||||
{ \
|
||||
.type = CLK_DIVIDER, \
|
||||
.pid = _pid, \
|
||||
.id = _id, \
|
||||
.data = { \
|
||||
.div = { \
|
||||
.name = _name, \
|
||||
.flags = _flags, \
|
||||
.reg = _reg, \
|
||||
.shift = _shift, \
|
||||
.width = _width, \
|
||||
.div_flags = _dflags, \
|
||||
.priv_flag = _pflag, \
|
||||
}, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ESWIN_FACTOR(_id, _name, _pdata, _mult, _div, _flags) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_data = _pdata, \
|
||||
.mult = _mult, \
|
||||
.div = _div, \
|
||||
.flags = _flags, \
|
||||
}
|
||||
|
||||
#define ESWIN_FACTOR_TYPE(_id, _name, _pid, _mult, _div, _flags) \
|
||||
{ \
|
||||
.type = CLK_FIXED_FACTOR, \
|
||||
.pid = _pid, \
|
||||
.id = _id, \
|
||||
.data = { \
|
||||
.factor = { \
|
||||
.name = _name, \
|
||||
.mult = _mult, \
|
||||
.div = _div, \
|
||||
.flags = _flags, \
|
||||
}, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ESWIN_FIXED(_id, _name, _flags, _rate) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.flags = _flags, \
|
||||
.rate = _rate, \
|
||||
}
|
||||
|
||||
#define ESWIN_GATE(_id, _name, _pdata, _flags, _reg, _idx, _gflags) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_data = _pdata, \
|
||||
.flags = _flags, \
|
||||
.reg = _reg, \
|
||||
.bit_idx = _idx, \
|
||||
.gate_flags = _gflags, \
|
||||
}
|
||||
|
||||
#define ESWIN_GATE_TYPE(_id, _name, _pid, _flags, _reg, _idx, _gflags) \
|
||||
{ \
|
||||
.type = CLK_GATE, \
|
||||
.pid = _pid, \
|
||||
.id = _id, \
|
||||
.data = { \
|
||||
.gate = { \
|
||||
.name = _name, \
|
||||
.flags = _flags, \
|
||||
.reg = _reg, \
|
||||
.bit_idx = _idx, \
|
||||
.gate_flags = _gflags, \
|
||||
}, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ESWIN_MUX(_id, _name, _pdata, _num_parents, _flags, _reg, \
|
||||
_shift, _width, _mflags) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_data = _pdata, \
|
||||
.num_parents = _num_parents, \
|
||||
.flags = _flags, \
|
||||
.reg = _reg, \
|
||||
.shift = _shift, \
|
||||
.width = _width, \
|
||||
.mux_flags = _mflags, \
|
||||
.table = NULL, \
|
||||
}
|
||||
|
||||
#define ESWIN_MUX_TBL(_id, _name, _pdata, _num_parents, _flags, _reg, \
|
||||
_shift, _width, _mflags, _table) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_data = _pdata, \
|
||||
.num_parents = _num_parents, \
|
||||
.flags = _flags, \
|
||||
.reg = _reg, \
|
||||
.shift = _shift, \
|
||||
.width = _width, \
|
||||
.mux_flags = _mflags, \
|
||||
.table = _table, \
|
||||
}
|
||||
|
||||
#define ESWIN_MUX_TYPE(_id, _name, _pdata, _num_parents, _flags, _reg, \
|
||||
_shift, _width, _mflags, _table) \
|
||||
{ \
|
||||
.type = CLK_MUX, \
|
||||
.id = _id, \
|
||||
.data = { \
|
||||
.mux = { \
|
||||
.name = _name, \
|
||||
.parent_data = _pdata, \
|
||||
.num_parents = _num_parents, \
|
||||
.flags = _flags, \
|
||||
.reg = _reg, \
|
||||
.shift = _shift, \
|
||||
.width = _width, \
|
||||
.mux_flags = _mflags, \
|
||||
.table = _table, \
|
||||
}, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ESWIN_PLL(_id, _name, _pdata, _reg0, _fb_shift, _reg1, \
|
||||
_frac_shift, _reg2, _reg, _lock_shift, _lock_width, \
|
||||
_max_rate, _min_rate) \
|
||||
{ \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_data = _pdata, \
|
||||
.ctrl_reg0 = _reg0, \
|
||||
.fbdiv_shift = _fb_shift, \
|
||||
.ctrl_reg1 = _reg1, \
|
||||
.frac_shift = _frac_shift, \
|
||||
.ctrl_reg2 = _reg2, \
|
||||
.status_reg = _reg, \
|
||||
.lock_shift = _lock_shift, \
|
||||
.lock_width = _lock_width, \
|
||||
.max_rate = _max_rate, \
|
||||
.min_rate = _min_rate, \
|
||||
}
|
||||
|
||||
#endif /* __ESWIN_COMMON_H__ */
|
||||
|
|
@ -85,9 +85,11 @@ static const struct imx_fracn_gppll_rate_table fracn_tbl[] = {
|
|||
PLL_FRACN_GP(519750000U, 173, 25, 100, 1, 8),
|
||||
PLL_FRACN_GP(498000000U, 166, 0, 1, 0, 8),
|
||||
PLL_FRACN_GP(484000000U, 121, 0, 1, 0, 6),
|
||||
PLL_FRACN_GP(477400000U, 119, 35, 100, 0, 6),
|
||||
PLL_FRACN_GP(445333333U, 167, 0, 1, 0, 9),
|
||||
PLL_FRACN_GP(400000000U, 200, 0, 1, 0, 12),
|
||||
PLL_FRACN_GP(393216000U, 163, 84, 100, 0, 10),
|
||||
PLL_FRACN_GP(333333333U, 125, 0, 1, 1, 9),
|
||||
PLL_FRACN_GP(332600000U, 138, 584, 1000, 0, 10),
|
||||
PLL_FRACN_GP(300000000U, 150, 0, 1, 0, 12),
|
||||
PLL_FRACN_GP(241900000U, 201, 584, 1000, 0, 20),
|
||||
|
|
|
|||
|
|
@ -188,9 +188,11 @@ static void of_assigned_ldb_sels(struct device_node *node,
|
|||
}
|
||||
if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) {
|
||||
pr_err("ccm: parent clock %d not in ccm\n", index);
|
||||
of_node_put(clkspec.np);
|
||||
return;
|
||||
}
|
||||
parent = clkspec.args[0];
|
||||
of_node_put(clkspec.np);
|
||||
|
||||
rc = of_parse_phandle_with_args(node, "assigned-clocks",
|
||||
"#clock-cells", index, &clkspec);
|
||||
|
|
@ -198,9 +200,11 @@ static void of_assigned_ldb_sels(struct device_node *node,
|
|||
return;
|
||||
if (clkspec.np != node || clkspec.args[0] >= IMX6QDL_CLK_END) {
|
||||
pr_err("ccm: child clock %d not in ccm\n", index);
|
||||
of_node_put(clkspec.np);
|
||||
return;
|
||||
}
|
||||
child = clkspec.args[0];
|
||||
of_node_put(clkspec.np);
|
||||
|
||||
if (child != IMX6QDL_CLK_LDB_DI0_SEL &&
|
||||
child != IMX6QDL_CLK_LDB_DI1_SEL)
|
||||
|
|
@ -238,8 +242,11 @@ static bool pll6_bypassed(struct device_node *node)
|
|||
return false;
|
||||
|
||||
if (clkspec.np == node &&
|
||||
clkspec.args[0] == IMX6QDL_PLL6_BYPASS)
|
||||
clkspec.args[0] == IMX6QDL_PLL6_BYPASS) {
|
||||
of_node_put(clkspec.np);
|
||||
break;
|
||||
}
|
||||
of_node_put(clkspec.np);
|
||||
}
|
||||
|
||||
/* PLL6 bypass is not part of the assigned clock list */
|
||||
|
|
@ -249,6 +256,9 @@ static bool pll6_bypassed(struct device_node *node)
|
|||
ret = of_parse_phandle_with_args(node, "assigned-clock-parents",
|
||||
"#clock-cells", index, &clkspec);
|
||||
|
||||
if (!ret)
|
||||
of_node_put(clkspec.np);
|
||||
|
||||
if (clkspec.args[0] != IMX6QDL_CLK_PLL6)
|
||||
return true;
|
||||
|
||||
|
|
|
|||
|
|
@ -371,7 +371,8 @@ static int imx8_acm_clk_probe(struct platform_device *pdev)
|
|||
for (i = 0; i < priv->soc_data->num_sels; i++) {
|
||||
hws[sels[i].clkid] = devm_clk_hw_register_mux_parent_data_table(dev,
|
||||
sels[i].name, sels[i].parents,
|
||||
sels[i].num_parents, 0,
|
||||
sels[i].num_parents,
|
||||
CLK_SET_RATE_NO_REPARENT,
|
||||
base + sels[i].reg,
|
||||
sels[i].shift, sels[i].width,
|
||||
0, NULL, NULL);
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ static const char * const imx8mq_dsi_esc_sels[] = {"osc_25m", "sys2_pll_100m", "
|
|||
static const char * const imx8mq_csi1_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "sys3_pll_out", "audio_pll2_out", "video_pll1_out", };
|
||||
|
||||
static const char * const imx8mq_csi1_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m",
|
||||
static const char * const imx8mq_csi1_phy_sels[] = {"osc_25m", "sys2_pll_333m", "sys2_pll_100m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", };
|
||||
|
||||
static const char * const imx8mq_csi1_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
|
||||
|
|
@ -246,7 +246,7 @@ static const char * const imx8mq_csi1_esc_sels[] = {"osc_25m", "sys2_pll_100m",
|
|||
static const char * const imx8mq_csi2_core_sels[] = {"osc_25m", "sys1_pll_266m", "sys2_pll_250m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "sys3_pll_out", "audio_pll2_out", "video_pll1_out", };
|
||||
|
||||
static const char * const imx8mq_csi2_phy_sels[] = {"osc_25m", "sys2_pll_125m", "sys2_pll_100m", "sys1_pll_800m",
|
||||
static const char * const imx8mq_csi2_phy_sels[] = {"osc_25m", "sys2_pll_333m", "sys2_pll_100m", "sys1_pll_800m",
|
||||
"sys2_pll_1000m", "clk_ext2", "audio_pll2_out", "video_pll1_out", };
|
||||
|
||||
static const char * const imx8mq_csi2_esc_sels[] = {"osc_25m", "sys2_pll_100m", "sys1_pll_80m", "sys1_pll_800m",
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ static void imx_pll14xx_calc_settings(struct clk_pll14xx *pll, unsigned long rat
|
|||
/* First try if we can get the desired rate from one of the static entries */
|
||||
tt = imx_get_pll_settings(pll, rate);
|
||||
if (tt) {
|
||||
pr_debug("%s: in=%ld, want=%ld, Using PLL setting from table\n",
|
||||
pr_debug("%s: in=%lu, want=%lu, Using PLL setting from table\n",
|
||||
clk_hw_get_name(&pll->hw), prate, rate);
|
||||
t->rate = tt->rate;
|
||||
t->mdiv = tt->mdiv;
|
||||
|
|
@ -173,7 +173,7 @@ static void imx_pll14xx_calc_settings(struct clk_pll14xx *pll, unsigned long rat
|
|||
|
||||
if (rate >= rate_min && rate <= rate_max) {
|
||||
kdiv = pll1443x_calc_kdiv(mdiv, pdiv, sdiv, rate, prate);
|
||||
pr_debug("%s: in=%ld, want=%ld Only adjust kdiv %ld -> %d\n",
|
||||
pr_debug("%s: in=%lu, want=%lu Only adjust kdiv %ld -> %d\n",
|
||||
clk_hw_get_name(&pll->hw), prate, rate,
|
||||
FIELD_GET(KDIV_MASK, pll_div_ctl1), kdiv);
|
||||
fout = pll14xx_calc_rate(pll, mdiv, pdiv, sdiv, kdiv, prate);
|
||||
|
|
@ -211,7 +211,7 @@ static void imx_pll14xx_calc_settings(struct clk_pll14xx *pll, unsigned long rat
|
|||
}
|
||||
}
|
||||
found:
|
||||
pr_debug("%s: in=%ld, want=%ld got=%d (pdiv=%d sdiv=%d mdiv=%d kdiv=%d)\n",
|
||||
pr_debug("%s: in=%lu, want=%lu got=%u (pdiv=%d sdiv=%d mdiv=%d kdiv=%d)\n",
|
||||
clk_hw_get_name(&pll->hw), prate, rate, t->rate, t->pdiv, t->sdiv,
|
||||
t->mdiv, t->kdiv);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,13 @@
|
|||
|
||||
#include "clk.h"
|
||||
|
||||
/*
|
||||
* The VF610_CLK_END corresponds to ones defined in
|
||||
* include/dt-bindings/clock/vf610-clock.h
|
||||
* It shall be the value of the last defined clock +1
|
||||
*/
|
||||
#define VF610_CLK_END 196
|
||||
|
||||
#define CCM_CCR (ccm_base + 0x00)
|
||||
#define CCM_CSR (ccm_base + 0x04)
|
||||
#define CCM_CCSR (ccm_base + 0x08)
|
||||
|
|
@ -313,6 +320,11 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
|
|||
clk[VF610_CLK_ENET_TS] = imx_clk_gate("enet_ts", "enet_ts_sel", CCM_CSCDR1, 23);
|
||||
clk[VF610_CLK_ENET0] = imx_clk_gate2("enet0", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(0));
|
||||
clk[VF610_CLK_ENET1] = imx_clk_gate2("enet1", "ipg_bus", CCM_CCGR9, CCM_CCGRx_CGn(1));
|
||||
clk[VF610_CLK_ESW] = imx_clk_gate2("esw", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(8));
|
||||
clk[VF610_CLK_ESW_MAC_TAB0] = imx_clk_gate2("esw_tab0", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(12));
|
||||
clk[VF610_CLK_ESW_MAC_TAB1] = imx_clk_gate2("esw_tab1", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(13));
|
||||
clk[VF610_CLK_ESW_MAC_TAB2] = imx_clk_gate2("esw_tab2", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(14));
|
||||
clk[VF610_CLK_ESW_MAC_TAB3] = imx_clk_gate2("esw_tab3", "ipg_bus", CCM_CCGR10, CCM_CCGRx_CGn(15));
|
||||
|
||||
clk[VF610_CLK_PIT] = imx_clk_gate2("pit", "ipg_bus", CCM_CCGR1, CCM_CCGRx_CGn(7));
|
||||
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ static int mpfs_ccc_register_outputs(struct device *dev, struct mpfs_ccc_out_hw_
|
|||
return dev_err_probe(dev, ret, "failed to register clock id: %d\n",
|
||||
out_hw->id);
|
||||
|
||||
data->hw_data.hws[out_hw->id] = &out_hw->divider.hw;
|
||||
data->hw_data.hws[out_hw->id - 2] = &out_hw->divider.hw;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -234,6 +234,10 @@ static int mpfs_ccc_probe(struct platform_device *pdev)
|
|||
unsigned int num_clks;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If DLLs get added here, mpfs_ccc_register_outputs() currently packs
|
||||
* sparse clock IDs in the hws array
|
||||
*/
|
||||
num_clks = ARRAY_SIZE(mpfs_ccc_pll_clks) + ARRAY_SIZE(mpfs_ccc_pll0out_clks) +
|
||||
ARRAY_SIZE(mpfs_ccc_pll1out_clks);
|
||||
|
||||
|
|
|
|||
|
|
@ -126,9 +126,11 @@ static const struct clk_div_table clk_table2[] = {
|
|||
static const struct clk_ops clk_double_div_ops;
|
||||
static const struct clk_ops clk_pm_cpu_ops;
|
||||
|
||||
#define __reg(__x) ((void __iomem __force *)(__x))
|
||||
|
||||
#define PERIPH_GATE(_name, _bit) \
|
||||
struct clk_gate gate_##_name = { \
|
||||
.reg = (void *)CLK_DIS, \
|
||||
.reg = __reg(CLK_DIS), \
|
||||
.bit_idx = _bit, \
|
||||
.hw.init = &(struct clk_init_data){ \
|
||||
.ops = &clk_gate_ops, \
|
||||
|
|
@ -137,7 +139,7 @@ struct clk_gate gate_##_name = { \
|
|||
|
||||
#define PERIPH_MUX(_name, _shift) \
|
||||
struct clk_mux mux_##_name = { \
|
||||
.reg = (void *)TBG_SEL, \
|
||||
.reg = __reg(TBG_SEL), \
|
||||
.shift = _shift, \
|
||||
.mask = 3, \
|
||||
.hw.init = &(struct clk_init_data){ \
|
||||
|
|
@ -147,8 +149,8 @@ struct clk_mux mux_##_name = { \
|
|||
|
||||
#define PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2) \
|
||||
struct clk_double_div rate_##_name = { \
|
||||
.reg1 = (void *)_reg1, \
|
||||
.reg2 = (void *)_reg2, \
|
||||
.reg1 = __reg(_reg1), \
|
||||
.reg2 = __reg(_reg2), \
|
||||
.shift1 = _shift1, \
|
||||
.shift2 = _shift2, \
|
||||
.hw.init = &(struct clk_init_data){ \
|
||||
|
|
@ -158,7 +160,7 @@ struct clk_double_div rate_##_name = { \
|
|||
|
||||
#define PERIPH_DIV(_name, _reg, _shift, _table) \
|
||||
struct clk_divider rate_##_name = { \
|
||||
.reg = (void *)_reg, \
|
||||
.reg = __reg(_reg), \
|
||||
.table = _table, \
|
||||
.shift = _shift, \
|
||||
.hw.init = &(struct clk_init_data){ \
|
||||
|
|
@ -168,10 +170,10 @@ struct clk_divider rate_##_name = { \
|
|||
|
||||
#define PERIPH_PM_CPU(_name, _shift1, _reg, _shift2) \
|
||||
struct clk_pm_cpu muxrate_##_name = { \
|
||||
.reg_mux = (void *)TBG_SEL, \
|
||||
.reg_mux = __reg(TBG_SEL), \
|
||||
.mask_mux = 3, \
|
||||
.shift_mux = _shift1, \
|
||||
.reg_div = (void *)_reg, \
|
||||
.reg_div = __reg(_reg), \
|
||||
.shift_div = _shift2, \
|
||||
.hw.init = &(struct clk_init_data){ \
|
||||
.ops = &clk_pm_cpu_ops, \
|
||||
|
|
|
|||
|
|
@ -19,33 +19,77 @@ menuconfig COMMON_CLK_QCOM
|
|||
|
||||
if COMMON_CLK_QCOM
|
||||
|
||||
config CLK_ELIZA_DISPCC
|
||||
tristate "Eliza Display Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select CLK_ELIZA_GCC
|
||||
help
|
||||
Support for the display clock controllers on Eliza SoCs.
|
||||
Say Y if you want to support display devices and functionality such as
|
||||
splash screen.
|
||||
|
||||
config CLK_ELIZA_GCC
|
||||
tristate "Eliza Global Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select QCOM_GDSC
|
||||
help
|
||||
Support for the global clock controller on Eliza devices.
|
||||
Say Y if you want to use peripheral devices such as UART, SPI,
|
||||
I2C, USB, UFS, SDCC, etc.
|
||||
|
||||
config CLK_ELIZA_TCSRCC
|
||||
tristate "Eliza TCSR Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select QCOM_GDSC
|
||||
help
|
||||
Support for the TCSR clock controller on Eliza devices.
|
||||
Say Y if you want to use peripheral devices such as USB/PCIe/UFS.
|
||||
|
||||
config CLK_GLYMUR_DISPCC
|
||||
tristate "GLYMUR Display Clock Controller"
|
||||
tristate "Glymur Display Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select CLK_GLYMUR_GCC
|
||||
help
|
||||
Support for the display clock controllers on Qualcomm
|
||||
Technologies, Inc. GLYMUR devices.
|
||||
Technologies, Inc. Glymur devices.
|
||||
Say Y if you want to support display devices and functionality such as
|
||||
splash screen.
|
||||
|
||||
config CLK_GLYMUR_GCC
|
||||
tristate "GLYMUR Global Clock Controller"
|
||||
tristate "Glymur Global Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select QCOM_GDSC
|
||||
help
|
||||
Support for the global clock controller on GLYMUR devices.
|
||||
Support for the global clock controller on Glymur devices.
|
||||
Say Y if you want to use peripheral devices such as UART, SPI,
|
||||
I2C, USB, UFS, SDCC, etc.
|
||||
|
||||
config CLK_GLYMUR_GPUCC
|
||||
tristate "GLYMUR Graphics Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select CLK_GLYMUR_GCC
|
||||
help
|
||||
Support for the graphics clock controller on GLYMUR devices.
|
||||
Say Y if you want to support graphics controller devices and
|
||||
functionality such as 3D graphics.
|
||||
|
||||
config CLK_GLYMUR_TCSRCC
|
||||
tristate "GLYMUR TCSR Clock Controller"
|
||||
tristate "Glymur TCSR Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select QCOM_GDSC
|
||||
help
|
||||
Support for the TCSR clock controller on GLYMUR devices.
|
||||
Support for the TCSR clock controller on Glymur devices.
|
||||
Say Y if you want to use peripheral devices such as USB/PCIe/EDP.
|
||||
|
||||
config CLK_GLYMUR_VIDEOCC
|
||||
tristate "Glymur Video Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select CLK_GLYMUR_GCC
|
||||
help
|
||||
Support for the video clock controller on Glymur devices.
|
||||
Say Y if you want to support video devices and functionality such as
|
||||
video encode and decode.
|
||||
|
||||
config CLK_KAANAPALI_CAMCC
|
||||
tristate "Kaanapali Camera Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
|
|
@ -101,6 +145,16 @@ config CLK_KAANAPALI_VIDEOCC
|
|||
Say Y if you want to support video devices and functionality such as
|
||||
video encode/decode.
|
||||
|
||||
config CLK_NORD_GCC
|
||||
tristate "Nord Global Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select QCOM_GDSC
|
||||
help
|
||||
Support for the global clock controller on Nord devices.
|
||||
Say Y if you want to use peripheral devices such as UART,
|
||||
SPI, I2C, USB, SD/UFS, PCIe etc. The clock controller is a combination
|
||||
of GCC, SE_GCC, NE_GCC and NW_GCC.
|
||||
|
||||
config CLK_X1E80100_CAMCC
|
||||
tristate "X1E80100 Camera Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
|
|
@ -314,6 +368,14 @@ config IPQ_GCC_5018
|
|||
Say Y if you want to use peripheral devices such as UART, SPI,
|
||||
i2c, USB, SD/eMMC, etc.
|
||||
|
||||
config IPQ_GCC_5210
|
||||
tristate "IPQ5210 Global Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
help
|
||||
Support for the global clock controller on ipq5210 devices.
|
||||
Say Y if you want to use peripheral devices such as UART, SPI,
|
||||
i2c, USB, SD/eMMC, etc.
|
||||
|
||||
config IPQ_GCC_5332
|
||||
tristate "IPQ5332 Global Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
|
|
@ -622,6 +684,13 @@ config QCS_GCC_404
|
|||
Say Y if you want to use multimedia devices or peripheral
|
||||
devices such as UART, SPI, I2C, USB, SD/eMMC, PCIe etc.
|
||||
|
||||
config CLK_NORD_TCSRCC
|
||||
tristate "Nord TCSR Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
help
|
||||
Support for the TCSR clock controller on Nord devices.
|
||||
Say Y if you want to use peripheral devices such as PCIe, USB, UFS etc.
|
||||
|
||||
config SA_CAMCC_8775P
|
||||
tristate "SA8775P Camera Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
|
|
@ -1481,6 +1550,15 @@ config SM_GPUCC_8650
|
|||
Say Y if you want to support graphics controller devices and
|
||||
functionality such as 3D graphics.
|
||||
|
||||
config SM_GPUCC_8750
|
||||
tristate "SM8750 Graphics Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select SM_GCC_8750
|
||||
help
|
||||
Support for the graphics clock controller on SM8750 devices.
|
||||
Say Y if you want to support graphics controller devices and
|
||||
functionality such as 3D graphics.
|
||||
|
||||
config SM_LPASSCC_6115
|
||||
tristate "SM6115 Low Power Audio Subsystem (LPASS) Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
|
|
@ -1579,10 +1657,10 @@ config SM_VIDEOCC_8250
|
|||
config SM_VIDEOCC_8350
|
||||
tristate "SM8350 Video Clock Controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
select SM_GCC_8350
|
||||
depends on SM_GCC_8350 || SC_GCC_8280XP
|
||||
select QCOM_GDSC
|
||||
help
|
||||
Support for the video clock controller on SM8350 devices.
|
||||
Support for the video clock controller on SM8350 or SC8280XP devices.
|
||||
Say Y if you want to support video devices and functionality such as
|
||||
video encode and decode.
|
||||
|
||||
|
|
|
|||
|
|
@ -20,16 +20,23 @@ clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
|
|||
# Keep alphabetically sorted by config
|
||||
obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
|
||||
obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o
|
||||
obj-$(CONFIG_CLK_ELIZA_DISPCC) += dispcc-eliza.o
|
||||
obj-$(CONFIG_CLK_ELIZA_GCC) += gcc-eliza.o
|
||||
obj-$(CONFIG_CLK_ELIZA_TCSRCC) += tcsrcc-eliza.o
|
||||
obj-$(CONFIG_CLK_GFM_LPASS_SM8250) += lpass-gfm-sm8250.o
|
||||
obj-$(CONFIG_CLK_GLYMUR_DISPCC) += dispcc-glymur.o
|
||||
obj-$(CONFIG_CLK_GLYMUR_GCC) += gcc-glymur.o
|
||||
obj-$(CONFIG_CLK_GLYMUR_GPUCC) += gpucc-glymur.o gxclkctl-kaanapali.o
|
||||
obj-$(CONFIG_CLK_GLYMUR_TCSRCC) += tcsrcc-glymur.o
|
||||
obj-$(CONFIG_CLK_GLYMUR_VIDEOCC) += videocc-glymur.o
|
||||
obj-$(CONFIG_CLK_KAANAPALI_CAMCC) += cambistmclkcc-kaanapali.o camcc-kaanapali.o
|
||||
obj-$(CONFIG_CLK_KAANAPALI_DISPCC) += dispcc-kaanapali.o
|
||||
obj-$(CONFIG_CLK_KAANAPALI_GCC) += gcc-kaanapali.o
|
||||
obj-$(CONFIG_CLK_KAANAPALI_GPUCC) += gpucc-kaanapali.o gxclkctl-kaanapali.o
|
||||
obj-$(CONFIG_CLK_KAANAPALI_TCSRCC) += tcsrcc-kaanapali.o
|
||||
obj-$(CONFIG_CLK_KAANAPALI_VIDEOCC) += videocc-kaanapali.o
|
||||
obj-$(CONFIG_CLK_NORD_GCC) += gcc-nord.o negcc-nord.o nwgcc-nord.o segcc-nord.o
|
||||
obj-$(CONFIG_CLK_NORD_TCSRCC) += tcsrcc-nord.o
|
||||
obj-$(CONFIG_CLK_X1E80100_CAMCC) += camcc-x1e80100.o
|
||||
obj-$(CONFIG_CLK_X1E80100_DISPCC) += dispcc-x1e80100.o
|
||||
obj-$(CONFIG_CLK_X1E80100_GCC) += gcc-x1e80100.o
|
||||
|
|
@ -43,6 +50,7 @@ obj-$(CONFIG_IPQ_APSS_6018) += apss-ipq6018.o
|
|||
obj-$(CONFIG_IPQ_CMN_PLL) += ipq-cmn-pll.o
|
||||
obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o
|
||||
obj-$(CONFIG_IPQ_GCC_5018) += gcc-ipq5018.o
|
||||
obj-$(CONFIG_IPQ_GCC_5210) += gcc-ipq5210.o
|
||||
obj-$(CONFIG_IPQ_GCC_5332) += gcc-ipq5332.o
|
||||
obj-$(CONFIG_IPQ_GCC_5424) += gcc-ipq5424.o
|
||||
obj-$(CONFIG_IPQ_GCC_6018) += gcc-ipq6018.o
|
||||
|
|
@ -180,6 +188,7 @@ obj-$(CONFIG_SM_GPUCC_8350) += gpucc-sm8350.o
|
|||
obj-$(CONFIG_SM_GPUCC_8450) += gpucc-sm8450.o
|
||||
obj-$(CONFIG_SM_GPUCC_8550) += gpucc-sm8550.o
|
||||
obj-$(CONFIG_SM_GPUCC_8650) += gpucc-sm8650.o
|
||||
obj-$(CONFIG_SM_GPUCC_8750) += gpucc-sm8750.o gxclkctl-kaanapali.o
|
||||
obj-$(CONFIG_SM_GPUCC_MILOS) += gpucc-milos.o
|
||||
obj-$(CONFIG_SM_LPASSCC_6115) += lpasscc-sm6115.o
|
||||
obj-$(CONFIG_SM_TCSRCC_8550) += tcsrcc-sm8550.o
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ static struct clk_alpha_pll *ipa5424_apss_plls[] = {
|
|||
&ipq5424_apss_pll,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data ipa5424_apss_driver_data = {
|
||||
static const struct qcom_cc_driver_data ipa5424_apss_driver_data = {
|
||||
.alpha_plls = ipa5424_apss_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(ipa5424_apss_plls),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/clock/qcom,kaanapali-cambistmclkcc.h>
|
||||
|
|
@ -385,7 +383,7 @@ static struct clk_alpha_pll *cam_bist_mclk_cc_kaanapali_plls[] = {
|
|||
&cam_bist_mclk_cc_pll0,
|
||||
};
|
||||
|
||||
static u32 cam_bist_mclk_cc_kaanapali_critical_cbcrs[] = {
|
||||
static const u32 cam_bist_mclk_cc_kaanapali_critical_cbcrs[] = {
|
||||
0x40e0, /* CAM_BIST_MCLK_CC_SLEEP_CLK */
|
||||
};
|
||||
|
||||
|
|
@ -397,7 +395,7 @@ static const struct regmap_config cam_bist_mclk_cc_kaanapali_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data cam_bist_mclk_cc_kaanapali_driver_data = {
|
||||
static const struct qcom_cc_driver_data cam_bist_mclk_cc_kaanapali_driver_data = {
|
||||
.alpha_plls = cam_bist_mclk_cc_kaanapali_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(cam_bist_mclk_cc_kaanapali_plls),
|
||||
.clk_cbcrs = cam_bist_mclk_cc_kaanapali_critical_cbcrs,
|
||||
|
|
|
|||
|
|
@ -402,7 +402,7 @@ static struct clk_alpha_pll *cam_bist_mclk_cc_sm8750_plls[] = {
|
|||
&cam_bist_mclk_cc_pll0,
|
||||
};
|
||||
|
||||
static u32 cam_bist_mclk_cc_sm8750_critical_cbcrs[] = {
|
||||
static const u32 cam_bist_mclk_cc_sm8750_critical_cbcrs[] = {
|
||||
0x40f8, /* CAM_BIST_MCLK_CC_SLEEP_CLK */
|
||||
};
|
||||
|
||||
|
|
@ -414,7 +414,7 @@ static const struct regmap_config cam_bist_mclk_cc_sm8750_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data cam_bist_mclk_cc_sm8750_driver_data = {
|
||||
static const struct qcom_cc_driver_data cam_bist_mclk_cc_sm8750_driver_data = {
|
||||
.alpha_plls = cam_bist_mclk_cc_sm8750_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(cam_bist_mclk_cc_sm8750_plls),
|
||||
.clk_cbcrs = cam_bist_mclk_cc_sm8750_critical_cbcrs,
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/clock/qcom,kaanapali-camcc.h>
|
||||
|
|
@ -2602,7 +2600,7 @@ static struct clk_alpha_pll *cam_cc_kaanapali_plls[] = {
|
|||
&cam_cc_pll7,
|
||||
};
|
||||
|
||||
static u32 cam_cc_kaanapali_critical_cbcrs[] = {
|
||||
static const u32 cam_cc_kaanapali_critical_cbcrs[] = {
|
||||
0x21398, /* CAM_CC_DRV_AHB_CLK */
|
||||
0x21390, /* CAM_CC_DRV_XO_CLK */
|
||||
0x21364, /* CAM_CC_GDSC_CLK */
|
||||
|
|
@ -2617,7 +2615,7 @@ static const struct regmap_config cam_cc_kaanapali_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data cam_cc_kaanapali_driver_data = {
|
||||
static const struct qcom_cc_driver_data cam_cc_kaanapali_driver_data = {
|
||||
.alpha_plls = cam_cc_kaanapali_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(cam_cc_kaanapali_plls),
|
||||
.clk_cbcrs = cam_cc_kaanapali_critical_cbcrs,
|
||||
|
|
|
|||
|
|
@ -2104,7 +2104,7 @@ static struct clk_alpha_pll *cam_cc_milos_plls[] = {
|
|||
&cam_cc_pll6,
|
||||
};
|
||||
|
||||
static u32 cam_cc_milos_critical_cbcrs[] = {
|
||||
static const u32 cam_cc_milos_critical_cbcrs[] = {
|
||||
0x25038, /* CAM_CC_GDSC_CLK */
|
||||
0x2505c, /* CAM_CC_SLEEP_CLK */
|
||||
};
|
||||
|
|
@ -2117,7 +2117,7 @@ static const struct regmap_config cam_cc_milos_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data cam_cc_milos_driver_data = {
|
||||
static const struct qcom_cc_driver_data cam_cc_milos_driver_data = {
|
||||
.alpha_plls = cam_cc_milos_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(cam_cc_milos_plls),
|
||||
.clk_cbcrs = cam_cc_milos_critical_cbcrs,
|
||||
|
|
|
|||
|
|
@ -1556,7 +1556,7 @@ static const struct regmap_config cam_cc_qcs615_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data cam_cc_qcs615_driver_data = {
|
||||
static const struct qcom_cc_driver_data cam_cc_qcs615_driver_data = {
|
||||
.alpha_plls = cam_cc_qcs615_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(cam_cc_qcs615_plls),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/clock/qcom,sc8180x-camcc.h>
|
||||
|
|
@ -63,6 +62,7 @@ static const struct alpha_pll_config cam_cc_pll0_config = {
|
|||
|
||||
static struct clk_alpha_pll cam_cc_pll0 = {
|
||||
.offset = 0x0,
|
||||
.config = &cam_cc_pll0_config,
|
||||
.vco_table = trion_vco,
|
||||
.num_vco = ARRAY_SIZE(trion_vco),
|
||||
.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION],
|
||||
|
|
@ -138,6 +138,7 @@ static const struct alpha_pll_config cam_cc_pll1_config = {
|
|||
|
||||
static struct clk_alpha_pll cam_cc_pll1 = {
|
||||
.offset = 0x1000,
|
||||
.config = &cam_cc_pll1_config,
|
||||
.vco_table = trion_vco,
|
||||
.num_vco = ARRAY_SIZE(trion_vco),
|
||||
.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION],
|
||||
|
|
@ -167,6 +168,7 @@ static const struct alpha_pll_config cam_cc_pll2_config = {
|
|||
|
||||
static struct clk_alpha_pll cam_cc_pll2 = {
|
||||
.offset = 0x2000,
|
||||
.config = &cam_cc_pll2_config,
|
||||
.vco_table = regera_vco,
|
||||
.num_vco = ARRAY_SIZE(regera_vco),
|
||||
.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_REGERA],
|
||||
|
|
@ -219,6 +221,7 @@ static const struct alpha_pll_config cam_cc_pll3_config = {
|
|||
|
||||
static struct clk_alpha_pll cam_cc_pll3 = {
|
||||
.offset = 0x3000,
|
||||
.config = &cam_cc_pll3_config,
|
||||
.vco_table = trion_vco,
|
||||
.num_vco = ARRAY_SIZE(trion_vco),
|
||||
.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION],
|
||||
|
|
@ -248,6 +251,7 @@ static const struct alpha_pll_config cam_cc_pll4_config = {
|
|||
|
||||
static struct clk_alpha_pll cam_cc_pll4 = {
|
||||
.offset = 0x4000,
|
||||
.config = &cam_cc_pll4_config,
|
||||
.vco_table = trion_vco,
|
||||
.num_vco = ARRAY_SIZE(trion_vco),
|
||||
.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION],
|
||||
|
|
@ -277,6 +281,7 @@ static const struct alpha_pll_config cam_cc_pll5_config = {
|
|||
|
||||
static struct clk_alpha_pll cam_cc_pll5 = {
|
||||
.offset = 0x4078,
|
||||
.config = &cam_cc_pll5_config,
|
||||
.vco_table = trion_vco,
|
||||
.num_vco = ARRAY_SIZE(trion_vco),
|
||||
.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION],
|
||||
|
|
@ -306,6 +311,7 @@ static const struct alpha_pll_config cam_cc_pll6_config = {
|
|||
|
||||
static struct clk_alpha_pll cam_cc_pll6 = {
|
||||
.offset = 0x40f0,
|
||||
.config = &cam_cc_pll6_config,
|
||||
.vco_table = trion_vco,
|
||||
.num_vco = ARRAY_SIZE(trion_vco),
|
||||
.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION],
|
||||
|
|
@ -2813,6 +2819,21 @@ static const struct qcom_reset_map cam_cc_sc8180x_resets[] = {
|
|||
[CAM_CC_MCLK7_BCR] = { 0x50e0 },
|
||||
};
|
||||
|
||||
static struct clk_alpha_pll *cam_cc_sc8180x_plls[] = {
|
||||
&cam_cc_pll0,
|
||||
&cam_cc_pll1,
|
||||
&cam_cc_pll2,
|
||||
&cam_cc_pll3,
|
||||
&cam_cc_pll4,
|
||||
&cam_cc_pll5,
|
||||
&cam_cc_pll6,
|
||||
};
|
||||
|
||||
static const u32 cam_cc_sc8180x_critical_cbcrs[] = {
|
||||
0xc1e4, /* CAM_CC_GDSC_CLK */
|
||||
0xc200, /* CAM_CC_SLEEP_CLK */
|
||||
};
|
||||
|
||||
static const struct regmap_config cam_cc_sc8180x_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
|
|
@ -2821,6 +2842,13 @@ static const struct regmap_config cam_cc_sc8180x_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static const struct qcom_cc_driver_data cam_cc_sc8180x_driver_data = {
|
||||
.alpha_plls = cam_cc_sc8180x_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(cam_cc_sc8180x_plls),
|
||||
.clk_cbcrs = cam_cc_sc8180x_critical_cbcrs,
|
||||
.num_clk_cbcrs = ARRAY_SIZE(cam_cc_sc8180x_critical_cbcrs),
|
||||
};
|
||||
|
||||
static const struct qcom_cc_desc cam_cc_sc8180x_desc = {
|
||||
.config = &cam_cc_sc8180x_regmap_config,
|
||||
.clks = cam_cc_sc8180x_clocks,
|
||||
|
|
@ -2829,6 +2857,8 @@ static const struct qcom_cc_desc cam_cc_sc8180x_desc = {
|
|||
.num_resets = ARRAY_SIZE(cam_cc_sc8180x_resets),
|
||||
.gdscs = cam_cc_sc8180x_gdscs,
|
||||
.num_gdscs = ARRAY_SIZE(cam_cc_sc8180x_gdscs),
|
||||
.use_rpm = true,
|
||||
.driver_data = &cam_cc_sc8180x_driver_data,
|
||||
};
|
||||
|
||||
static const struct of_device_id cam_cc_sc8180x_match_table[] = {
|
||||
|
|
@ -2839,40 +2869,7 @@ MODULE_DEVICE_TABLE(of, cam_cc_sc8180x_match_table);
|
|||
|
||||
static int cam_cc_sc8180x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
ret = devm_pm_runtime_enable(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap = qcom_cc_map(pdev, &cam_cc_sc8180x_desc);
|
||||
if (IS_ERR(regmap)) {
|
||||
pm_runtime_put(&pdev->dev);
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
clk_trion_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config);
|
||||
clk_trion_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config);
|
||||
clk_regera_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config);
|
||||
clk_trion_pll_configure(&cam_cc_pll3, regmap, &cam_cc_pll3_config);
|
||||
clk_trion_pll_configure(&cam_cc_pll4, regmap, &cam_cc_pll4_config);
|
||||
clk_trion_pll_configure(&cam_cc_pll5, regmap, &cam_cc_pll5_config);
|
||||
clk_trion_pll_configure(&cam_cc_pll6, regmap, &cam_cc_pll6_config);
|
||||
|
||||
/* Keep some clocks always enabled */
|
||||
qcom_branch_set_clk_en(regmap, 0xc1e4); /* CAM_CC_GDSC_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0xc200); /* CAM_CC_SLEEP_CLK */
|
||||
|
||||
ret = qcom_cc_really_probe(&pdev->dev, &cam_cc_sc8180x_desc, regmap);
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
return qcom_cc_probe(pdev, &cam_cc_sc8180x_desc);
|
||||
}
|
||||
|
||||
static struct platform_driver cam_cc_sc8180x_driver = {
|
||||
|
|
|
|||
|
|
@ -2915,7 +2915,7 @@ static struct clk_alpha_pll *cam_cc_sm8450_plls[] = {
|
|||
&cam_cc_pll8,
|
||||
};
|
||||
|
||||
static u32 cam_cc_sm8450_critical_cbcrs[] = {
|
||||
static const u32 cam_cc_sm8450_critical_cbcrs[] = {
|
||||
0x1320c, /* CAM_CC_GDSC_CLK */
|
||||
};
|
||||
|
||||
|
|
@ -3030,7 +3030,7 @@ static struct gdsc *cam_cc_sm8450_gdscs[] = {
|
|||
[TITAN_TOP_GDSC] = &titan_top_gdsc,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data cam_cc_sm8450_driver_data = {
|
||||
static const struct qcom_cc_driver_data cam_cc_sm8450_driver_data = {
|
||||
.alpha_plls = cam_cc_sm8450_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(cam_cc_sm8450_plls),
|
||||
.clk_cbcrs = cam_cc_sm8450_critical_cbcrs,
|
||||
|
|
|
|||
|
|
@ -3517,7 +3517,7 @@ static struct clk_alpha_pll *cam_cc_sm8550_plls[] = {
|
|||
&cam_cc_pll12,
|
||||
};
|
||||
|
||||
static u32 cam_cc_sm8550_critical_cbcrs[] = {
|
||||
static const u32 cam_cc_sm8550_critical_cbcrs[] = {
|
||||
0x1419c, /* CAM_CC_GDSC_CLK */
|
||||
0x142cc, /* CAM_CC_SLEEP_CLK */
|
||||
};
|
||||
|
|
@ -3530,7 +3530,7 @@ static const struct regmap_config cam_cc_sm8550_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data cam_cc_sm8550_driver_data = {
|
||||
static const struct qcom_cc_driver_data cam_cc_sm8550_driver_data = {
|
||||
.alpha_plls = cam_cc_sm8550_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(cam_cc_sm8550_plls),
|
||||
.clk_cbcrs = cam_cc_sm8550_critical_cbcrs,
|
||||
|
|
|
|||
|
|
@ -3533,7 +3533,7 @@ static struct clk_alpha_pll *cam_cc_sm8650_plls[] = {
|
|||
&cam_cc_pll10,
|
||||
};
|
||||
|
||||
static u32 cam_cc_sm8650_critical_cbcrs[] = {
|
||||
static const u32 cam_cc_sm8650_critical_cbcrs[] = {
|
||||
0x132ec, /* CAM_CC_GDSC_CLK */
|
||||
0x13308, /* CAM_CC_SLEEP_CLK */
|
||||
0x13314, /* CAM_CC_DRV_XO_CLK */
|
||||
|
|
@ -3548,7 +3548,7 @@ static const struct regmap_config cam_cc_sm8650_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data cam_cc_sm8650_driver_data = {
|
||||
static const struct qcom_cc_driver_data cam_cc_sm8650_driver_data = {
|
||||
.alpha_plls = cam_cc_sm8650_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(cam_cc_sm8650_plls),
|
||||
.clk_cbcrs = cam_cc_sm8650_critical_cbcrs,
|
||||
|
|
|
|||
|
|
@ -2651,7 +2651,7 @@ static struct clk_alpha_pll *cam_cc_sm8750_plls[] = {
|
|||
&cam_cc_pll6,
|
||||
};
|
||||
|
||||
static u32 cam_cc_sm8750_critical_cbcrs[] = {
|
||||
static const u32 cam_cc_sm8750_critical_cbcrs[] = {
|
||||
0x113c4, /* CAM_CC_DRV_AHB_CLK */
|
||||
0x113c0, /* CAM_CC_DRV_XO_CLK */
|
||||
0x1137c, /* CAM_CC_GDSC_CLK */
|
||||
|
|
@ -2666,7 +2666,7 @@ static const struct regmap_config cam_cc_sm8750_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data cam_cc_sm8750_driver_data = {
|
||||
static const struct qcom_cc_driver_data cam_cc_sm8750_driver_data = {
|
||||
.alpha_plls = cam_cc_sm8750_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(cam_cc_sm8750_plls),
|
||||
.clk_cbcrs = cam_cc_sm8750_critical_cbcrs,
|
||||
|
|
|
|||
|
|
@ -2434,7 +2434,7 @@ static struct clk_alpha_pll *cam_cc_x1e80100_plls[] = {
|
|||
&cam_cc_pll8,
|
||||
};
|
||||
|
||||
static u32 cam_cc_x1e80100_critical_cbcrs[] = {
|
||||
static const u32 cam_cc_x1e80100_critical_cbcrs[] = {
|
||||
0x13a9c, /* CAM_CC_GDSC_CLK */
|
||||
0x13ab8, /* CAM_CC_SLEEP_CLK */
|
||||
};
|
||||
|
|
@ -2447,7 +2447,7 @@ static const struct regmap_config cam_cc_x1e80100_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data cam_cc_x1e80100_driver_data = {
|
||||
static const struct qcom_cc_driver_data cam_cc_x1e80100_driver_data = {
|
||||
.alpha_plls = cam_cc_x1e80100_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(cam_cc_x1e80100_plls),
|
||||
.clk_cbcrs = cam_cc_x1e80100_critical_cbcrs,
|
||||
|
|
|
|||
|
|
@ -1117,6 +1117,8 @@ static const struct frac_entry frac_table_pixel[] = {
|
|||
{ 4, 9 },
|
||||
{ 1, 1 },
|
||||
{ 2, 3 },
|
||||
{ 16, 35},
|
||||
{ 4, 15},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -349,6 +349,10 @@ DEFINE_CLK_RPMH_ARC(bi_tcxo, "xo.lvl", 0x3, 2);
|
|||
DEFINE_CLK_RPMH_ARC(bi_tcxo, "xo.lvl", 0x3, 4);
|
||||
DEFINE_CLK_RPMH_ARC(qlink, "qphy.lvl", 0x1, 4);
|
||||
|
||||
DEFINE_CLK_RPMH_VRM(ln_bb_clk1, _a1, "lnbclka1", 1);
|
||||
DEFINE_CLK_RPMH_VRM(ln_bb_clk2, _a1, "lnbclka2", 1);
|
||||
DEFINE_CLK_RPMH_VRM(ln_bb_clk3, _a1, "lnbclka3", 1);
|
||||
|
||||
DEFINE_CLK_RPMH_VRM(ln_bb_clk1, _a2, "lnbclka1", 2);
|
||||
DEFINE_CLK_RPMH_VRM(ln_bb_clk2, _a2, "lnbclka2", 2);
|
||||
DEFINE_CLK_RPMH_VRM(ln_bb_clk3, _a2, "lnbclka3", 2);
|
||||
|
|
@ -372,6 +376,8 @@ DEFINE_CLK_RPMH_VRM(rf_clk3, _d, "rfclkd3", 1);
|
|||
DEFINE_CLK_RPMH_VRM(rf_clk4, _d, "rfclkd4", 1);
|
||||
|
||||
DEFINE_CLK_RPMH_VRM(rf_clk3, _a2, "rfclka3", 2);
|
||||
DEFINE_CLK_RPMH_VRM(rf_clk4, _a2, "rfclka4", 2);
|
||||
DEFINE_CLK_RPMH_VRM(rf_clk5, _a2, "rfclka5", 2);
|
||||
|
||||
DEFINE_CLK_RPMH_VRM(clk1, _a1, "clka1", 1);
|
||||
DEFINE_CLK_RPMH_VRM(clk2, _a1, "clka2", 1);
|
||||
|
|
@ -940,6 +946,44 @@ static const struct clk_rpmh_desc clk_rpmh_kaanapali = {
|
|||
.num_clks = ARRAY_SIZE(kaanapali_rpmh_clocks),
|
||||
};
|
||||
|
||||
static struct clk_hw *eliza_rpmh_clocks[] = {
|
||||
[RPMH_CXO_CLK] = &clk_rpmh_bi_tcxo_div2.hw,
|
||||
[RPMH_CXO_CLK_A] = &clk_rpmh_bi_tcxo_div2_ao.hw,
|
||||
[RPMH_LN_BB_CLK1] = &clk_rpmh_clk6_a2.hw,
|
||||
[RPMH_LN_BB_CLK1_A] = &clk_rpmh_clk6_a2_ao.hw,
|
||||
[RPMH_LN_BB_CLK3] = &clk_rpmh_clk8_a2.hw,
|
||||
[RPMH_LN_BB_CLK3_A] = &clk_rpmh_clk8_a2_ao.hw,
|
||||
[RPMH_RF_CLK1] = &clk_rpmh_rf_clk1_a.hw,
|
||||
[RPMH_RF_CLK1_A] = &clk_rpmh_rf_clk1_a_ao.hw,
|
||||
[RPMH_RF_CLK2] = &clk_rpmh_rf_clk2_a.hw,
|
||||
[RPMH_RF_CLK2_A] = &clk_rpmh_rf_clk2_a_ao.hw,
|
||||
[RPMH_RF_CLK4] = &clk_rpmh_rf_clk4_a2.hw,
|
||||
[RPMH_RF_CLK4_A] = &clk_rpmh_rf_clk4_a2_ao.hw,
|
||||
[RPMH_RF_CLK5] = &clk_rpmh_rf_clk5_a2.hw,
|
||||
[RPMH_RF_CLK5_A] = &clk_rpmh_rf_clk5_a2_ao.hw,
|
||||
[RPMH_IPA_CLK] = &clk_rpmh_ipa.hw,
|
||||
};
|
||||
|
||||
static const struct clk_rpmh_desc clk_rpmh_eliza = {
|
||||
.clks = eliza_rpmh_clocks,
|
||||
.num_clks = ARRAY_SIZE(eliza_rpmh_clocks),
|
||||
};
|
||||
|
||||
static struct clk_hw *nord_rpmh_clocks[] = {
|
||||
[RPMH_CXO_CLK] = &clk_rpmh_bi_tcxo_div1.hw,
|
||||
[RPMH_CXO_CLK_A] = &clk_rpmh_bi_tcxo_div1_ao.hw,
|
||||
[RPMH_LN_BB_CLK2] = &clk_rpmh_ln_bb_clk2_a1.hw,
|
||||
[RPMH_LN_BB_CLK2_A] = &clk_rpmh_ln_bb_clk2_a1_ao.hw,
|
||||
[RPMH_LN_BB_CLK3] = &clk_rpmh_ln_bb_clk3_a1.hw,
|
||||
[RPMH_LN_BB_CLK3_A] = &clk_rpmh_ln_bb_clk3_a1_ao.hw,
|
||||
[RPMH_IPA_CLK] = &clk_rpmh_ipa.hw,
|
||||
};
|
||||
|
||||
static const struct clk_rpmh_desc clk_rpmh_nord = {
|
||||
.clks = nord_rpmh_clocks,
|
||||
.num_clks = ARRAY_SIZE(nord_rpmh_clocks),
|
||||
};
|
||||
|
||||
static struct clk_hw *of_clk_rpmh_hw_get(struct of_phandle_args *clkspec,
|
||||
void *data)
|
||||
{
|
||||
|
|
@ -1029,9 +1073,11 @@ static int clk_rpmh_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const struct of_device_id clk_rpmh_match_table[] = {
|
||||
{ .compatible = "qcom,eliza-rpmh-clk", .data = &clk_rpmh_eliza},
|
||||
{ .compatible = "qcom,glymur-rpmh-clk", .data = &clk_rpmh_glymur},
|
||||
{ .compatible = "qcom,kaanapali-rpmh-clk", .data = &clk_rpmh_kaanapali},
|
||||
{ .compatible = "qcom,milos-rpmh-clk", .data = &clk_rpmh_milos},
|
||||
{ .compatible = "qcom,nord-rpmh-clk", .data = &clk_rpmh_nord},
|
||||
{ .compatible = "qcom,qcs615-rpmh-clk", .data = &clk_rpmh_qcs615},
|
||||
{ .compatible = "qcom,qdu1000-rpmh-clk", .data = &clk_rpmh_qdu1000},
|
||||
{ .compatible = "qcom,sa8775p-rpmh-clk", .data = &clk_rpmh_sa8775p},
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ struct qcom_icc_hws_data {
|
|||
struct qcom_cc_driver_data {
|
||||
struct clk_alpha_pll **alpha_plls;
|
||||
size_t num_alpha_plls;
|
||||
u32 *clk_cbcrs;
|
||||
const u32 *clk_cbcrs;
|
||||
size_t num_clk_cbcrs;
|
||||
const struct clk_rcg_dfs_data *dfs_rcgs;
|
||||
size_t num_dfs_rcgs;
|
||||
|
|
@ -49,7 +49,7 @@ struct qcom_cc_desc {
|
|||
size_t num_icc_hws;
|
||||
unsigned int icc_first_node_id;
|
||||
bool use_rpm;
|
||||
struct qcom_cc_driver_data *driver_data;
|
||||
const struct qcom_cc_driver_data *driver_data;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -6,9 +6,7 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/clock/qcom,glymur-dispcc.h>
|
||||
|
|
@ -417,7 +415,7 @@ static struct clk_rcg2 disp_cc_mdss_dptx1_aux_clk_src = {
|
|||
.parent_data = disp_cc_parent_data_1,
|
||||
.num_parents = ARRAY_SIZE(disp_cc_parent_data_1),
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_dp_ops,
|
||||
.ops = &clk_rcg2_shared_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -747,7 +745,6 @@ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = {
|
|||
&disp_cc_mdss_byte0_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
@ -762,7 +759,6 @@ static struct clk_regmap_div disp_cc_mdss_byte1_div_clk_src = {
|
|||
&disp_cc_mdss_byte1_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
@ -1925,7 +1921,7 @@ static struct clk_alpha_pll *disp_cc_glymur_plls[] = {
|
|||
&disp_cc_pll1,
|
||||
};
|
||||
|
||||
static u32 disp_cc_glymur_critical_cbcrs[] = {
|
||||
static const u32 disp_cc_glymur_critical_cbcrs[] = {
|
||||
0xe07c, /* DISP_CC_SLEEP_CLK */
|
||||
0xe05c, /* DISP_CC_XO_CLK */
|
||||
};
|
||||
|
|
@ -1938,7 +1934,7 @@ static const struct regmap_config disp_cc_glymur_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data disp_cc_glymur_driver_data = {
|
||||
static const struct qcom_cc_driver_data disp_cc_glymur_driver_data = {
|
||||
.alpha_plls = disp_cc_glymur_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(disp_cc_glymur_plls),
|
||||
.clk_cbcrs = disp_cc_glymur_critical_cbcrs,
|
||||
|
|
@ -1978,5 +1974,5 @@ static struct platform_driver disp_cc_glymur_driver = {
|
|||
|
||||
module_platform_driver(disp_cc_glymur_driver);
|
||||
|
||||
MODULE_DESCRIPTION("QTI DISPCC GLYMUR Driver");
|
||||
MODULE_DESCRIPTION("QTI DISPCC Glymur Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/clock/qcom,kaanapali-dispcc.h>
|
||||
|
|
@ -800,7 +798,6 @@ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = {
|
|||
&disp_cc_mdss_byte0_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
@ -815,7 +812,6 @@ static struct clk_regmap_div disp_cc_mdss_byte1_div_clk_src = {
|
|||
&disp_cc_mdss_byte1_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
@ -1890,7 +1886,7 @@ static struct clk_alpha_pll *disp_cc_kaanapali_plls[] = {
|
|||
&disp_cc_pll2,
|
||||
};
|
||||
|
||||
static u32 disp_cc_kaanapali_critical_cbcrs[] = {
|
||||
static const u32 disp_cc_kaanapali_critical_cbcrs[] = {
|
||||
0xe064, /* DISP_CC_SLEEP_CLK */
|
||||
0xe05c, /* DISP_CC_XO_CLK */
|
||||
0xc00c, /* DISP_CC_MDSS_RSCC_AHB_CLK */
|
||||
|
|
@ -1911,7 +1907,7 @@ static void clk_kaanapali_regs_configure(struct device *dev, struct regmap *regm
|
|||
regmap_update_bits(regmap, DISP_CC_MISC_CMD, BIT(4), BIT(4));
|
||||
}
|
||||
|
||||
static struct qcom_cc_driver_data disp_cc_kaanapali_driver_data = {
|
||||
static const struct qcom_cc_driver_data disp_cc_kaanapali_driver_data = {
|
||||
.alpha_plls = disp_cc_kaanapali_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(disp_cc_kaanapali_plls),
|
||||
.clk_cbcrs = disp_cc_kaanapali_critical_cbcrs,
|
||||
|
|
|
|||
|
|
@ -4,12 +4,10 @@
|
|||
* Copyright (c) 2025, Luca Weiss <luca.weiss@fairphone.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
|
|
@ -394,7 +392,6 @@ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = {
|
|||
&disp_cc_mdss_byte0_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
@ -909,7 +906,7 @@ static struct clk_alpha_pll *disp_cc_milos_plls[] = {
|
|||
&disp_cc_pll0,
|
||||
};
|
||||
|
||||
static u32 disp_cc_milos_critical_cbcrs[] = {
|
||||
static const u32 disp_cc_milos_critical_cbcrs[] = {
|
||||
0xe06c, /* DISP_CC_SLEEP_CLK */
|
||||
0xe04c, /* DISP_CC_XO_CLK */
|
||||
};
|
||||
|
|
@ -929,7 +926,7 @@ static void disp_cc_milos_clk_regs_configure(struct device *dev, struct regmap *
|
|||
}
|
||||
|
||||
|
||||
static struct qcom_cc_driver_data disp_cc_milos_driver_data = {
|
||||
static const struct qcom_cc_driver_data disp_cc_milos_driver_data = {
|
||||
.alpha_plls = disp_cc_milos_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(disp_cc_milos_plls),
|
||||
.clk_cbcrs = disp_cc_milos_critical_cbcrs,
|
||||
|
|
|
|||
|
|
@ -739,7 +739,7 @@ static struct clk_alpha_pll *disp_cc_qcs615_plls[] = {
|
|||
&disp_cc_pll0,
|
||||
};
|
||||
|
||||
static u32 disp_cc_qcs615_critical_cbcrs[] = {
|
||||
static const u32 disp_cc_qcs615_critical_cbcrs[] = {
|
||||
0x6054, /* DISP_CC_XO_CLK */
|
||||
};
|
||||
|
||||
|
|
@ -751,7 +751,7 @@ static const struct regmap_config disp_cc_qcs615_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data disp_cc_qcs615_driver_data = {
|
||||
static const struct qcom_cc_driver_data disp_cc_qcs615_driver_data = {
|
||||
.alpha_plls = disp_cc_qcs615_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(disp_cc_qcs615_plls),
|
||||
.clk_cbcrs = disp_cc_qcs615_critical_cbcrs,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "clk-regmap-divider.h"
|
||||
#include "common.h"
|
||||
#include "gdsc.h"
|
||||
#include "reset.h"
|
||||
|
||||
enum {
|
||||
P_BI_TCXO,
|
||||
|
|
@ -636,6 +637,11 @@ static struct gdsc mdss_gdsc = {
|
|||
.flags = HW_CTRL,
|
||||
};
|
||||
|
||||
static const struct qcom_reset_map disp_cc_sc7180_resets[] = {
|
||||
[DISP_CC_MDSS_CORE_BCR] = { 0x2000 },
|
||||
[DISP_CC_MDSS_RSCC_BCR] = { 0x4000 },
|
||||
};
|
||||
|
||||
static struct gdsc *disp_cc_sc7180_gdscs[] = {
|
||||
[MDSS_GDSC] = &mdss_gdsc,
|
||||
};
|
||||
|
|
@ -687,6 +693,8 @@ static const struct qcom_cc_desc disp_cc_sc7180_desc = {
|
|||
.config = &disp_cc_sc7180_regmap_config,
|
||||
.clks = disp_cc_sc7180_clocks,
|
||||
.num_clks = ARRAY_SIZE(disp_cc_sc7180_clocks),
|
||||
.resets = disp_cc_sc7180_resets,
|
||||
.num_resets = ARRAY_SIZE(disp_cc_sc7180_resets),
|
||||
.gdscs = disp_cc_sc7180_gdscs,
|
||||
.num_gdscs = ARRAY_SIZE(disp_cc_sc7180_gdscs),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1160,7 +1160,6 @@ static struct clk_regmap_div disp0_cc_mdss_byte0_div_clk_src = {
|
|||
&disp0_cc_mdss_byte0_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
@ -1175,7 +1174,6 @@ static struct clk_regmap_div disp1_cc_mdss_byte0_div_clk_src = {
|
|||
&disp1_cc_mdss_byte0_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
@ -1190,7 +1188,6 @@ static struct clk_regmap_div disp0_cc_mdss_byte1_div_clk_src = {
|
|||
&disp0_cc_mdss_byte1_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
@ -1205,7 +1202,6 @@ static struct clk_regmap_div disp1_cc_mdss_byte1_div_clk_src = {
|
|||
&disp1_cc_mdss_byte1_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -335,7 +335,6 @@ static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = {
|
|||
&disp_cc_mdss_byte0_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "clk-regmap-divider.h"
|
||||
#include "common.h"
|
||||
#include "gdsc.h"
|
||||
#include "reset.h"
|
||||
|
||||
enum {
|
||||
DT_BI_TCXO,
|
||||
|
|
@ -511,6 +512,10 @@ static struct clk_branch disp_cc_sleep_clk = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct qcom_reset_map disp_cc_sm6115_resets[] = {
|
||||
[DISP_CC_MDSS_CORE_BCR] = { 0x2000 },
|
||||
};
|
||||
|
||||
static struct gdsc mdss_gdsc = {
|
||||
.gdscr = 0x3000,
|
||||
.pd = {
|
||||
|
|
@ -561,6 +566,8 @@ static const struct qcom_cc_desc disp_cc_sm6115_desc = {
|
|||
.config = &disp_cc_sm6115_regmap_config,
|
||||
.clks = disp_cc_sm6115_clocks,
|
||||
.num_clks = ARRAY_SIZE(disp_cc_sm6115_clocks),
|
||||
.resets = disp_cc_sm6115_resets,
|
||||
.num_resets = ARRAY_SIZE(disp_cc_sm6115_resets),
|
||||
.gdscs = disp_cc_sm6115_gdscs,
|
||||
.num_gdscs = ARRAY_SIZE(disp_cc_sm6115_gdscs),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "clk-regmap.h"
|
||||
#include "common.h"
|
||||
#include "gdsc.h"
|
||||
#include "reset.h"
|
||||
|
||||
enum {
|
||||
P_BI_TCXO,
|
||||
|
|
@ -607,6 +608,10 @@ static struct clk_branch disp_cc_xo_clk = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct qcom_reset_map disp_cc_sm6125_resets[] = {
|
||||
[DISP_CC_MDSS_CORE_BCR] = { 0x2000 },
|
||||
};
|
||||
|
||||
static struct gdsc mdss_gdsc = {
|
||||
.gdscr = 0x3000,
|
||||
.pd = {
|
||||
|
|
@ -663,6 +668,8 @@ static const struct qcom_cc_desc disp_cc_sm6125_desc = {
|
|||
.config = &disp_cc_sm6125_regmap_config,
|
||||
.clks = disp_cc_sm6125_clocks,
|
||||
.num_clks = ARRAY_SIZE(disp_cc_sm6125_clocks),
|
||||
.resets = disp_cc_sm6125_resets,
|
||||
.num_resets = ARRAY_SIZE(disp_cc_sm6125_resets),
|
||||
.gdscs = disp_cc_sm6125_gdscs,
|
||||
.num_gdscs = ARRAY_SIZE(disp_cc_sm6125_gdscs),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -578,7 +578,7 @@ static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = {
|
|||
.name = "disp_cc_mdss_pclk0_clk_src",
|
||||
.parent_data = disp_cc_parent_data_6,
|
||||
.num_parents = ARRAY_SIZE(disp_cc_parent_data_6),
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
|
||||
.ops = &clk_pixel_ops,
|
||||
},
|
||||
};
|
||||
|
|
@ -592,7 +592,7 @@ static struct clk_rcg2 disp_cc_mdss_pclk1_clk_src = {
|
|||
.name = "disp_cc_mdss_pclk1_clk_src",
|
||||
.parent_data = disp_cc_parent_data_6,
|
||||
.num_parents = ARRAY_SIZE(disp_cc_parent_data_6),
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
|
||||
.ops = &clk_pixel_ops,
|
||||
},
|
||||
};
|
||||
|
|
@ -632,7 +632,7 @@ static struct clk_rcg2 disp_cc_mdss_vsync_clk_src = {
|
|||
.parent_data = disp_cc_parent_data_1,
|
||||
.num_parents = ARRAY_SIZE(disp_cc_parent_data_1),
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_rcg2_ops,
|
||||
.ops = &clk_rcg2_shared_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ static struct clk_rcg2 disp_cc_mdss_dptx1_aux_clk_src = {
|
|||
.parent_data = disp_cc_parent_data_1,
|
||||
.num_parents = ARRAY_SIZE(disp_cc_parent_data_1),
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_dp_ops,
|
||||
.ops = &clk_rcg2_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -591,7 +591,6 @@ static struct clk_regmap_div mdss_0_disp_cc_mdss_byte0_div_clk_src = {
|
|||
&mdss_0_disp_cc_mdss_byte0_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
@ -606,7 +605,6 @@ static struct clk_regmap_div mdss_0_disp_cc_mdss_byte1_div_clk_src = {
|
|||
&mdss_0_disp_cc_mdss_byte1_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -591,7 +591,6 @@ static struct clk_regmap_div mdss_1_disp_cc_mdss_byte0_div_clk_src = {
|
|||
&mdss_1_disp_cc_mdss_byte0_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
@ -606,7 +605,6 @@ static struct clk_regmap_div mdss_1_disp_cc_mdss_byte1_div_clk_src = {
|
|||
&mdss_1_disp_cc_mdss_byte1_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ops,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -6,7 +6,6 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
|
|
@ -8507,6 +8506,7 @@ static const struct qcom_reset_map gcc_glymur_resets[] = {
|
|||
[GCC_VIDEO_AXI0_CLK_ARES] = { 0x3201c, 2 },
|
||||
[GCC_VIDEO_AXI1_CLK_ARES] = { 0x32044, 2 },
|
||||
[GCC_VIDEO_BCR] = { 0x32000 },
|
||||
[GCC_VIDEO_AXI0C_CLK_ARES] = { 0x32030, 2 },
|
||||
};
|
||||
|
||||
static const struct clk_rcg_dfs_data gcc_dfs_clocks[] = {
|
||||
|
|
@ -8538,7 +8538,7 @@ static const struct clk_rcg_dfs_data gcc_dfs_clocks[] = {
|
|||
DEFINE_RCG_DFS(gcc_qupv3_wrap2_s7_clk_src),
|
||||
};
|
||||
|
||||
static u32 gcc_glymur_critical_cbcrs[] = {
|
||||
static const u32 gcc_glymur_critical_cbcrs[] = {
|
||||
0x26004, /* GCC_CAMERA_AHB_CLK */
|
||||
0x26040, /* GCC_CAMERA_XO_CLK */
|
||||
0x27004, /* GCC_DISP_AHB_CLK */
|
||||
|
|
@ -8561,7 +8561,7 @@ static void clk_glymur_regs_configure(struct device *dev, struct regmap *regmap)
|
|||
qcom_branch_set_force_mem_core(regmap, gcc_ufs_phy_ice_core_clk, true);
|
||||
}
|
||||
|
||||
static struct qcom_cc_driver_data gcc_glymur_driver_data = {
|
||||
static const struct qcom_cc_driver_data gcc_glymur_driver_data = {
|
||||
.clk_cbcrs = gcc_glymur_critical_cbcrs,
|
||||
.num_clk_cbcrs = ARRAY_SIZE(gcc_glymur_critical_cbcrs),
|
||||
.dfs_rcgs = gcc_dfs_clocks,
|
||||
|
|
@ -8611,5 +8611,5 @@ static void __exit gcc_glymur_exit(void)
|
|||
}
|
||||
module_exit(gcc_glymur_exit);
|
||||
|
||||
MODULE_DESCRIPTION("QTI GCC GLYMUR Driver");
|
||||
MODULE_DESCRIPTION("QTI GCC Glymur Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -400,7 +400,7 @@ static struct clk_branch gcc_xo_clk_src = {
|
|||
.fw_name = "xo",
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
|
|
@ -3458,7 +3457,7 @@ static const struct clk_rcg_dfs_data gcc_dfs_clocks[] = {
|
|||
DEFINE_RCG_DFS(gcc_qupv3_wrap4_s4_clk_src),
|
||||
};
|
||||
|
||||
static u32 gcc_kaanapali_critical_cbcrs[] = {
|
||||
static const u32 gcc_kaanapali_critical_cbcrs[] = {
|
||||
0xa0004, /* GCC_CAM_BIST_MCLK_AHB_CLK */
|
||||
0x26004, /* GCC_CAMERA_AHB_CLK */
|
||||
0x2603c, /* GCC_CAMERA_XO_CLK */
|
||||
|
|
@ -3486,7 +3485,7 @@ static void clk_kaanapali_regs_configure(struct device *dev, struct regmap *regm
|
|||
qcom_branch_set_force_mem_core(regmap, gcc_ufs_phy_ice_core_clk, true);
|
||||
}
|
||||
|
||||
static struct qcom_cc_driver_data gcc_kaanapali_driver_data = {
|
||||
static const struct qcom_cc_driver_data gcc_kaanapali_driver_data = {
|
||||
.clk_cbcrs = gcc_kaanapali_critical_cbcrs,
|
||||
.num_clk_cbcrs = ARRAY_SIZE(gcc_kaanapali_critical_cbcrs),
|
||||
.dfs_rcgs = gcc_dfs_clocks,
|
||||
|
|
|
|||
|
|
@ -3152,7 +3152,7 @@ static struct gdsc *gcc_milos_gdscs[] = {
|
|||
[USB3_PHY_GDSC] = &usb3_phy_gdsc,
|
||||
};
|
||||
|
||||
static u32 gcc_milos_critical_cbcrs[] = {
|
||||
static const u32 gcc_milos_critical_cbcrs[] = {
|
||||
0x26004, /* GCC_CAMERA_AHB_CLK */
|
||||
0x26018, /* GCC_CAMERA_HF_XO_CLK */
|
||||
0x2601c, /* GCC_CAMERA_SF_XO_CLK */
|
||||
|
|
@ -3171,7 +3171,7 @@ static const struct regmap_config gcc_milos_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static struct qcom_cc_driver_data gcc_milos_driver_data = {
|
||||
static const struct qcom_cc_driver_data gcc_milos_driver_data = {
|
||||
.clk_cbcrs = gcc_milos_critical_cbcrs,
|
||||
.num_clk_cbcrs = ARRAY_SIZE(gcc_milos_critical_cbcrs),
|
||||
.dfs_rcgs = gcc_milos_dfs_clocks,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -4172,7 +4172,7 @@ static struct gdsc usb30_sec_gdsc = {
|
|||
.pd = {
|
||||
.name = "usb30_sec_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.pwrsts = PWRSTS_RET_ON,
|
||||
.flags = POLL_CFG_GDSCR,
|
||||
};
|
||||
|
||||
|
|
@ -4190,7 +4190,7 @@ static struct gdsc usb30_prim_gdsc = {
|
|||
.pd = {
|
||||
.name = "usb30_prim_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.pwrsts = PWRSTS_RET_ON,
|
||||
.flags = POLL_CFG_GDSCR,
|
||||
};
|
||||
|
||||
|
|
@ -4199,7 +4199,7 @@ static struct gdsc pcie_0_gdsc = {
|
|||
.pd = {
|
||||
.name = "pcie_0_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.pwrsts = PWRSTS_RET_ON,
|
||||
.flags = POLL_CFG_GDSCR,
|
||||
};
|
||||
|
||||
|
|
@ -4226,7 +4226,7 @@ static struct gdsc pcie_1_gdsc = {
|
|||
.pd = {
|
||||
.name = "pcie_1_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.pwrsts = PWRSTS_RET_ON,
|
||||
.flags = POLL_CFG_GDSCR,
|
||||
};
|
||||
|
||||
|
|
@ -4235,7 +4235,7 @@ static struct gdsc pcie_2_gdsc = {
|
|||
.pd = {
|
||||
.name = "pcie_2_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.pwrsts = PWRSTS_RET_ON,
|
||||
.flags = POLL_CFG_GDSCR,
|
||||
};
|
||||
|
||||
|
|
@ -4253,7 +4253,7 @@ static struct gdsc pcie_3_gdsc = {
|
|||
.pd = {
|
||||
.name = "pcie_3_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.pwrsts = PWRSTS_RET_ON,
|
||||
.flags = POLL_CFG_GDSCR,
|
||||
};
|
||||
|
||||
|
|
@ -4262,10 +4262,55 @@ static struct gdsc usb30_mp_gdsc = {
|
|||
.pd = {
|
||||
.name = "usb30_mp_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.pwrsts = PWRSTS_RET_ON,
|
||||
.flags = POLL_CFG_GDSCR,
|
||||
};
|
||||
|
||||
static struct gdsc hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc = {
|
||||
.gdscr = 0x7d050,
|
||||
.pd = {
|
||||
.name = "hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.flags = VOTABLE,
|
||||
};
|
||||
|
||||
static struct gdsc hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc = {
|
||||
.gdscr = 0x7d058,
|
||||
.pd = {
|
||||
.name = "hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.flags = VOTABLE,
|
||||
};
|
||||
|
||||
static struct gdsc hlos1_vote_mmnoc_mmu_tbu_sf_gdsc = {
|
||||
.gdscr = 0x7d054,
|
||||
.pd = {
|
||||
.name = "hlos1_vote_mmnoc_mmu_tbu_sf_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.flags = VOTABLE,
|
||||
};
|
||||
|
||||
static struct gdsc hlos1_vote_turing_mmu_tbu0_gdsc = {
|
||||
.gdscr = 0x7d05c,
|
||||
.pd = {
|
||||
.name = "hlos1_vote_turing_mmu_tbu0_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.flags = VOTABLE,
|
||||
};
|
||||
|
||||
static struct gdsc hlos1_vote_turing_mmu_tbu1_gdsc = {
|
||||
.gdscr = 0x7d060,
|
||||
.pd = {
|
||||
.name = "hlos1_vote_turing_mmu_tbu1_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.flags = VOTABLE,
|
||||
};
|
||||
|
||||
static struct clk_regmap *gcc_sc8180x_clocks[] = {
|
||||
[GCC_AGGRE_NOC_PCIE_TBU_CLK] = &gcc_aggre_noc_pcie_tbu_clk.clkr,
|
||||
[GCC_AGGRE_UFS_CARD_AXI_CLK] = &gcc_aggre_ufs_card_axi_clk.clkr,
|
||||
|
|
@ -4560,7 +4605,7 @@ static const struct qcom_reset_map gcc_sc8180x_resets[] = {
|
|||
[GCC_VIDEO_AXI1_CLK_BCR] = { .reg = 0xb028, .bit = 2, .udelay = 150 },
|
||||
};
|
||||
|
||||
static const struct clk_rcg_dfs_data gcc_dfs_clocks[] = {
|
||||
static const struct clk_rcg_dfs_data gcc_sc8180x_dfs_clocks[] = {
|
||||
DEFINE_RCG_DFS(gcc_qupv3_wrap0_s0_clk_src),
|
||||
DEFINE_RCG_DFS(gcc_qupv3_wrap0_s1_clk_src),
|
||||
DEFINE_RCG_DFS(gcc_qupv3_wrap0_s2_clk_src),
|
||||
|
|
@ -4595,6 +4640,24 @@ static struct gdsc *gcc_sc8180x_gdscs[] = {
|
|||
[USB30_MP_GDSC] = &usb30_mp_gdsc,
|
||||
[USB30_PRIM_GDSC] = &usb30_prim_gdsc,
|
||||
[USB30_SEC_GDSC] = &usb30_sec_gdsc,
|
||||
[HLOS1_VOTE_MMNOC_MMU_TBU_HF0_GDSC] = &hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc,
|
||||
[HLOS1_VOTE_MMNOC_MMU_TBU_HF1_GDSC] = &hlos1_vote_mmnoc_mmu_tbu_hf1_gdsc,
|
||||
[HLOS1_VOTE_MMNOC_MMU_TBU_SF_GDSC] = &hlos1_vote_mmnoc_mmu_tbu_sf_gdsc,
|
||||
[HLOS1_VOTE_TURING_MMU_TBU0_GDSC] = &hlos1_vote_turing_mmu_tbu0_gdsc,
|
||||
[HLOS1_VOTE_TURING_MMU_TBU1_GDSC] = &hlos1_vote_turing_mmu_tbu1_gdsc,
|
||||
};
|
||||
|
||||
static const u32 gcc_sc8180x_critical_cbcrs[] = {
|
||||
0xb004, /* GCC_VIDEO_AHB_CLK */
|
||||
0xb008, /* GCC_CAMERA_AHB_CLK */
|
||||
0xb00c, /* GCC_DISP_AHB_CLK */
|
||||
0xb040, /* GCC_VIDEO_XO_CLK */
|
||||
0xb044, /* GCC_CAMERA_XO_CLK */
|
||||
0xb048, /* GCC_DISP_XO_CLK */
|
||||
0x48004, /* GCC_CPUSS_GNOC_CLK */
|
||||
0x48190, /* GCC_CPUSS_DVM_BUS_CLK */
|
||||
0x4d004, /* GCC_NPU_CFG_AHB_CLK */
|
||||
0x71004, /* GCC_GPU_CFG_AHB_CLK */
|
||||
};
|
||||
|
||||
static const struct regmap_config gcc_sc8180x_regmap_config = {
|
||||
|
|
@ -4605,6 +4668,21 @@ static const struct regmap_config gcc_sc8180x_regmap_config = {
|
|||
.fast_io = true,
|
||||
};
|
||||
|
||||
static void clk_sc8180x_regs_configure(struct device *dev, struct regmap *regmap)
|
||||
{
|
||||
/* Disable the GPLL0 active input to NPU and GPU via MISC registers */
|
||||
regmap_update_bits(regmap, 0x4d110, 0x3, 0x3);
|
||||
regmap_update_bits(regmap, 0x71028, 0x3, 0x3);
|
||||
}
|
||||
|
||||
static const struct qcom_cc_driver_data gcc_sc8180x_driver_data = {
|
||||
.clk_cbcrs = gcc_sc8180x_critical_cbcrs,
|
||||
.num_clk_cbcrs = ARRAY_SIZE(gcc_sc8180x_critical_cbcrs),
|
||||
.dfs_rcgs = gcc_sc8180x_dfs_clocks,
|
||||
.num_dfs_rcgs = ARRAY_SIZE(gcc_sc8180x_dfs_clocks),
|
||||
.clk_regs_configure = clk_sc8180x_regs_configure,
|
||||
};
|
||||
|
||||
static const struct qcom_cc_desc gcc_sc8180x_desc = {
|
||||
.config = &gcc_sc8180x_regmap_config,
|
||||
.clks = gcc_sc8180x_clocks,
|
||||
|
|
@ -4613,6 +4691,8 @@ static const struct qcom_cc_desc gcc_sc8180x_desc = {
|
|||
.num_resets = ARRAY_SIZE(gcc_sc8180x_resets),
|
||||
.gdscs = gcc_sc8180x_gdscs,
|
||||
.num_gdscs = ARRAY_SIZE(gcc_sc8180x_gdscs),
|
||||
.use_rpm = true,
|
||||
.driver_data = &gcc_sc8180x_driver_data,
|
||||
};
|
||||
|
||||
static const struct of_device_id gcc_sc8180x_match_table[] = {
|
||||
|
|
@ -4623,35 +4703,7 @@ MODULE_DEVICE_TABLE(of, gcc_sc8180x_match_table);
|
|||
|
||||
static int gcc_sc8180x_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
regmap = qcom_cc_map(pdev, &gcc_sc8180x_desc);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
/* Keep some clocks always-on */
|
||||
qcom_branch_set_clk_en(regmap, 0xb004); /* GCC_VIDEO_AHB_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0xb008); /* GCC_CAMERA_AHB_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0xb00c); /* GCC_DISP_AHB_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0xb040); /* GCC_VIDEO_XO_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0xb044); /* GCC_CAMERA_XO_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0xb048); /* GCC_DISP_XO_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0x48004); /* GCC_CPUSS_GNOC_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0x48190); /* GCC_CPUSS_DVM_BUS_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0x4d004); /* GCC_NPU_CFG_AHB_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0x71004); /* GCC_GPU_CFG_AHB_CLK */
|
||||
|
||||
/* Disable the GPLL0 active input to NPU and GPU via MISC registers */
|
||||
regmap_update_bits(regmap, 0x4d110, 0x3, 0x3);
|
||||
regmap_update_bits(regmap, 0x71028, 0x3, 0x3);
|
||||
|
||||
ret = qcom_cc_register_rcg_dfs(regmap, gcc_dfs_clocks,
|
||||
ARRAY_SIZE(gcc_dfs_clocks));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return qcom_cc_really_probe(&pdev->dev, &gcc_sc8180x_desc, regmap);
|
||||
return qcom_cc_probe(pdev, &gcc_sc8180x_desc);
|
||||
}
|
||||
|
||||
static struct platform_driver gcc_sc8180x_driver = {
|
||||
|
|
|
|||
|
|
@ -7480,6 +7480,7 @@ static int gcc_x1e80100_probe(struct platform_device *pdev)
|
|||
qcom_branch_set_clk_en(regmap, 0x32004); /* GCC_VIDEO_AHB_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0x32030); /* GCC_VIDEO_XO_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0x71004); /* GCC_GPU_CFG_AHB_CLK */
|
||||
qcom_branch_set_clk_en(regmap, 0x7d01c); /* GCC_HLOS1_VOTE_AGGRE_NOC_MMU_USB_QTB_CLK */
|
||||
|
||||
/* Clear GDSC_SLEEP_ENA_VOTE to stop votes being auto-removed in sleep. */
|
||||
regmap_write(regmap, 0x52224, 0x0);
|
||||
|
|
|
|||
|
|
@ -518,10 +518,20 @@ static int gdsc_add_subdomain_list(struct dev_pm_domain_list *pd_list,
|
|||
|
||||
ret = pm_genpd_add_subdomain(genpd, subdomain);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto remove_added_subdomains;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
remove_added_subdomains:
|
||||
for (i--; i >= 0; i--) {
|
||||
struct device *dev = pd_list->pd_devs[i];
|
||||
struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
|
||||
|
||||
pm_genpd_remove_subdomain(genpd, subdomain);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gdsc_remove_subdomain_list(struct dev_pm_domain_list *pd_list,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,618 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <dt-bindings/clock/qcom,glymur-gpucc.h>
|
||||
|
||||
#include "clk-alpha-pll.h"
|
||||
#include "clk-branch.h"
|
||||
#include "clk-pll.h"
|
||||
#include "clk-rcg.h"
|
||||
#include "clk-regmap.h"
|
||||
#include "clk-regmap-divider.h"
|
||||
#include "clk-regmap-mux.h"
|
||||
#include "common.h"
|
||||
#include "gdsc.h"
|
||||
#include "reset.h"
|
||||
|
||||
enum {
|
||||
DT_BI_TCXO,
|
||||
DT_GPLL0_OUT_MAIN,
|
||||
DT_GPLL0_OUT_MAIN_DIV,
|
||||
};
|
||||
|
||||
enum {
|
||||
P_BI_TCXO,
|
||||
P_GPLL0_OUT_MAIN,
|
||||
P_GPLL0_OUT_MAIN_DIV,
|
||||
P_GPU_CC_PLL0_OUT_EVEN,
|
||||
P_GPU_CC_PLL0_OUT_MAIN,
|
||||
P_GPU_CC_PLL0_OUT_ODD,
|
||||
};
|
||||
|
||||
static const struct pll_vco taycan_eko_t_vco[] = {
|
||||
{ 249600000, 2500000000, 0 },
|
||||
};
|
||||
|
||||
/* 1150.0 MHz Configuration */
|
||||
static const struct alpha_pll_config gpu_cc_pll0_config = {
|
||||
.l = 0x3b,
|
||||
.alpha = 0xe555,
|
||||
.config_ctl_val = 0x25c400e7,
|
||||
.config_ctl_hi_val = 0x0a8060e0,
|
||||
.config_ctl_hi1_val = 0xf51dea20,
|
||||
.user_ctl_val = 0x00000408,
|
||||
.user_ctl_hi_val = 0x00000002,
|
||||
};
|
||||
|
||||
static struct clk_alpha_pll gpu_cc_pll0 = {
|
||||
.offset = 0x0,
|
||||
.config = &gpu_cc_pll0_config,
|
||||
.vco_table = taycan_eko_t_vco,
|
||||
.num_vco = ARRAY_SIZE(taycan_eko_t_vco),
|
||||
.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EKO_T],
|
||||
.clkr = {
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_pll0",
|
||||
.parent_data = &(const struct clk_parent_data) {
|
||||
.index = DT_BI_TCXO,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.ops = &clk_alpha_pll_taycan_eko_t_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct clk_div_table post_div_table_gpu_cc_pll0_out_even[] = {
|
||||
{ 0x1, 2 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct clk_alpha_pll_postdiv gpu_cc_pll0_out_even = {
|
||||
.offset = 0x0,
|
||||
.post_div_shift = 10,
|
||||
.post_div_table = post_div_table_gpu_cc_pll0_out_even,
|
||||
.num_post_div = ARRAY_SIZE(post_div_table_gpu_cc_pll0_out_even),
|
||||
.width = 4,
|
||||
.regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EKO_T],
|
||||
.clkr.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_pll0_out_even",
|
||||
.parent_hws = (const struct clk_hw*[]) {
|
||||
&gpu_cc_pll0.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_alpha_pll_postdiv_taycan_eko_t_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct parent_map gpu_cc_parent_map_0[] = {
|
||||
{ P_BI_TCXO, 0 },
|
||||
{ P_GPLL0_OUT_MAIN, 5 },
|
||||
{ P_GPLL0_OUT_MAIN_DIV, 6 },
|
||||
};
|
||||
|
||||
static const struct clk_parent_data gpu_cc_parent_data_0[] = {
|
||||
{ .index = DT_BI_TCXO },
|
||||
{ .index = DT_GPLL0_OUT_MAIN },
|
||||
{ .index = DT_GPLL0_OUT_MAIN_DIV },
|
||||
};
|
||||
|
||||
static const struct parent_map gpu_cc_parent_map_1[] = {
|
||||
{ P_BI_TCXO, 0 },
|
||||
{ P_GPU_CC_PLL0_OUT_MAIN, 1 },
|
||||
{ P_GPU_CC_PLL0_OUT_EVEN, 2 },
|
||||
{ P_GPU_CC_PLL0_OUT_ODD, 3 },
|
||||
{ P_GPLL0_OUT_MAIN, 5 },
|
||||
{ P_GPLL0_OUT_MAIN_DIV, 6 },
|
||||
};
|
||||
|
||||
static const struct clk_parent_data gpu_cc_parent_data_1[] = {
|
||||
{ .index = DT_BI_TCXO },
|
||||
{ .hw = &gpu_cc_pll0.clkr.hw },
|
||||
{ .hw = &gpu_cc_pll0_out_even.clkr.hw },
|
||||
{ .hw = &gpu_cc_pll0.clkr.hw },
|
||||
{ .index = DT_GPLL0_OUT_MAIN },
|
||||
{ .index = DT_GPLL0_OUT_MAIN_DIV },
|
||||
};
|
||||
|
||||
static const struct freq_tbl ftbl_gpu_cc_ff_clk_src[] = {
|
||||
F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct clk_rcg2 gpu_cc_ff_clk_src = {
|
||||
.cmd_rcgr = 0x9474,
|
||||
.mnd_width = 0,
|
||||
.hid_width = 5,
|
||||
.parent_map = gpu_cc_parent_map_0,
|
||||
.freq_tbl = ftbl_gpu_cc_ff_clk_src,
|
||||
.clkr.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_ff_clk_src",
|
||||
.parent_data = gpu_cc_parent_data_0,
|
||||
.num_parents = ARRAY_SIZE(gpu_cc_parent_data_0),
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_rcg2_shared_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src[] = {
|
||||
F(19200000, P_BI_TCXO, 1, 0, 0),
|
||||
F(575000000, P_GPU_CC_PLL0_OUT_EVEN, 1, 0, 0),
|
||||
F(700000000, P_GPU_CC_PLL0_OUT_EVEN, 1, 0, 0),
|
||||
F(725000000, P_GPU_CC_PLL0_OUT_EVEN, 1, 0, 0),
|
||||
F(750000000, P_GPU_CC_PLL0_OUT_EVEN, 1, 0, 0),
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct clk_rcg2 gpu_cc_gmu_clk_src = {
|
||||
.cmd_rcgr = 0x9318,
|
||||
.mnd_width = 0,
|
||||
.hid_width = 5,
|
||||
.parent_map = gpu_cc_parent_map_1,
|
||||
.freq_tbl = ftbl_gpu_cc_gmu_clk_src,
|
||||
.clkr.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_gmu_clk_src",
|
||||
.parent_data = gpu_cc_parent_data_1,
|
||||
.num_parents = ARRAY_SIZE(gpu_cc_parent_data_1),
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_rcg2_shared_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct freq_tbl ftbl_gpu_cc_hub_clk_src[] = {
|
||||
F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
|
||||
F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0),
|
||||
F(400000000, P_GPLL0_OUT_MAIN, 1.5, 0, 0),
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct clk_rcg2 gpu_cc_hub_clk_src = {
|
||||
.cmd_rcgr = 0x93f0,
|
||||
.mnd_width = 0,
|
||||
.hid_width = 5,
|
||||
.parent_map = gpu_cc_parent_map_1,
|
||||
.freq_tbl = ftbl_gpu_cc_hub_clk_src,
|
||||
.clkr.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_hub_clk_src",
|
||||
.parent_data = gpu_cc_parent_data_1,
|
||||
.num_parents = ARRAY_SIZE(gpu_cc_parent_data_1),
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_rcg2_shared_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_regmap_div gpu_cc_hub_div_clk_src = {
|
||||
.reg = 0x9430,
|
||||
.shift = 0,
|
||||
.width = 4,
|
||||
.clkr.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_hub_div_clk_src",
|
||||
.parent_hws = (const struct clk_hw*[]) {
|
||||
&gpu_cc_hub_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_regmap_div_ro_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_ahb_clk = {
|
||||
.halt_reg = 0x90bc,
|
||||
.halt_check = BRANCH_HALT_DELAY,
|
||||
.clkr = {
|
||||
.enable_reg = 0x90bc,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_ahb_clk",
|
||||
.parent_hws = (const struct clk_hw*[]) {
|
||||
&gpu_cc_hub_div_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_cx_accu_shift_clk = {
|
||||
.halt_reg = 0x9108,
|
||||
.halt_check = BRANCH_HALT_VOTED,
|
||||
.clkr = {
|
||||
.enable_reg = 0x9108,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_cx_accu_shift_clk",
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_cx_ff_clk = {
|
||||
.halt_reg = 0x90ec,
|
||||
.halt_check = BRANCH_HALT,
|
||||
.clkr = {
|
||||
.enable_reg = 0x90ec,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_cx_ff_clk",
|
||||
.parent_hws = (const struct clk_hw*[]) {
|
||||
&gpu_cc_ff_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_cx_gmu_clk = {
|
||||
.halt_reg = 0x90d4,
|
||||
.halt_check = BRANCH_HALT_VOTED,
|
||||
.clkr = {
|
||||
.enable_reg = 0x90d4,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_cx_gmu_clk",
|
||||
.parent_hws = (const struct clk_hw*[]) {
|
||||
&gpu_cc_gmu_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_branch2_aon_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_cxo_clk = {
|
||||
.halt_reg = 0x90e4,
|
||||
.halt_check = BRANCH_HALT,
|
||||
.clkr = {
|
||||
.enable_reg = 0x90e4,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_cxo_clk",
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_demet_clk = {
|
||||
.halt_reg = 0x9010,
|
||||
.halt_check = BRANCH_HALT_VOTED,
|
||||
.clkr = {
|
||||
.enable_reg = 0x9010,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_demet_clk",
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_dpm_clk = {
|
||||
.halt_reg = 0x910c,
|
||||
.halt_check = BRANCH_HALT,
|
||||
.clkr = {
|
||||
.enable_reg = 0x910c,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_dpm_clk",
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_freq_measure_clk = {
|
||||
.halt_reg = 0x900c,
|
||||
.halt_check = BRANCH_HALT,
|
||||
.clkr = {
|
||||
.enable_reg = 0x900c,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_freq_measure_clk",
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_gpu_smmu_vote_clk = {
|
||||
.halt_reg = 0x7000,
|
||||
.halt_check = BRANCH_HALT_VOTED,
|
||||
.clkr = {
|
||||
.enable_reg = 0x7000,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_gpu_smmu_vote_clk",
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_gx_accu_shift_clk = {
|
||||
.halt_reg = 0x9070,
|
||||
.halt_check = BRANCH_HALT_VOTED,
|
||||
.clkr = {
|
||||
.enable_reg = 0x9070,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_gx_accu_shift_clk",
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_gx_acd_ahb_ff_clk = {
|
||||
.halt_reg = 0x9068,
|
||||
.halt_check = BRANCH_HALT,
|
||||
.clkr = {
|
||||
.enable_reg = 0x9068,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_gx_acd_ahb_ff_clk",
|
||||
.parent_hws = (const struct clk_hw*[]) {
|
||||
&gpu_cc_ff_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_gx_ahb_ff_clk = {
|
||||
.halt_reg = 0x9064,
|
||||
.halt_check = BRANCH_HALT,
|
||||
.clkr = {
|
||||
.enable_reg = 0x9064,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_gx_ahb_ff_clk",
|
||||
.parent_hws = (const struct clk_hw*[]) {
|
||||
&gpu_cc_ff_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_gx_gmu_clk = {
|
||||
.halt_reg = 0x9060,
|
||||
.halt_check = BRANCH_HALT,
|
||||
.clkr = {
|
||||
.enable_reg = 0x9060,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_gx_gmu_clk",
|
||||
.parent_hws = (const struct clk_hw*[]) {
|
||||
&gpu_cc_gmu_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_gx_rcg_ahb_ff_clk = {
|
||||
.halt_reg = 0x906c,
|
||||
.halt_check = BRANCH_HALT_VOTED,
|
||||
.clkr = {
|
||||
.enable_reg = 0x906c,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_gx_rcg_ahb_ff_clk",
|
||||
.parent_hws = (const struct clk_hw*[]) {
|
||||
&gpu_cc_ff_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_hub_aon_clk = {
|
||||
.halt_reg = 0x93ec,
|
||||
.halt_check = BRANCH_HALT_VOTED,
|
||||
.clkr = {
|
||||
.enable_reg = 0x93ec,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_hub_aon_clk",
|
||||
.parent_hws = (const struct clk_hw*[]) {
|
||||
&gpu_cc_hub_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_branch2_aon_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_hub_cx_int_clk = {
|
||||
.halt_reg = 0x90e8,
|
||||
.halt_check = BRANCH_HALT_VOTED,
|
||||
.clkr = {
|
||||
.enable_reg = 0x90e8,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_hub_cx_int_clk",
|
||||
.parent_hws = (const struct clk_hw*[]) {
|
||||
&gpu_cc_hub_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_branch2_aon_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_memnoc_gfx_clk = {
|
||||
.halt_reg = 0x90f0,
|
||||
.halt_check = BRANCH_HALT_VOTED,
|
||||
.clkr = {
|
||||
.enable_reg = 0x90f0,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_memnoc_gfx_clk",
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_rscc_hub_aon_clk = {
|
||||
.halt_reg = 0x93e8,
|
||||
.halt_check = BRANCH_HALT,
|
||||
.clkr = {
|
||||
.enable_reg = 0x93e8,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_rscc_hub_aon_clk",
|
||||
.parent_hws = (const struct clk_hw*[]) {
|
||||
&gpu_cc_hub_clk_src.clkr.hw,
|
||||
},
|
||||
.num_parents = 1,
|
||||
.flags = CLK_SET_RATE_PARENT,
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct clk_branch gpu_cc_sleep_clk = {
|
||||
.halt_reg = 0x90cc,
|
||||
.halt_check = BRANCH_HALT,
|
||||
.clkr = {
|
||||
.enable_reg = 0x90cc,
|
||||
.enable_mask = BIT(0),
|
||||
.hw.init = &(const struct clk_init_data) {
|
||||
.name = "gpu_cc_sleep_clk",
|
||||
.ops = &clk_branch2_ops,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static struct gdsc gpu_cc_cx_gdsc = {
|
||||
.gdscr = 0x9080,
|
||||
.gds_hw_ctrl = 0x9094,
|
||||
.en_rest_wait_val = 0x2,
|
||||
.en_few_wait_val = 0x2,
|
||||
.clk_dis_wait_val = 0xf,
|
||||
.pd = {
|
||||
.name = "gpu_cc_cx_gdsc",
|
||||
},
|
||||
.pwrsts = PWRSTS_OFF_ON,
|
||||
.flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE,
|
||||
};
|
||||
|
||||
static struct clk_regmap *gpu_cc_glymur_clocks[] = {
|
||||
[GPU_CC_AHB_CLK] = &gpu_cc_ahb_clk.clkr,
|
||||
[GPU_CC_CX_ACCU_SHIFT_CLK] = &gpu_cc_cx_accu_shift_clk.clkr,
|
||||
[GPU_CC_CX_FF_CLK] = &gpu_cc_cx_ff_clk.clkr,
|
||||
[GPU_CC_CX_GMU_CLK] = &gpu_cc_cx_gmu_clk.clkr,
|
||||
[GPU_CC_CXO_CLK] = &gpu_cc_cxo_clk.clkr,
|
||||
[GPU_CC_DEMET_CLK] = &gpu_cc_demet_clk.clkr,
|
||||
[GPU_CC_DPM_CLK] = &gpu_cc_dpm_clk.clkr,
|
||||
[GPU_CC_FF_CLK_SRC] = &gpu_cc_ff_clk_src.clkr,
|
||||
[GPU_CC_FREQ_MEASURE_CLK] = &gpu_cc_freq_measure_clk.clkr,
|
||||
[GPU_CC_GMU_CLK_SRC] = &gpu_cc_gmu_clk_src.clkr,
|
||||
[GPU_CC_GPU_SMMU_VOTE_CLK] = &gpu_cc_gpu_smmu_vote_clk.clkr,
|
||||
[GPU_CC_GX_ACCU_SHIFT_CLK] = &gpu_cc_gx_accu_shift_clk.clkr,
|
||||
[GPU_CC_GX_ACD_AHB_FF_CLK] = &gpu_cc_gx_acd_ahb_ff_clk.clkr,
|
||||
[GPU_CC_GX_AHB_FF_CLK] = &gpu_cc_gx_ahb_ff_clk.clkr,
|
||||
[GPU_CC_GX_GMU_CLK] = &gpu_cc_gx_gmu_clk.clkr,
|
||||
[GPU_CC_GX_RCG_AHB_FF_CLK] = &gpu_cc_gx_rcg_ahb_ff_clk.clkr,
|
||||
[GPU_CC_HUB_AON_CLK] = &gpu_cc_hub_aon_clk.clkr,
|
||||
[GPU_CC_HUB_CLK_SRC] = &gpu_cc_hub_clk_src.clkr,
|
||||
[GPU_CC_HUB_CX_INT_CLK] = &gpu_cc_hub_cx_int_clk.clkr,
|
||||
[GPU_CC_HUB_DIV_CLK_SRC] = &gpu_cc_hub_div_clk_src.clkr,
|
||||
[GPU_CC_MEMNOC_GFX_CLK] = &gpu_cc_memnoc_gfx_clk.clkr,
|
||||
[GPU_CC_PLL0] = &gpu_cc_pll0.clkr,
|
||||
[GPU_CC_PLL0_OUT_EVEN] = &gpu_cc_pll0_out_even.clkr,
|
||||
[GPU_CC_RSCC_HUB_AON_CLK] = &gpu_cc_rscc_hub_aon_clk.clkr,
|
||||
[GPU_CC_SLEEP_CLK] = &gpu_cc_sleep_clk.clkr,
|
||||
};
|
||||
|
||||
static struct gdsc *gpu_cc_glymur_gdscs[] = {
|
||||
[GPU_CC_CX_GDSC] = &gpu_cc_cx_gdsc,
|
||||
};
|
||||
|
||||
static const struct qcom_reset_map gpu_cc_glymur_resets[] = {
|
||||
[GPU_CC_CB_BCR] = { 0x93a0 },
|
||||
[GPU_CC_CX_BCR] = { 0x907c },
|
||||
[GPU_CC_FAST_HUB_BCR] = { 0x93e4 },
|
||||
[GPU_CC_FF_BCR] = { 0x9470 },
|
||||
[GPU_CC_GMU_BCR] = { 0x9314 },
|
||||
[GPU_CC_GX_BCR] = { 0x905c },
|
||||
[GPU_CC_XO_BCR] = { 0x9000 },
|
||||
};
|
||||
|
||||
static struct clk_alpha_pll *gpu_cc_glymur_plls[] = {
|
||||
&gpu_cc_pll0,
|
||||
};
|
||||
|
||||
static const u32 gpu_cc_glymur_critical_cbcrs[] = {
|
||||
0x93a4, /* GPU_CC_CB_CLK */
|
||||
0x9008, /* GPU_CC_CXO_AON_CLK */
|
||||
0x9004, /* GPU_CC_RSCC_XO_AON_CLK */
|
||||
};
|
||||
|
||||
static const struct regmap_config gpu_cc_glymur_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = 0x95e8,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static const struct qcom_cc_driver_data gpu_cc_glymur_driver_data = {
|
||||
.alpha_plls = gpu_cc_glymur_plls,
|
||||
.num_alpha_plls = ARRAY_SIZE(gpu_cc_glymur_plls),
|
||||
.clk_cbcrs = gpu_cc_glymur_critical_cbcrs,
|
||||
.num_clk_cbcrs = ARRAY_SIZE(gpu_cc_glymur_critical_cbcrs),
|
||||
};
|
||||
|
||||
static const struct qcom_cc_desc gpu_cc_glymur_desc = {
|
||||
.config = &gpu_cc_glymur_regmap_config,
|
||||
.clks = gpu_cc_glymur_clocks,
|
||||
.num_clks = ARRAY_SIZE(gpu_cc_glymur_clocks),
|
||||
.resets = gpu_cc_glymur_resets,
|
||||
.num_resets = ARRAY_SIZE(gpu_cc_glymur_resets),
|
||||
.gdscs = gpu_cc_glymur_gdscs,
|
||||
.num_gdscs = ARRAY_SIZE(gpu_cc_glymur_gdscs),
|
||||
.use_rpm = true,
|
||||
.driver_data = &gpu_cc_glymur_driver_data,
|
||||
};
|
||||
|
||||
static const struct of_device_id gpu_cc_glymur_match_table[] = {
|
||||
{ .compatible = "qcom,glymur-gpucc" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gpu_cc_glymur_match_table);
|
||||
|
||||
static int gpu_cc_glymur_probe(struct platform_device *pdev)
|
||||
{
|
||||
return qcom_cc_probe(pdev, &gpu_cc_glymur_desc);
|
||||
}
|
||||
|
||||
static struct platform_driver gpu_cc_glymur_driver = {
|
||||
.probe = gpu_cc_glymur_probe,
|
||||
.driver = {
|
||||
.name = "gpucc-glymur",
|
||||
.of_match_table = gpu_cc_glymur_match_table,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(gpu_cc_glymur_driver);
|
||||
|
||||
MODULE_DESCRIPTION("QTI GPUCC Glymur Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue