We have a couple new features and changes in the core clk framework this time
around because we've finally gotten around to fixing some long standing issues. There's still work to do though, so this PR is largely laying down the foundation for all the driver changes to come in the next merge window. The first problem we're alleviating is how parents of clks are specified. With the new method, we should see lots of drivers migrate away from the current design of string comparisons on the entire clk tree to a more direct method where they can use clk_hw pointers or more localized names specified in DT or via clkdev. This should reduce our reliance on string comparisons for all the topology description logic that we've been using for years and hopefully speed some things up while avoiding problems we have with generating clk names. Beyond that we also got rid of the CLK_IS_BASIC flag because it wasn't really helping anyone and we introduced big-endian versions of the basic clk types so that we can get rid of clk_{readl,writel}(). Both of these are things that driver developers have tried to use over the years that I typically bat away during code reviews because they're not useful. It's great to see these two things go away so maintainers can save time not worrying about these things. On the driver side we got the usual collection of new SoC support and non-critical fixes and updates to existing code. The big topics that stand out are the new driver support for Mediatek MT8183 and MT8516 SoCs, Amlogic Meson8b and G12a SoCs, and the SiFive FU540 SoC. The other patches in the driver pile are mostly fixes for things that are being used for the first time or additions for clks that couldn't be tested before because there wasn't a consumer driver that exercised them. Details are below and also in the sub-maintainer tags. Core: - Remove clk_readl() and introduce BE versions of basic clk types - Rewrite how clk parents can be specified to allow DT/clkdev lookups - Removal of the CLK_IS_BASIC clk flag - Framework documentation updates and fixes New Drivers: - Support for STM32F769 - AT91 sam9x60 PMC support - SiFive FU540 PRCI and PLL support - Qualcomm QCS404 CDSP clk support - Qualcomm QCS404 Turing clk support - Mediatek MT8183 clock support - Mediatek MT8516 clock support - Milbeaut M10V clk controller support - Support for Cirrus Logic Lochnagar clks Updates: - Rework AT91 sckc DT bindings - Fix slow RC oscillator issue on sama5d3 - Mark UFS clk as critical on Hi-Silicon hi3660 SoCs - Various static analysis fixes/finds and const markings - Video Engine (ECLK) support on Aspeed SoCs - Xilinx ZynqMP Versal platform support - Convert Xilinx ZynqMP driver to be struct oriented - Fixes for Rockchip rk3328 and rk3288 SoCs - Sub-type for Rockchip SoCs where mux and divider aren't a single register - Remove SNVS clock from i.MX7UPL clock driver and bindings - Improve i.MX5 clock driver for i.MX50 support - Addition of ADC clock definition for Exynos 5410 SoC (Odroid XU) - Export a new clock for the MBUS controller on the A13 - Allwinner H6 fixes to support a finer clocking of the video and VPU engines - Add g12a support in the Amlogic axg audio clock controller - Add missing PCI USB clock on Rensas RZ/N1 - Add Z2 (Cortex-A53) clocks on Rensas R-Car E3 and RZ/G2E - A new helper DIV64_U64_ROUND_CLOSEST() in <linux/math64.h> - VPU and Video Decoder clocks on Amlogic Meson8b - Finally remove the wrong ABP Meson8b clock id - Add Video Decoder, PCIe PLL, and CPU Clocks on Amlogic G12A - Re-expose SAR_ADC_SEL and CTS_OSCIN on Amlogic G12A AO clock controller - Un-expose some Amlogic AXG-Audio input clocks IDs -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEE9L57QeeUxqYDyoaDrQKIl8bklSUFAlzUabQRHHNib3lkQGtl cm5lbC5vcmcACgkQrQKIl8bklSUMmBAAr0WrvWa3s1Ue+lPfmehcAfeI2NkBPC/E uhKD+vHBil/Aha33tFTPtVjsZiaMuUETNGPppEUUrHgu4K3UMJZl0iYql6XNVP77 OObIM5wqXoJ5Yv1e1G0p7X/Qztx7UxEtPwbXJ/9kNN2t6yzg4y8vD2cmXgV5KzHp yRUDFNbH9JEyWFbrPhPjD3Bk1PCwdmXNFQg/uYk79g3c84js9MCbWvIqVEuU3vps 3/9lsDkhbp/flrSOA7D1eloQ6aPXdkLsFzDkJ+6mCA3zxsW/i2N38ZKVDTYG/5rx USh3Z0Vyd0f9pKlQqwe1tyr2PBJrYWTtcPBSDcdr4BI1209xseFe4TaqHw1IRKcB uYX0gtNTTcgx+8Znvp9y+hE8DhbVNpZvblZuab+rbfb/Gte2wC5/zvzEAz1EqPap 43VYdi3JR9iWGsC/r4+5OdVFRgWmXFsn5ysQRLkRgE41fKRn7joGHhPS5xDTI0l/ 1rA/8Oh0GMAcSOQ0aSBtavmMCsyJJTjG7s6MpqiO5u/DHnb4MB1Jd0rEWNIjiVmJ cqS8II+EbaLWZgt9r3W7ePhkfpHlLw+c4mwI9lRF7Zo67Rz9lrlt1l9YxPnJHewN uTRn2ch5W90Jr289wymDNQZGGvCyr+nxKYlSd+kXjlH6poDn6bxYdKHgJexDYmZR NVylbizS6lg= =/uso -----END PGP SIGNATURE----- Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux Pull clk framework updates from Stephen Boyd: "We have a couple new features and changes in the core clk framework this time around because we've finally gotten around to fixing some long standing issues. There's still work to do though, so this pull request is largely laying down the foundation for all the driver changes to come in the next merge window. The first problem we're alleviating is how parents of clks are specified. With the new method, we should see lots of drivers migrate away from the current design of string comparisons on the entire clk tree to a more direct method where they can use clk_hw pointers or more localized names specified in DT or via clkdev. This should reduce our reliance on string comparisons for all the topology description logic that we've been using for years and hopefully speed some things up while avoiding problems we have with generating clk names. Beyond that we also got rid of the CLK_IS_BASIC flag because it wasn't really helping anyone and we introduced big-endian versions of the basic clk types so that we can get rid of clk_{readl,writel}(). Both of these are things that driver developers have tried to use over the years that I typically bat away during code reviews because they're not useful. It's great to see these two things go away so maintainers can save time not worrying about these things. On the driver side we got the usual collection of new SoC support and non-critical fixes and updates to existing code. The big topics that stand out are the new driver support for Mediatek MT8183 and MT8516 SoCs, Amlogic Meson8b and G12a SoCs, and the SiFive FU540 SoC. The other patches in the driver pile are mostly fixes for things that are being used for the first time or additions for clks that couldn't be tested before because there wasn't a consumer driver that exercised them. Details are below and also in the sub-maintainer tags. Core: - Remove clk_readl() and introduce BE versions of basic clk types - Rewrite how clk parents can be specified to allow DT/clkdev lookups - Removal of the CLK_IS_BASIC clk flag - Framework documentation updates and fixes New Drivers: - Support for STM32F769 - AT91 sam9x60 PMC support - SiFive FU540 PRCI and PLL support - Qualcomm QCS404 CDSP clk support - Qualcomm QCS404 Turing clk support - Mediatek MT8183 clock support - Mediatek MT8516 clock support - Milbeaut M10V clk controller support - Support for Cirrus Logic Lochnagar clks Updates: - Rework AT91 sckc DT bindings - Fix slow RC oscillator issue on sama5d3 - Mark UFS clk as critical on Hi-Silicon hi3660 SoCs - Various static analysis fixes/finds and const markings - Video Engine (ECLK) support on Aspeed SoCs - Xilinx ZynqMP Versal platform support - Convert Xilinx ZynqMP driver to be struct oriented - Fixes for Rockchip rk3328 and rk3288 SoCs - Sub-type for Rockchip SoCs where mux and divider aren't a single register - Remove SNVS clock from i.MX7UPL clock driver and bindings - Improve i.MX5 clock driver for i.MX50 support - Addition of ADC clock definition for Exynos 5410 SoC (Odroid XU) - Export a new clock for the MBUS controller on the A13 - Allwinner H6 fixes to support a finer clocking of the video and VPU engines - Add g12a support in the Amlogic axg audio clock controller - Add missing PCI USB clock on Rensas RZ/N1 - Add Z2 (Cortex-A53) clocks on Rensas R-Car E3 and RZ/G2E - A new helper DIV64_U64_ROUND_CLOSEST() in <linux/math64.h> - VPU and Video Decoder clocks on Amlogic Meson8b - Finally remove the wrong ABP Meson8b clock id - Add Video Decoder, PCIe PLL, and CPU Clocks on Amlogic G12A - Re-expose SAR_ADC_SEL and CTS_OSCIN on Amlogic G12A AO clock controller - Un-expose some Amlogic AXG-Audio input clocks IDs" * tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (172 commits) clk: Cache core in clk_fetch_parent_index() without names clk: imx: correct pfdv2 gate_bit/vld_bit operations clk: sifive: add a driver for the SiFive FU540 PRCI IP block clk: analogbits: add Wide-Range PLL library clk: imx: clk-pllv3: mark expected switch fall-throughs clk: imx8mq: Add dsi_ipg_div clk: imx: pllv4: add fractional-N pll support clk: sunxi-ng: Use the correct style for SPDX License Identifier clk: sprd: Use the correct style for SPDX License Identifier clk: renesas: Use the correct style for SPDX License Identifier clk: qcom: Use the correct style for SPDX License Identifier clk: davinci: Use the correct style for SPDX License Identifier clk: actions: Use the correct style for SPDX License Identifier clk: imx: keep uart clock on during system boot clk: imx: correct i.MX7D AV PLL num/denom offset dt-bindings: clk: add documentation for the SiFive PRCI driver clk: stm32mp1: Add ddrperfm clock clk: Remove CLK_IS_BASIC clk flag clock: milbeaut: Add Milbeaut M10V clock controller dt-bindings: clock: milbeaut: add Milbeaut clock description ...
This commit is contained in:
commit
ea5aee6d97
|
@ -14,6 +14,8 @@ Required Properties:
|
|||
- "mediatek,mt7629-apmixedsys"
|
||||
- "mediatek,mt8135-apmixedsys"
|
||||
- "mediatek,mt8173-apmixedsys"
|
||||
- "mediatek,mt8183-apmixedsys", "syscon"
|
||||
- "mediatek,mt8516-apmixedsys"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The apmixedsys controller uses the common clk binding from
|
||||
|
|
|
@ -9,6 +9,7 @@ Required Properties:
|
|||
- "mediatek,mt2701-audsys", "syscon"
|
||||
- "mediatek,mt7622-audsys", "syscon"
|
||||
- "mediatek,mt7623-audsys", "mediatek,mt2701-audsys", "syscon"
|
||||
- "mediatek,mt8183-audiosys", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The AUDSYS controller uses the common clk binding from
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
MediaTek CAMSYS controller
|
||||
============================
|
||||
|
||||
The MediaTek camsys controller provides various clocks to the system.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt8183-camsys", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The camsys controller uses the common clk binding from
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
The available clocks are defined in dt-bindings/clock/mt*-clk.h.
|
||||
|
||||
Example:
|
||||
|
||||
camsys: camsys@1a000000 {
|
||||
compatible = "mediatek,mt8183-camsys", "syscon";
|
||||
reg = <0 0x1a000000 0 0x1000>;
|
||||
#clock-cells = <1>;
|
||||
};
|
|
@ -11,6 +11,7 @@ Required Properties:
|
|||
- "mediatek,mt6797-imgsys", "syscon"
|
||||
- "mediatek,mt7623-imgsys", "mediatek,mt2701-imgsys", "syscon"
|
||||
- "mediatek,mt8173-imgsys", "syscon"
|
||||
- "mediatek,mt8183-imgsys", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The imgsys controller uses the common clk binding from
|
||||
|
|
|
@ -15,6 +15,8 @@ Required Properties:
|
|||
- "mediatek,mt7629-infracfg", "syscon"
|
||||
- "mediatek,mt8135-infracfg", "syscon"
|
||||
- "mediatek,mt8173-infracfg", "syscon"
|
||||
- "mediatek,mt8183-infracfg", "syscon"
|
||||
- "mediatek,mt8516-infracfg", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
- #reset-cells: Must be 1
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
Mediatek IPU controller
|
||||
============================
|
||||
|
||||
The Mediatek ipu controller provides various clocks to the system.
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt8183-ipu_conn", "syscon"
|
||||
- "mediatek,mt8183-ipu_adl", "syscon"
|
||||
- "mediatek,mt8183-ipu_core0", "syscon"
|
||||
- "mediatek,mt8183-ipu_core1", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The ipu controller uses the common clk binding from
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
The available clocks are defined in dt-bindings/clock/mt*-clk.h.
|
||||
|
||||
Example:
|
||||
|
||||
ipu_conn: syscon@19000000 {
|
||||
compatible = "mediatek,mt8183-ipu_conn", "syscon";
|
||||
reg = <0 0x19000000 0 0x1000>;
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
|
||||
ipu_adl: syscon@19010000 {
|
||||
compatible = "mediatek,mt8183-ipu_adl", "syscon";
|
||||
reg = <0 0x19010000 0 0x1000>;
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
|
||||
ipu_core0: syscon@19180000 {
|
||||
compatible = "mediatek,mt8183-ipu_core0", "syscon";
|
||||
reg = <0 0x19180000 0 0x1000>;
|
||||
#clock-cells = <1>;
|
||||
};
|
||||
|
||||
ipu_core1: syscon@19280000 {
|
||||
compatible = "mediatek,mt8183-ipu_core1", "syscon";
|
||||
reg = <0 0x19280000 0 0x1000>;
|
||||
#clock-cells = <1>;
|
||||
};
|
|
@ -7,6 +7,7 @@ Required Properties:
|
|||
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt2712-mcucfg", "syscon"
|
||||
- "mediatek,mt8183-mcucfg", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The mcucfg controller uses the common clk binding from
|
||||
|
|
|
@ -7,6 +7,7 @@ Required Properties:
|
|||
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt2712-mfgcfg", "syscon"
|
||||
- "mediatek,mt8183-mfgcfg", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The mfgcfg controller uses the common clk binding from
|
||||
|
|
|
@ -11,6 +11,7 @@ Required Properties:
|
|||
- "mediatek,mt6797-mmsys", "syscon"
|
||||
- "mediatek,mt7623-mmsys", "mediatek,mt2701-mmsys", "syscon"
|
||||
- "mediatek,mt8173-mmsys", "syscon"
|
||||
- "mediatek,mt8183-mmsys", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The mmsys controller uses the common clk binding from
|
||||
|
|
|
@ -14,6 +14,8 @@ Required Properties:
|
|||
- "mediatek,mt7629-topckgen"
|
||||
- "mediatek,mt8135-topckgen"
|
||||
- "mediatek,mt8173-topckgen"
|
||||
- "mediatek,mt8183-topckgen", "syscon"
|
||||
- "mediatek,mt8516-topckgen"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The topckgen controller uses the common clk binding from
|
||||
|
|
|
@ -11,6 +11,7 @@ Required Properties:
|
|||
- "mediatek,mt6797-vdecsys", "syscon"
|
||||
- "mediatek,mt7623-vdecsys", "mediatek,mt2701-vdecsys", "syscon"
|
||||
- "mediatek,mt8173-vdecsys", "syscon"
|
||||
- "mediatek,mt8183-vdecsys", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The vdecsys controller uses the common clk binding from
|
||||
|
|
|
@ -9,6 +9,7 @@ Required Properties:
|
|||
- "mediatek,mt2712-vencsys", "syscon"
|
||||
- "mediatek,mt6797-vencsys", "syscon"
|
||||
- "mediatek,mt8173-vencsys", "syscon"
|
||||
- "mediatek,mt8183-vencsys", "syscon"
|
||||
- #clock-cells: Must be 1
|
||||
|
||||
The vencsys controller uses the common clk binding from
|
||||
|
|
|
@ -6,7 +6,8 @@ devices.
|
|||
|
||||
Required Properties:
|
||||
|
||||
- compatible : should be "amlogic,axg-audio-clkc" for the A113X and A113D
|
||||
- compatible : should be "amlogic,axg-audio-clkc" for the A113X and A113D,
|
||||
"amlogic,g12a-audio-clkc" for G12A.
|
||||
- reg : physical base address of the clock controller and length of
|
||||
memory mapped region.
|
||||
- clocks : a list of phandle + clock-specifier pairs for the clocks listed
|
||||
|
|
|
@ -8,35 +8,30 @@ Slow Clock controller:
|
|||
|
||||
Required properties:
|
||||
- compatible : shall be one of the following:
|
||||
"atmel,at91sam9x5-sckc" or
|
||||
"atmel,at91sam9x5-sckc",
|
||||
"atmel,sama5d3-sckc" or
|
||||
"atmel,sama5d4-sckc":
|
||||
at91 SCKC (Slow Clock Controller)
|
||||
This node contains the slow clock definitions.
|
||||
|
||||
"atmel,at91sam9x5-clk-slow-osc":
|
||||
at91 slow oscillator
|
||||
|
||||
"atmel,at91sam9x5-clk-slow-rc-osc":
|
||||
at91 internal slow RC oscillator
|
||||
- reg : defines the IO memory reserved for the SCKC.
|
||||
- #size-cells : shall be 0 (reg is used to encode clk id).
|
||||
- #address-cells : shall be 1 (reg is used to encode clk id).
|
||||
- #clock-cells : shall be 0.
|
||||
- clocks : shall be the input parent clock phandle for the clock.
|
||||
|
||||
Optional properties:
|
||||
- atmel,osc-bypass : boolean property. Set this when a clock signal is directly
|
||||
provided on XIN.
|
||||
|
||||
For example:
|
||||
sckc: sckc@fffffe50 {
|
||||
compatible = "atmel,sama5d3-pmc";
|
||||
reg = <0xfffffe50 0x4>
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
|
||||
/* put at91 slow clocks here */
|
||||
sckc@fffffe50 {
|
||||
compatible = "atmel,at91sam9x5-sckc";
|
||||
reg = <0xfffffe50 0x4>;
|
||||
clocks = <&slow_xtal>;
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
||||
Power Management Controller (PMC):
|
||||
|
||||
Required properties:
|
||||
- compatible : shall be "atmel,<chip>-pmc", "syscon":
|
||||
- compatible : shall be "atmel,<chip>-pmc", "syscon" or
|
||||
"microchip,sam9x60-pmc"
|
||||
<chip> can be: at91rm9200, at91sam9260, at91sam9261,
|
||||
at91sam9263, at91sam9g45, at91sam9n12, at91sam9rl, at91sam9g15,
|
||||
at91sam9g25, at91sam9g35, at91sam9x25, at91sam9x35, at91sam9x5,
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
Cirrus Logic Lochnagar Audio Development Board
|
||||
|
||||
Lochnagar is an evaluation and development board for Cirrus Logic
|
||||
Smart CODEC and Amp devices. It allows the connection of most Cirrus
|
||||
Logic devices on mini-cards, as well as allowing connection of
|
||||
various application processor systems to provide a full evaluation
|
||||
platform. Audio system topology, clocking and power can all be
|
||||
controlled through the Lochnagar, allowing the device under test
|
||||
to be used in a variety of possible use cases.
|
||||
|
||||
This binding document describes the binding for the clock portion of
|
||||
the driver.
|
||||
|
||||
Also see these documents for generic binding information:
|
||||
[1] Clock : ../clock/clock-bindings.txt
|
||||
|
||||
And these for relevant defines:
|
||||
[2] include/dt-bindings/clock/lochnagar.h
|
||||
|
||||
This binding must be part of the Lochnagar MFD binding:
|
||||
[3] ../mfd/cirrus,lochnagar.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : One of the following strings:
|
||||
"cirrus,lochnagar1-clk"
|
||||
"cirrus,lochnagar2-clk"
|
||||
|
||||
- #clock-cells : Must be 1. The first cell indicates the clock
|
||||
number, see [2] for available clocks and [1].
|
||||
|
||||
Optional properties:
|
||||
|
||||
- clocks : Must contain an entry for each clock in clock-names.
|
||||
- clock-names : May contain entries for each of the following
|
||||
clocks:
|
||||
- ln-cdc-clkout : Output clock from CODEC card.
|
||||
- ln-dsp-clkout : Output clock from DSP card.
|
||||
- ln-gf-mclk1,ln-gf-mclk2,ln-gf-mclk3,ln-gf-mclk4 : Optional
|
||||
input audio clocks from host system.
|
||||
- ln-psia1-mclk, ln-psia2-mclk : Optional input audio clocks from
|
||||
external connector.
|
||||
- ln-spdif-clkout : Optional input audio clock from SPDIF.
|
||||
- ln-adat-mclk : Optional input audio clock from ADAT.
|
||||
- ln-pmic-32k : On board fixed clock.
|
||||
- ln-clk-12m : On board fixed clock.
|
||||
- ln-clk-11m : On board fixed clock.
|
||||
- ln-clk-24m : On board fixed clock.
|
||||
- ln-clk-22m : On board fixed clock.
|
||||
- ln-clk-8m : On board fixed clock.
|
||||
- ln-usb-clk-24m : On board fixed clock.
|
||||
- ln-usb-clk-12m : On board fixed clock.
|
||||
|
||||
- assigned-clocks : A list of Lochnagar clocks to be reparented, see
|
||||
[2] for available clocks.
|
||||
- assigned-clock-parents : Parents to be assigned to the clocks
|
||||
listed in "assigned-clocks".
|
||||
|
||||
Optional nodes:
|
||||
|
||||
- fixed-clock nodes may be registered for the following on board clocks:
|
||||
- ln-pmic-32k : 32768 Hz
|
||||
- ln-clk-12m : 12288000 Hz
|
||||
- ln-clk-11m : 11298600 Hz
|
||||
- ln-clk-24m : 24576000 Hz
|
||||
- ln-clk-22m : 22579200 Hz
|
||||
- ln-clk-8m : 8192000 Hz
|
||||
- ln-usb-clk-24m : 24576000 Hz
|
||||
- ln-usb-clk-12m : 12288000 Hz
|
||||
|
||||
Example:
|
||||
|
||||
lochnagar {
|
||||
lochnagar-clk {
|
||||
compatible = "cirrus,lochnagar2-clk";
|
||||
|
||||
#clock-cells = <1>;
|
||||
|
||||
clocks = <&clk-audio>, <&clk_pmic>;
|
||||
clock-names = "ln-gf-mclk2", "ln-pmic-32k";
|
||||
|
||||
assigned-clocks = <&lochnagar-clk LOCHNAGAR_CDC_MCLK1>,
|
||||
<&lochnagar-clk LOCHNAGAR_CDC_MCLK2>;
|
||||
assigned-clock-parents = <&clk-audio>,
|
||||
<&clk-pmic>;
|
||||
};
|
||||
|
||||
clk-pmic: clk-pmic {
|
||||
compatible = "fixed-clock";
|
||||
clock-cells = <0>;
|
||||
clock-frequency = <32768>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,73 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/bindings/clock/milbeaut-clock.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Milbeaut SoCs Clock Controller Binding
|
||||
|
||||
maintainers:
|
||||
- Taichi Sugaya <sugaya.taichi@socionext.com>
|
||||
|
||||
description: |
|
||||
Milbeaut SoCs Clock controller is an integrated clock controller, which
|
||||
generates and supplies to all modules.
|
||||
|
||||
This binding uses common clock bindings
|
||||
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- socionext,milbeaut-m10v-ccu
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: external clock
|
||||
|
||||
'#clock-cells':
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- '#clock-cells'
|
||||
|
||||
examples:
|
||||
# Clock controller node:
|
||||
- |
|
||||
m10v-clk-ctrl@1d021000 {
|
||||
compatible = "socionext,milbeaut-m10v-clk-ccu";
|
||||
reg = <0x1d021000 0x4000>;
|
||||
#clock-cells = <1>;
|
||||
clocks = <&clki40mhz>;
|
||||
};
|
||||
|
||||
# Required an external clock for Clock controller node:
|
||||
- |
|
||||
clocks {
|
||||
clki40mhz: clki40mhz {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <40000000>;
|
||||
};
|
||||
/* other clocks */
|
||||
};
|
||||
|
||||
# The clock consumer shall specify the desired clock-output of the clock
|
||||
# controller as below by specifying output-id in its "clk" phandle cell.
|
||||
# 2: uart
|
||||
# 4: 32-bit timer
|
||||
# 7: UHS-I/II
|
||||
- |
|
||||
serial@1e700010 {
|
||||
compatible = "socionext,milbeaut-usio-uart";
|
||||
reg = <0x1e700010 0x10>;
|
||||
interrupts = <0 141 0x4>, <0 149 0x4>;
|
||||
interrupt-names = "rx", "tx";
|
||||
clocks = <&clk 2>;
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,19 @@
|
|||
Qualcomm Turing Clock & Reset Controller Binding
|
||||
------------------------------------------------
|
||||
|
||||
Required properties :
|
||||
- compatible: shall contain "qcom,qcs404-turingcc".
|
||||
- reg: shall contain base register location and length.
|
||||
- clocks: ahb clock for the TuringCC
|
||||
- #clock-cells: from common clock binding, shall contain 1.
|
||||
- #reset-cells: from common reset binding, shall contain 1.
|
||||
|
||||
Example:
|
||||
turingcc: clock-controller@800000 {
|
||||
compatible = "qcom,qcs404-turingcc";
|
||||
reg = <0x00800000 0x30000>;
|
||||
clocks = <&gcc GCC_CDSP_CFG_AHB_CLK>;
|
||||
|
||||
#clock-cells = <1>;
|
||||
#reset-cells = <1>;
|
||||
};
|
|
@ -39,6 +39,7 @@ Required properties:
|
|||
* "fsl,b4860-clockgen"
|
||||
* "fsl,ls1012a-clockgen"
|
||||
* "fsl,ls1021a-clockgen"
|
||||
* "fsl,ls1028a-clockgen"
|
||||
* "fsl,ls1043a-clockgen"
|
||||
* "fsl,ls1046a-clockgen"
|
||||
* "fsl,ls1088a-clockgen"
|
||||
|
@ -83,8 +84,8 @@ second cell is the clock index for the specified type.
|
|||
1 cmux index (n in CLKCnCSR)
|
||||
2 hwaccel index (n in CLKCGnHWACSR)
|
||||
3 fman 0 for fm1, 1 for fm2
|
||||
4 platform pll 0=pll, 1=pll/2, 2=pll/3, 3=pll/4
|
||||
4=pll/5, 5=pll/6, 6=pll/7, 7=pll/8
|
||||
4 platform pll n=pll/(n+1). For example, when n=1,
|
||||
that means output_freq=PLL_freq/2.
|
||||
5 coreclk must be 0
|
||||
|
||||
3. Example
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
SiFive FU540 PRCI bindings
|
||||
|
||||
On the FU540 family of SoCs, most system-wide clock and reset integration
|
||||
is via the PRCI IP block.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "sifive,<chip>-prci". Only one value is
|
||||
supported: "sifive,fu540-c000-prci"
|
||||
- reg: Should describe the PRCI's register target physical address region
|
||||
- clocks: Should point to the hfclk device tree node and the rtcclk
|
||||
device tree node. The RTC clock here is not a time-of-day clock,
|
||||
but is instead a high-stability clock source for system timers
|
||||
and cycle counters.
|
||||
- #clock-cells: Should be <1>
|
||||
|
||||
The clock consumer should specify the desired clock via the clock ID
|
||||
macros defined in include/dt-bindings/clock/sifive-fu540-prci.h.
|
||||
These macros begin with PRCI_CLK_.
|
||||
|
||||
The hfclk and rtcclk nodes are required, and represent physical
|
||||
crystals or resonators located on the PCB. These nodes should be present
|
||||
underneath /, rather than /soc.
|
||||
|
||||
Examples:
|
||||
|
||||
/* under /, in PCB-specific DT data */
|
||||
hfclk: hfclk {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <33333333>;
|
||||
clock-output-names = "hfclk";
|
||||
};
|
||||
rtcclk: rtcclk {
|
||||
#clock-cells = <0>;
|
||||
compatible = "fixed-clock";
|
||||
clock-frequency = <1000000>;
|
||||
clock-output-names = "rtcclk";
|
||||
};
|
||||
|
||||
/* under /soc, in SoC-specific DT data */
|
||||
prci: clock-controller@10000000 {
|
||||
compatible = "sifive,fu540-c000-prci";
|
||||
reg = <0x0 0x10000000 0x0 0x1000>;
|
||||
clocks = <&hfclk>, <&rtcclk>;
|
||||
#clock-cells = <1>;
|
||||
};
|
|
@ -11,6 +11,8 @@ Required properties:
|
|||
"st,stm32f42xx-rcc"
|
||||
"st,stm32f469-rcc"
|
||||
"st,stm32f746-rcc"
|
||||
"st,stm32f769-rcc"
|
||||
|
||||
- reg: should be register base and length as documented in the
|
||||
datasheet
|
||||
- #reset-cells: 1, see below
|
||||
|
@ -102,6 +104,10 @@ The secondary index is bound with the following magic numbers:
|
|||
28 CLK_I2C3
|
||||
29 CLK_I2C4
|
||||
30 CLK_LPTIMER (LPTimer1 clock)
|
||||
31 CLK_PLL_SRC
|
||||
32 CLK_DFSDM1
|
||||
33 CLK_ADFSDM1
|
||||
34 CLK_F769_DSI
|
||||
)
|
||||
|
||||
Example:
|
||||
|
|
|
@ -979,6 +979,12 @@ F: drivers/iio/adc/ltc2497*
|
|||
X: drivers/iio/*/adjd*
|
||||
F: drivers/staging/iio/*/ad*
|
||||
|
||||
ANALOGBITS PLL LIBRARIES
|
||||
M: Paul Walmsley <paul.walmsley@sifive.com>
|
||||
S: Supported
|
||||
F: drivers/clk/analogbits/*
|
||||
F: include/linux/clk/analogbits*
|
||||
|
||||
ANDES ARCHITECTURE
|
||||
M: Greentime Hu <green.hu@gmail.com>
|
||||
M: Vincent Chen <deanbo422@gmail.com>
|
||||
|
|
|
@ -119,6 +119,9 @@ void __init ti_clk_init_features(void)
|
|||
if (cpu_is_omap343x())
|
||||
features.flags |= TI_CLK_DPLL_HAS_FREQSEL;
|
||||
|
||||
if (omap_type() == OMAP2_DEVICE_TYPE_GP)
|
||||
features.flags |= TI_CLK_DEVICE_TYPE_GP;
|
||||
|
||||
/* Idlest value for interface clocks.
|
||||
* 24xx uses 0 to indicate not ready, and 1 to indicate ready.
|
||||
* 34xx reverses this, just to keep us on our toes
|
||||
|
|
|
@ -648,10 +648,10 @@ static struct clockdomain *_get_clkdm(struct omap_hwmod *oh)
|
|||
if (oh->clkdm) {
|
||||
return oh->clkdm;
|
||||
} else if (oh->_clk) {
|
||||
if (__clk_get_flags(oh->_clk) & CLK_IS_BASIC)
|
||||
if (!omap2_clk_is_hw_omap(__clk_get_hw(oh->_clk)))
|
||||
return NULL;
|
||||
clk = to_clk_hw_omap(__clk_get_hw(oh->_clk));
|
||||
return clk->clkdm;
|
||||
return clk->clkdm;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -160,7 +160,7 @@ static struct clk __init *alchemy_clk_setup_cpu(const char *parent_name,
|
|||
id.name = ALCHEMY_CPU_CLK;
|
||||
id.parent_names = &parent_name;
|
||||
id.num_parents = 1;
|
||||
id.flags = CLK_IS_BASIC;
|
||||
id.flags = 0;
|
||||
id.ops = &alchemy_clkops_cpu;
|
||||
h->init = &id;
|
||||
|
||||
|
|
|
@ -239,6 +239,7 @@ static inline struct clk *mpc512x_clk_divider(
|
|||
const char *name, const char *parent_name, u8 clkflags,
|
||||
u32 __iomem *reg, u8 pos, u8 len, int divflags)
|
||||
{
|
||||
divflags |= CLK_DIVIDER_BIG_ENDIAN;
|
||||
return clk_register_divider(NULL, name, parent_name, clkflags,
|
||||
reg, pos, len, divflags, &clklock);
|
||||
}
|
||||
|
@ -250,7 +251,7 @@ static inline struct clk *mpc512x_clk_divtable(
|
|||
{
|
||||
u8 divflags;
|
||||
|
||||
divflags = 0;
|
||||
divflags = CLK_DIVIDER_BIG_ENDIAN;
|
||||
return clk_register_divider_table(NULL, name, parent_name, 0,
|
||||
reg, pos, len, divflags,
|
||||
divtab, &clklock);
|
||||
|
@ -261,10 +262,12 @@ static inline struct clk *mpc512x_clk_gated(
|
|||
u32 __iomem *reg, u8 pos)
|
||||
{
|
||||
int clkflags;
|
||||
u8 gateflags;
|
||||
|
||||
clkflags = CLK_SET_RATE_PARENT;
|
||||
gateflags = CLK_GATE_BIG_ENDIAN;
|
||||
return clk_register_gate(NULL, name, parent_name, clkflags,
|
||||
reg, pos, 0, &clklock);
|
||||
reg, pos, gateflags, &clklock);
|
||||
}
|
||||
|
||||
static inline struct clk *mpc512x_clk_muxed(const char *name,
|
||||
|
@ -275,7 +278,7 @@ static inline struct clk *mpc512x_clk_muxed(const char *name,
|
|||
u8 muxflags;
|
||||
|
||||
clkflags = CLK_SET_RATE_PARENT;
|
||||
muxflags = 0;
|
||||
muxflags = CLK_MUX_BIG_ENDIAN;
|
||||
return clk_register_mux(NULL, name,
|
||||
parent_names, parent_count, clkflags,
|
||||
reg, pos, len, muxflags, &clklock);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
config CLKDEV_LOOKUP
|
||||
bool
|
||||
|
@ -219,6 +220,13 @@ config COMMON_CLK_XGENE
|
|||
---help---
|
||||
Sypport for the APM X-Gene SoC reference, PLL, and device clocks.
|
||||
|
||||
config COMMON_CLK_LOCHNAGAR
|
||||
tristate "Cirrus Logic Lochnagar clock driver"
|
||||
depends on MFD_LOCHNAGAR
|
||||
help
|
||||
This driver supports the clocking features of the Cirrus Logic
|
||||
Lochnagar audio development board.
|
||||
|
||||
config COMMON_CLK_NXP
|
||||
def_bool COMMON_CLK && (ARCH_LPC18XX || ARCH_LPC32XX)
|
||||
select REGMAP_MMIO if ARCH_LPC32XX
|
||||
|
@ -297,6 +305,7 @@ config COMMON_CLK_FIXED_MMIO
|
|||
Support for Memory Mapped IO Fixed clocks
|
||||
|
||||
source "drivers/clk/actions/Kconfig"
|
||||
source "drivers/clk/analogbits/Kconfig"
|
||||
source "drivers/clk/bcm/Kconfig"
|
||||
source "drivers/clk/hisilicon/Kconfig"
|
||||
source "drivers/clk/imgtec/Kconfig"
|
||||
|
@ -309,7 +318,9 @@ source "drivers/clk/mvebu/Kconfig"
|
|||
source "drivers/clk/qcom/Kconfig"
|
||||
source "drivers/clk/renesas/Kconfig"
|
||||
source "drivers/clk/samsung/Kconfig"
|
||||
source "drivers/clk/sifive/Kconfig"
|
||||
source "drivers/clk/sprd/Kconfig"
|
||||
source "drivers/clk/sunxi/Kconfig"
|
||||
source "drivers/clk/sunxi-ng/Kconfig"
|
||||
source "drivers/clk/tegra/Kconfig"
|
||||
source "drivers/clk/ti/Kconfig"
|
||||
|
|
|
@ -32,8 +32,10 @@ obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o
|
|||
obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o
|
||||
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
|
||||
obj-$(CONFIG_CLK_HSDK) += clk-hsdk-pll.o
|
||||
obj-$(CONFIG_COMMON_CLK_LOCHNAGAR) += clk-lochnagar.o
|
||||
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
|
||||
obj-$(CONFIG_COMMON_CLK_MAX9485) += clk-max9485.o
|
||||
obj-$(CONFIG_ARCH_MILBEAUT_M10V) += clk-milbeaut.o
|
||||
obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
|
||||
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
|
||||
obj-$(CONFIG_ARCH_NPCM7XX) += clk-npcm7xx.o
|
||||
|
@ -64,6 +66,7 @@ obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o
|
|||
|
||||
# please keep this section sorted lexicographically by directory path name
|
||||
obj-y += actions/
|
||||
obj-y += analogbits/
|
||||
obj-$(CONFIG_COMMON_CLK_AT91) += at91/
|
||||
obj-$(CONFIG_ARCH_ARTPEC) += axis/
|
||||
obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/
|
||||
|
@ -93,6 +96,7 @@ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
|
|||
obj-y += renesas/
|
||||
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
|
||||
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
|
||||
obj-$(CONFIG_CLK_SIFIVE) += sifive/
|
||||
obj-$(CONFIG_ARCH_SIRF) += sirf/
|
||||
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
|
||||
obj-$(CONFIG_PLAT_SPEAR) += spear/
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
//
|
||||
// OWL common clock driver
|
||||
//
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
//
|
||||
// OWL composite clock driver
|
||||
//
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
//
|
||||
// OWL divider clock driver
|
||||
//
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
//
|
||||
// OWL factor clock driver
|
||||
//
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
//
|
||||
// OWL fixed factor clock driver
|
||||
//
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
//
|
||||
// OWL gate clock driver
|
||||
//
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
//
|
||||
// OWL mux clock driver
|
||||
//
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
//
|
||||
// OWL pll clock driver
|
||||
//
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
//
|
||||
// Actions Semi Owl SoCs Reset Management Unit driver
|
||||
//
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
config CLK_ANALOGBITS_WRPLL_CLN28HPC
|
||||
bool
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_CLK_ANALOGBITS_WRPLL_CLN28HPC) += wrpll-cln28hpc.o
|
|
@ -0,0 +1,364 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018-2019 SiFive, Inc.
|
||||
* Wesley Terpstra
|
||||
* Paul Walmsley
|
||||
*
|
||||
* This library supports configuration parsing and reprogramming of
|
||||
* the CLN28HPC variant of the Analog Bits Wide Range PLL. The
|
||||
* intention is for this library to be reusable for any device that
|
||||
* integrates this PLL; thus the register structure and programming
|
||||
* details are expected to be provided by a separate IP block driver.
|
||||
*
|
||||
* The bulk of this code is primarily useful for clock configurations
|
||||
* that must operate at arbitrary rates, as opposed to clock configurations
|
||||
* that are restricted by software or manufacturer guidance to a small,
|
||||
* pre-determined set of performance points.
|
||||
*
|
||||
* References:
|
||||
* - Analog Bits "Wide Range PLL Datasheet", version 2015.10.01
|
||||
* - SiFive FU540-C000 Manual v1p0, Chapter 7 "Clocking and Reset"
|
||||
* https://static.dev.sifive.com/FU540-C000-v1.0.pdf
|
||||
*/
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/clk/analogbits-wrpll-cln28hpc.h>
|
||||
|
||||
/* MIN_INPUT_FREQ: minimum input clock frequency, in Hz (Fref_min) */
|
||||
#define MIN_INPUT_FREQ 7000000
|
||||
|
||||
/* MAX_INPUT_FREQ: maximum input clock frequency, in Hz (Fref_max) */
|
||||
#define MAX_INPUT_FREQ 600000000
|
||||
|
||||
/* MIN_POST_DIVIDE_REF_FREQ: minimum post-divider reference frequency, in Hz */
|
||||
#define MIN_POST_DIVR_FREQ 7000000
|
||||
|
||||
/* MAX_POST_DIVIDE_REF_FREQ: maximum post-divider reference frequency, in Hz */
|
||||
#define MAX_POST_DIVR_FREQ 200000000
|
||||
|
||||
/* MIN_VCO_FREQ: minimum VCO frequency, in Hz (Fvco_min) */
|
||||
#define MIN_VCO_FREQ 2400000000UL
|
||||
|
||||
/* MAX_VCO_FREQ: maximum VCO frequency, in Hz (Fvco_max) */
|
||||
#define MAX_VCO_FREQ 4800000000ULL
|
||||
|
||||
/* MAX_DIVQ_DIVISOR: maximum output divisor. Selected by DIVQ = 6 */
|
||||
#define MAX_DIVQ_DIVISOR 64
|
||||
|
||||
/* MAX_DIVR_DIVISOR: maximum reference divisor. Selected by DIVR = 63 */
|
||||
#define MAX_DIVR_DIVISOR 64
|
||||
|
||||
/* MAX_LOCK_US: maximum PLL lock time, in microseconds (tLOCK_max) */
|
||||
#define MAX_LOCK_US 70
|
||||
|
||||
/*
|
||||
* ROUND_SHIFT: number of bits to shift to avoid precision loss in the rounding
|
||||
* algorithm
|
||||
*/
|
||||
#define ROUND_SHIFT 20
|
||||
|
||||
/*
|
||||
* Private functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* __wrpll_calc_filter_range() - determine PLL loop filter bandwidth
|
||||
* @post_divr_freq: input clock rate after the R divider
|
||||
*
|
||||
* Select the value to be presented to the PLL RANGE input signals, based
|
||||
* on the input clock frequency after the post-R-divider @post_divr_freq.
|
||||
* This code follows the recommendations in the PLL datasheet for filter
|
||||
* range selection.
|
||||
*
|
||||
* Return: The RANGE value to be presented to the PLL configuration inputs,
|
||||
* or a negative return code upon error.
|
||||
*/
|
||||
static int __wrpll_calc_filter_range(unsigned long post_divr_freq)
|
||||
{
|
||||
if (post_divr_freq < MIN_POST_DIVR_FREQ ||
|
||||
post_divr_freq > MAX_POST_DIVR_FREQ) {
|
||||
WARN(1, "%s: post-divider reference freq out of range: %lu",
|
||||
__func__, post_divr_freq);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
switch (post_divr_freq) {
|
||||
case 0 ... 10999999:
|
||||
return 1;
|
||||
case 11000000 ... 17999999:
|
||||
return 2;
|
||||
case 18000000 ... 29999999:
|
||||
return 3;
|
||||
case 30000000 ... 49999999:
|
||||
return 4;
|
||||
case 50000000 ... 79999999:
|
||||
return 5;
|
||||
case 80000000 ... 129999999:
|
||||
return 6;
|
||||
}
|
||||
|
||||
return 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* __wrpll_calc_fbdiv() - return feedback fixed divide value
|
||||
* @c: ptr to a struct wrpll_cfg record to read from
|
||||
*
|
||||
* The internal feedback path includes a fixed by-two divider; the
|
||||
* external feedback path does not. Return the appropriate divider
|
||||
* value (2 or 1) depending on whether internal or external feedback
|
||||
* is enabled. This code doesn't test for invalid configurations
|
||||
* (e.g. both or neither of WRPLL_FLAGS_*_FEEDBACK are set); it relies
|
||||
* on the caller to do so.
|
||||
*
|
||||
* Context: Any context. Caller must protect the memory pointed to by
|
||||
* @c from simultaneous modification.
|
||||
*
|
||||
* Return: 2 if internal feedback is enabled or 1 if external feedback
|
||||
* is enabled.
|
||||
*/
|
||||
static u8 __wrpll_calc_fbdiv(const struct wrpll_cfg *c)
|
||||
{
|
||||
return (c->flags & WRPLL_FLAGS_INT_FEEDBACK_MASK) ? 2 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* __wrpll_calc_divq() - determine DIVQ based on target PLL output clock rate
|
||||
* @target_rate: target PLL output clock rate
|
||||
* @vco_rate: pointer to a u64 to store the computed VCO rate into
|
||||
*
|
||||
* Determine a reasonable value for the PLL Q post-divider, based on the
|
||||
* target output rate @target_rate for the PLL. Along with returning the
|
||||
* computed Q divider value as the return value, this function stores the
|
||||
* desired target VCO rate into the variable pointed to by @vco_rate.
|
||||
*
|
||||
* Context: Any context. Caller must protect the memory pointed to by
|
||||
* @vco_rate from simultaneous access or modification.
|
||||
*
|
||||
* Return: a positive integer DIVQ value to be programmed into the hardware
|
||||
* upon success, or 0 upon error (since 0 is an invalid DIVQ value)
|
||||
*/
|
||||
static u8 __wrpll_calc_divq(u32 target_rate, u64 *vco_rate)
|
||||
{
|
||||
u64 s;
|
||||
u8 divq = 0;
|
||||
|
||||
if (!vco_rate) {
|
||||
WARN_ON(1);
|
||||
goto wcd_out;
|
||||
}
|
||||
|
||||
s = div_u64(MAX_VCO_FREQ, target_rate);
|
||||
if (s <= 1) {
|
||||
divq = 1;
|
||||
*vco_rate = MAX_VCO_FREQ;
|
||||
} else if (s > MAX_DIVQ_DIVISOR) {
|
||||
divq = ilog2(MAX_DIVQ_DIVISOR);
|
||||
*vco_rate = MIN_VCO_FREQ;
|
||||
} else {
|
||||
divq = ilog2(s);
|
||||
*vco_rate = (u64)target_rate << divq;
|
||||
}
|
||||
|
||||
wcd_out:
|
||||
return divq;
|
||||
}
|
||||
|
||||
/**
|
||||
* __wrpll_update_parent_rate() - update PLL data when parent rate changes
|
||||
* @c: ptr to a struct wrpll_cfg record to write PLL data to
|
||||
* @parent_rate: PLL input refclk rate (pre-R-divider)
|
||||
*
|
||||
* Pre-compute some data used by the PLL configuration algorithm when
|
||||
* the PLL's reference clock rate changes. The intention is to avoid
|
||||
* computation when the parent rate remains constant - expected to be
|
||||
* the common case.
|
||||
*
|
||||
* Returns: 0 upon success or -ERANGE if the reference clock rate is
|
||||
* out of range.
|
||||
*/
|
||||
static int __wrpll_update_parent_rate(struct wrpll_cfg *c,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u8 max_r_for_parent;
|
||||
|
||||
if (parent_rate > MAX_INPUT_FREQ || parent_rate < MIN_POST_DIVR_FREQ)
|
||||
return -ERANGE;
|
||||
|
||||
c->parent_rate = parent_rate;
|
||||
max_r_for_parent = div_u64(parent_rate, MIN_POST_DIVR_FREQ);
|
||||
c->max_r = min_t(u8, MAX_DIVR_DIVISOR, max_r_for_parent);
|
||||
|
||||
c->init_r = DIV_ROUND_UP_ULL(parent_rate, MAX_POST_DIVR_FREQ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrpll_configure() - compute PLL configuration for a target rate
|
||||
* @c: ptr to a struct wrpll_cfg record to write into
|
||||
* @target_rate: target PLL output clock rate (post-Q-divider)
|
||||
* @parent_rate: PLL input refclk rate (pre-R-divider)
|
||||
*
|
||||
* Compute the appropriate PLL signal configuration values and store
|
||||
* in PLL context @c. PLL reprogramming is not glitchless, so the
|
||||
* caller should switch any downstream logic to a different clock
|
||||
* source or clock-gate it before presenting these values to the PLL
|
||||
* configuration signals.
|
||||
*
|
||||
* The caller must pass this function a pre-initialized struct
|
||||
* wrpll_cfg record: either initialized to zero (with the
|
||||
* exception of the .name and .flags fields) or read from the PLL.
|
||||
*
|
||||
* Context: Any context. Caller must protect the memory pointed to by @c
|
||||
* from simultaneous access or modification.
|
||||
*
|
||||
* Return: 0 upon success; anything else upon failure.
|
||||
*/
|
||||
int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned long ratio;
|
||||
u64 target_vco_rate, delta, best_delta, f_pre_div, vco, vco_pre;
|
||||
u32 best_f, f, post_divr_freq;
|
||||
u8 fbdiv, divq, best_r, r;
|
||||
int range;
|
||||
|
||||
if (c->flags == 0) {
|
||||
WARN(1, "%s called with uninitialized PLL config", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Initialize rounding data if it hasn't been initialized already */
|
||||
if (parent_rate != c->parent_rate) {
|
||||
if (__wrpll_update_parent_rate(c, parent_rate)) {
|
||||
pr_err("%s: PLL input rate is out of range\n",
|
||||
__func__);
|
||||
return -ERANGE;
|
||||
}
|
||||
}
|
||||
|
||||
c->flags &= ~WRPLL_FLAGS_RESET_MASK;
|
||||
|
||||
/* Put the PLL into bypass if the user requests the parent clock rate */
|
||||
if (target_rate == parent_rate) {
|
||||
c->flags |= WRPLL_FLAGS_BYPASS_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
c->flags &= ~WRPLL_FLAGS_BYPASS_MASK;
|
||||
|
||||
/* Calculate the Q shift and target VCO rate */
|
||||
divq = __wrpll_calc_divq(target_rate, &target_vco_rate);
|
||||
if (!divq)
|
||||
return -1;
|
||||
c->divq = divq;
|
||||
|
||||
/* Precalculate the pre-Q divider target ratio */
|
||||
ratio = div64_u64((target_vco_rate << ROUND_SHIFT), parent_rate);
|
||||
|
||||
fbdiv = __wrpll_calc_fbdiv(c);
|
||||
best_r = 0;
|
||||
best_f = 0;
|
||||
best_delta = MAX_VCO_FREQ;
|
||||
|
||||
/*
|
||||
* Consider all values for R which land within
|
||||
* [MIN_POST_DIVR_FREQ, MAX_POST_DIVR_FREQ]; prefer smaller R
|
||||
*/
|
||||
for (r = c->init_r; r <= c->max_r; ++r) {
|
||||
f_pre_div = ratio * r;
|
||||
f = (f_pre_div + (1 << ROUND_SHIFT)) >> ROUND_SHIFT;
|
||||
f >>= (fbdiv - 1);
|
||||
|
||||
post_divr_freq = div_u64(parent_rate, r);
|
||||
vco_pre = fbdiv * post_divr_freq;
|
||||
vco = vco_pre * f;
|
||||
|
||||
/* Ensure rounding didn't take us out of range */
|
||||
if (vco > target_vco_rate) {
|
||||
--f;
|
||||
vco = vco_pre * f;
|
||||
} else if (vco < MIN_VCO_FREQ) {
|
||||
++f;
|
||||
vco = vco_pre * f;
|
||||
}
|
||||
|
||||
delta = abs(target_rate - vco);
|
||||
if (delta < best_delta) {
|
||||
best_delta = delta;
|
||||
best_r = r;
|
||||
best_f = f;
|
||||
}
|
||||
}
|
||||
|
||||
c->divr = best_r - 1;
|
||||
c->divf = best_f - 1;
|
||||
|
||||
post_divr_freq = div_u64(parent_rate, best_r);
|
||||
|
||||
/* Pick the best PLL jitter filter */
|
||||
range = __wrpll_calc_filter_range(post_divr_freq);
|
||||
if (range < 0)
|
||||
return range;
|
||||
c->range = range;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrpll_calc_output_rate() - calculate the PLL's target output rate
|
||||
* @c: ptr to a struct wrpll_cfg record to read from
|
||||
* @parent_rate: PLL refclk rate
|
||||
*
|
||||
* Given a pointer to the PLL's current input configuration @c and the
|
||||
* PLL's input reference clock rate @parent_rate (before the R
|
||||
* pre-divider), calculate the PLL's output clock rate (after the Q
|
||||
* post-divider).
|
||||
*
|
||||
* Context: Any context. Caller must protect the memory pointed to by @c
|
||||
* from simultaneous modification.
|
||||
*
|
||||
* Return: the PLL's output clock rate, in Hz. The return value from
|
||||
* this function is intended to be convenient to pass directly
|
||||
* to the Linux clock framework; thus there is no explicit
|
||||
* error return value.
|
||||
*/
|
||||
unsigned long wrpll_calc_output_rate(const struct wrpll_cfg *c,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u8 fbdiv;
|
||||
u64 n;
|
||||
|
||||
if (c->flags & WRPLL_FLAGS_EXT_FEEDBACK_MASK) {
|
||||
WARN(1, "external feedback mode not yet supported");
|
||||
return ULONG_MAX;
|
||||
}
|
||||
|
||||
fbdiv = __wrpll_calc_fbdiv(c);
|
||||
n = parent_rate * fbdiv * (c->divf + 1);
|
||||
n = div_u64(n, c->divr + 1);
|
||||
n >>= c->divq;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* wrpll_calc_max_lock_us() - return the time for the PLL to lock
|
||||
* @c: ptr to a struct wrpll_cfg record to read from
|
||||
*
|
||||
* Return the minimum amount of time (in microseconds) that the caller
|
||||
* must wait after reprogramming the PLL to ensure that it is locked
|
||||
* to the input frequency and stable. This is likely to depend on the DIVR
|
||||
* value; this is under discussion with the manufacturer.
|
||||
*
|
||||
* Return: the minimum amount of time the caller must wait for the PLL
|
||||
* to lock (in microseconds)
|
||||
*/
|
||||
unsigned int wrpll_calc_max_lock_us(const struct wrpll_cfg *c)
|
||||
{
|
||||
return MAX_LOCK_US;
|
||||
}
|
|
@ -14,6 +14,8 @@ obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o
|
|||
obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o
|
||||
obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o
|
||||
obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o
|
||||
obj-$(CONFIG_HAVE_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o
|
||||
obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o
|
||||
obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o
|
||||
obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o
|
||||
obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o
|
||||
|
|
|
@ -41,7 +41,7 @@ static u8 sam9260_plla_out[] = { 0, 2 };
|
|||
|
||||
static u16 sam9260_plla_icpll[] = { 1, 1 };
|
||||
|
||||
static struct clk_range sam9260_plla_outputs[] = {
|
||||
static const struct clk_range sam9260_plla_outputs[] = {
|
||||
{ .min = 80000000, .max = 160000000 },
|
||||
{ .min = 150000000, .max = 240000000 },
|
||||
};
|
||||
|
@ -58,7 +58,7 @@ static u8 sam9260_pllb_out[] = { 1 };
|
|||
|
||||
static u16 sam9260_pllb_icpll[] = { 1 };
|
||||
|
||||
static struct clk_range sam9260_pllb_outputs[] = {
|
||||
static const struct clk_range sam9260_pllb_outputs[] = {
|
||||
{ .min = 70000000, .max = 130000000 },
|
||||
};
|
||||
|
||||
|
@ -128,7 +128,7 @@ static u8 sam9g20_plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 };
|
|||
|
||||
static u16 sam9g20_plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 };
|
||||
|
||||
static struct clk_range sam9g20_plla_outputs[] = {
|
||||
static const struct clk_range sam9g20_plla_outputs[] = {
|
||||
{ .min = 745000000, .max = 800000000 },
|
||||
{ .min = 695000000, .max = 750000000 },
|
||||
{ .min = 645000000, .max = 700000000 },
|
||||
|
@ -151,7 +151,7 @@ static u8 sam9g20_pllb_out[] = { 0 };
|
|||
|
||||
static u16 sam9g20_pllb_icpll[] = { 0 };
|
||||
|
||||
static struct clk_range sam9g20_pllb_outputs[] = {
|
||||
static const struct clk_range sam9g20_pllb_outputs[] = {
|
||||
{ .min = 30000000, .max = 100000000 },
|
||||
};
|
||||
|
||||
|
@ -182,7 +182,7 @@ static const struct clk_master_characteristics sam9261_mck_characteristics = {
|
|||
.divisors = { 1, 2, 4, 0 },
|
||||
};
|
||||
|
||||
static struct clk_range sam9261_plla_outputs[] = {
|
||||
static const struct clk_range sam9261_plla_outputs[] = {
|
||||
{ .min = 80000000, .max = 200000000 },
|
||||
{ .min = 190000000, .max = 240000000 },
|
||||
};
|
||||
|
@ -199,7 +199,7 @@ static u8 sam9261_pllb_out[] = { 1 };
|
|||
|
||||
static u16 sam9261_pllb_icpll[] = { 1 };
|
||||
|
||||
static struct clk_range sam9261_pllb_outputs[] = {
|
||||
static const struct clk_range sam9261_pllb_outputs[] = {
|
||||
{ .min = 70000000, .max = 130000000 },
|
||||
};
|
||||
|
||||
|
@ -262,7 +262,7 @@ static const struct clk_master_characteristics sam9263_mck_characteristics = {
|
|||
.divisors = { 1, 2, 4, 0 },
|
||||
};
|
||||
|
||||
static struct clk_range sam9263_pll_outputs[] = {
|
||||
static const struct clk_range sam9263_pll_outputs[] = {
|
||||
{ .min = 80000000, .max = 200000000 },
|
||||
{ .min = 190000000, .max = 240000000 },
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ static const struct clk_master_characteristics sam9rl_mck_characteristics = {
|
|||
|
||||
static u8 sam9rl_plla_out[] = { 0, 2 };
|
||||
|
||||
static struct clk_range sam9rl_plla_outputs[] = {
|
||||
static const struct clk_range sam9rl_plla_outputs[] = {
|
||||
{ .min = 80000000, .max = 200000000 },
|
||||
{ .min = 190000000, .max = 240000000 },
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 };
|
|||
|
||||
static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 };
|
||||
|
||||
static struct clk_range plla_outputs[] = {
|
||||
static const struct clk_range plla_outputs[] = {
|
||||
{ .min = 745000000, .max = 800000000 },
|
||||
{ .min = 695000000, .max = 750000000 },
|
||||
{ .min = 645000000, .max = 700000000 },
|
||||
|
@ -49,6 +49,13 @@ static const struct {
|
|||
{ .n = "pck1", .p = "prog1", .id = 9 },
|
||||
};
|
||||
|
||||
static const struct clk_pcr_layout at91sam9x5_pcr_layout = {
|
||||
.offset = 0x10c,
|
||||
.cmd = BIT(12),
|
||||
.pid_mask = GENMASK(5, 0),
|
||||
.div_mask = GENMASK(17, 16),
|
||||
};
|
||||
|
||||
struct pck {
|
||||
char *n;
|
||||
u8 id;
|
||||
|
@ -242,6 +249,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(at91sam9x5_periphck); i++) {
|
||||
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
|
||||
&at91sam9x5_pcr_layout,
|
||||
at91sam9x5_periphck[i].n,
|
||||
"masterck",
|
||||
at91sam9x5_periphck[i].id,
|
||||
|
@ -254,6 +262,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
|
|||
|
||||
for (i = 0; extra_pcks[i].id; i++) {
|
||||
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
|
||||
&at91sam9x5_pcr_layout,
|
||||
extra_pcks[i].n,
|
||||
"masterck",
|
||||
extra_pcks[i].id,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
@ -31,6 +32,7 @@ struct clk_generated {
|
|||
spinlock_t *lock;
|
||||
u32 id;
|
||||
u32 gckdiv;
|
||||
const struct clk_pcr_layout *layout;
|
||||
u8 parent_id;
|
||||
bool audio_pll_allowed;
|
||||
};
|
||||
|
@ -47,14 +49,14 @@ static int clk_generated_enable(struct clk_hw *hw)
|
|||
__func__, gck->gckdiv, gck->parent_id);
|
||||
|
||||
spin_lock_irqsave(gck->lock, flags);
|
||||
regmap_write(gck->regmap, AT91_PMC_PCR,
|
||||
(gck->id & AT91_PMC_PCR_PID_MASK));
|
||||
regmap_update_bits(gck->regmap, AT91_PMC_PCR,
|
||||
AT91_PMC_PCR_GCKDIV_MASK | AT91_PMC_PCR_GCKCSS_MASK |
|
||||
AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN,
|
||||
AT91_PMC_PCR_GCKCSS(gck->parent_id) |
|
||||
AT91_PMC_PCR_CMD |
|
||||
AT91_PMC_PCR_GCKDIV(gck->gckdiv) |
|
||||
regmap_write(gck->regmap, gck->layout->offset,
|
||||
(gck->id & gck->layout->pid_mask));
|
||||
regmap_update_bits(gck->regmap, gck->layout->offset,
|
||||
AT91_PMC_PCR_GCKDIV_MASK | gck->layout->gckcss_mask |
|
||||
gck->layout->cmd | AT91_PMC_PCR_GCKEN,
|
||||
field_prep(gck->layout->gckcss_mask, gck->parent_id) |
|
||||
gck->layout->cmd |
|
||||
FIELD_PREP(AT91_PMC_PCR_GCKDIV_MASK, gck->gckdiv) |
|
||||
AT91_PMC_PCR_GCKEN);
|
||||
spin_unlock_irqrestore(gck->lock, flags);
|
||||
return 0;
|
||||
|
@ -66,11 +68,11 @@ static void clk_generated_disable(struct clk_hw *hw)
|
|||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(gck->lock, flags);
|
||||
regmap_write(gck->regmap, AT91_PMC_PCR,
|
||||
(gck->id & AT91_PMC_PCR_PID_MASK));
|
||||
regmap_update_bits(gck->regmap, AT91_PMC_PCR,
|
||||
AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN,
|
||||
AT91_PMC_PCR_CMD);
|
||||
regmap_write(gck->regmap, gck->layout->offset,
|
||||
(gck->id & gck->layout->pid_mask));
|
||||
regmap_update_bits(gck->regmap, gck->layout->offset,
|
||||
gck->layout->cmd | AT91_PMC_PCR_GCKEN,
|
||||
gck->layout->cmd);
|
||||
spin_unlock_irqrestore(gck->lock, flags);
|
||||
}
|
||||
|
||||
|
@ -81,9 +83,9 @@ static int clk_generated_is_enabled(struct clk_hw *hw)
|
|||
unsigned int status;
|
||||
|
||||
spin_lock_irqsave(gck->lock, flags);
|
||||
regmap_write(gck->regmap, AT91_PMC_PCR,
|
||||
(gck->id & AT91_PMC_PCR_PID_MASK));
|
||||
regmap_read(gck->regmap, AT91_PMC_PCR, &status);
|
||||
regmap_write(gck->regmap, gck->layout->offset,
|
||||
(gck->id & gck->layout->pid_mask));
|
||||
regmap_read(gck->regmap, gck->layout->offset, &status);
|
||||
spin_unlock_irqrestore(gck->lock, flags);
|
||||
|
||||
return status & AT91_PMC_PCR_GCKEN ? 1 : 0;
|
||||
|
@ -259,19 +261,18 @@ static void clk_generated_startup(struct clk_generated *gck)
|
|||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(gck->lock, flags);
|
||||
regmap_write(gck->regmap, AT91_PMC_PCR,
|
||||
(gck->id & AT91_PMC_PCR_PID_MASK));
|
||||
regmap_read(gck->regmap, AT91_PMC_PCR, &tmp);
|
||||
regmap_write(gck->regmap, gck->layout->offset,
|
||||
(gck->id & gck->layout->pid_mask));
|
||||
regmap_read(gck->regmap, gck->layout->offset, &tmp);
|
||||
spin_unlock_irqrestore(gck->lock, flags);
|
||||
|
||||
gck->parent_id = (tmp & AT91_PMC_PCR_GCKCSS_MASK)
|
||||
>> AT91_PMC_PCR_GCKCSS_OFFSET;
|
||||
gck->gckdiv = (tmp & AT91_PMC_PCR_GCKDIV_MASK)
|
||||
>> AT91_PMC_PCR_GCKDIV_OFFSET;
|
||||
gck->parent_id = field_get(gck->layout->gckcss_mask, tmp);
|
||||
gck->gckdiv = FIELD_GET(AT91_PMC_PCR_GCKDIV_MASK, tmp);
|
||||
}
|
||||
|
||||
struct clk_hw * __init
|
||||
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
|
||||
const struct clk_pcr_layout *layout,
|
||||
const char *name, const char **parent_names,
|
||||
u8 num_parents, u8 id, bool pll_audio,
|
||||
const struct clk_range *range)
|
||||
|
@ -298,6 +299,7 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
|
|||
gck->lock = lock;
|
||||
gck->range = *range;
|
||||
gck->audio_pll_allowed = pll_audio;
|
||||
gck->layout = layout;
|
||||
|
||||
clk_generated_startup(gck);
|
||||
hw = &gck->hw;
|
||||
|
|
|
@ -29,6 +29,7 @@ struct clk_master {
|
|||
struct regmap *regmap;
|
||||
const struct clk_master_layout *layout;
|
||||
const struct clk_master_characteristics *characteristics;
|
||||
u32 mckr;
|
||||
};
|
||||
|
||||
static inline bool clk_master_ready(struct regmap *regmap)
|
||||
|
@ -69,7 +70,7 @@ static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
|
|||
master->characteristics;
|
||||
unsigned int mckr;
|
||||
|
||||
regmap_read(master->regmap, AT91_PMC_MCKR, &mckr);
|
||||
regmap_read(master->regmap, master->layout->offset, &mckr);
|
||||
mckr &= layout->mask;
|
||||
|
||||
pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
|
||||
|
@ -95,7 +96,7 @@ static u8 clk_master_get_parent(struct clk_hw *hw)
|
|||
struct clk_master *master = to_clk_master(hw);
|
||||
unsigned int mckr;
|
||||
|
||||
regmap_read(master->regmap, AT91_PMC_MCKR, &mckr);
|
||||
regmap_read(master->regmap, master->layout->offset, &mckr);
|
||||
|
||||
return mckr & AT91_PMC_CSS;
|
||||
}
|
||||
|
@ -147,13 +148,14 @@ at91_clk_register_master(struct regmap *regmap,
|
|||
return hw;
|
||||
}
|
||||
|
||||
|
||||
const struct clk_master_layout at91rm9200_master_layout = {
|
||||
.mask = 0x31F,
|
||||
.pres_shift = 2,
|
||||
.offset = AT91_PMC_MCKR,
|
||||
};
|
||||
|
||||
const struct clk_master_layout at91sam9x5_master_layout = {
|
||||
.mask = 0x373,
|
||||
.pres_shift = 4,
|
||||
.offset = AT91_PMC_MCKR,
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
|
@ -23,9 +24,6 @@ DEFINE_SPINLOCK(pmc_pcr_lock);
|
|||
#define PERIPHERAL_ID_MAX 31
|
||||
#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX))
|
||||
|
||||
#define PERIPHERAL_RSHIFT_MASK 0x3
|
||||
#define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
|
||||
|
||||
#define PERIPHERAL_MAX_SHIFT 3
|
||||
|
||||
struct clk_peripheral {
|
||||
|
@ -43,6 +41,7 @@ struct clk_sam9x5_peripheral {
|
|||
spinlock_t *lock;
|
||||
u32 id;
|
||||
u32 div;
|
||||
const struct clk_pcr_layout *layout;
|
||||
bool auto_div;
|
||||
};
|
||||
|
||||
|
@ -169,13 +168,13 @@ static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
|
|||
return 0;
|
||||
|
||||
spin_lock_irqsave(periph->lock, flags);
|
||||
regmap_write(periph->regmap, AT91_PMC_PCR,
|
||||
(periph->id & AT91_PMC_PCR_PID_MASK));
|
||||
regmap_update_bits(periph->regmap, AT91_PMC_PCR,
|
||||
AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD |
|
||||
regmap_write(periph->regmap, periph->layout->offset,
|
||||
(periph->id & periph->layout->pid_mask));
|
||||
regmap_update_bits(periph->regmap, periph->layout->offset,
|
||||
periph->layout->div_mask | periph->layout->cmd |
|
||||
AT91_PMC_PCR_EN,
|
||||
AT91_PMC_PCR_DIV(periph->div) |
|
||||
AT91_PMC_PCR_CMD |
|
||||
field_prep(periph->layout->div_mask, periph->div) |
|
||||
periph->layout->cmd |
|
||||
AT91_PMC_PCR_EN);
|
||||
spin_unlock_irqrestore(periph->lock, flags);
|
||||
|
||||
|
@ -191,11 +190,11 @@ static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
|
|||
return;
|
||||
|
||||
spin_lock_irqsave(periph->lock, flags);
|
||||
regmap_write(periph->regmap, AT91_PMC_PCR,
|
||||
(periph->id & AT91_PMC_PCR_PID_MASK));
|
||||
regmap_update_bits(periph->regmap, AT91_PMC_PCR,
|
||||
AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD,
|
||||
AT91_PMC_PCR_CMD);
|
||||
regmap_write(periph->regmap, periph->layout->offset,
|
||||
(periph->id & periph->layout->pid_mask));
|
||||
regmap_update_bits(periph->regmap, periph->layout->offset,
|
||||
AT91_PMC_PCR_EN | periph->layout->cmd,
|
||||
periph->layout->cmd);
|
||||
spin_unlock_irqrestore(periph->lock, flags);
|
||||
}
|
||||
|
||||
|
@ -209,9 +208,9 @@ static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
|
|||
return 1;
|
||||
|
||||
spin_lock_irqsave(periph->lock, flags);
|
||||
regmap_write(periph->regmap, AT91_PMC_PCR,
|
||||
(periph->id & AT91_PMC_PCR_PID_MASK));
|
||||
regmap_read(periph->regmap, AT91_PMC_PCR, &status);
|
||||
regmap_write(periph->regmap, periph->layout->offset,
|
||||
(periph->id & periph->layout->pid_mask));
|
||||
regmap_read(periph->regmap, periph->layout->offset, &status);
|
||||
spin_unlock_irqrestore(periph->lock, flags);
|
||||
|
||||
return status & AT91_PMC_PCR_EN ? 1 : 0;
|
||||
|
@ -229,13 +228,13 @@ clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
|
|||
return parent_rate;
|
||||
|
||||
spin_lock_irqsave(periph->lock, flags);
|
||||
regmap_write(periph->regmap, AT91_PMC_PCR,
|
||||
(periph->id & AT91_PMC_PCR_PID_MASK));
|
||||
regmap_read(periph->regmap, AT91_PMC_PCR, &status);
|
||||
regmap_write(periph->regmap, periph->layout->offset,
|
||||
(periph->id & periph->layout->pid_mask));
|
||||
regmap_read(periph->regmap, periph->layout->offset, &status);
|
||||
spin_unlock_irqrestore(periph->lock, flags);
|
||||
|
||||
if (status & AT91_PMC_PCR_EN) {
|
||||
periph->div = PERIPHERAL_RSHIFT(status);
|
||||
periph->div = field_get(periph->layout->div_mask, status);
|
||||
periph->auto_div = false;
|
||||
} else {
|
||||
clk_sam9x5_peripheral_autodiv(periph);
|
||||
|
@ -328,6 +327,7 @@ static const struct clk_ops sam9x5_peripheral_ops = {
|
|||
|
||||
struct clk_hw * __init
|
||||
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
|
||||
const struct clk_pcr_layout *layout,
|
||||
const char *name, const char *parent_name,
|
||||
u32 id, const struct clk_range *range)
|
||||
{
|
||||
|
@ -354,7 +354,9 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
|
|||
periph->div = 0;
|
||||
periph->regmap = regmap;
|
||||
periph->lock = lock;
|
||||
periph->auto_div = true;
|
||||
if (layout->div_mask)
|
||||
periph->auto_div = true;
|
||||
periph->layout = layout;
|
||||
periph->range = *range;
|
||||
|
||||
hw = &periph->hw;
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019 Microchip Technology Inc.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/at91_pmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
#define PMC_PLL_CTRL0 0xc
|
||||
#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
|
||||
#define PMC_PLL_CTRL0_ENPLL BIT(28)
|
||||
#define PMC_PLL_CTRL0_ENPLLCK BIT(29)
|
||||
#define PMC_PLL_CTRL0_ENLOCK BIT(31)
|
||||
|
||||
#define PMC_PLL_CTRL1 0x10
|
||||
#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)
|
||||
#define PMC_PLL_CTRL1_MUL_MSK GENMASK(30, 24)
|
||||
|
||||
#define PMC_PLL_ACR 0x18
|
||||
#define PMC_PLL_ACR_DEFAULT 0x1b040010UL
|
||||
#define PMC_PLL_ACR_UTMIVR BIT(12)
|
||||
#define PMC_PLL_ACR_UTMIBG BIT(13)
|
||||
#define PMC_PLL_ACR_LOOP_FILTER_MSK GENMASK(31, 24)
|
||||
|
||||
#define PMC_PLL_UPDT 0x1c
|
||||
#define PMC_PLL_UPDT_UPDATE BIT(8)
|
||||
|
||||
#define PMC_PLL_ISR0 0xec
|
||||
|
||||
#define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
|
||||
#define UPLL_DIV 2
|
||||
#define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)
|
||||
|
||||
#define PLL_MAX_ID 1
|
||||
|
||||
struct sam9x60_pll {
|
||||
struct clk_hw hw;
|
||||
struct regmap *regmap;
|
||||
spinlock_t *lock;
|
||||
const struct clk_pll_characteristics *characteristics;
|
||||
u32 frac;
|
||||
u8 id;
|
||||
u8 div;
|
||||
u16 mul;
|
||||
};
|
||||
|
||||
#define to_sam9x60_pll(hw) container_of(hw, struct sam9x60_pll, hw)
|
||||
|
||||
static inline bool sam9x60_pll_ready(struct regmap *regmap, int id)
|
||||
{
|
||||
unsigned int status;
|
||||
|
||||
regmap_read(regmap, PMC_PLL_ISR0, &status);
|
||||
|
||||
return !!(status & BIT(id));
|
||||
}
|
||||
|
||||
static int sam9x60_pll_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
|
||||
struct regmap *regmap = pll->regmap;
|
||||
unsigned long flags;
|
||||
u8 div;
|
||||
u16 mul;
|
||||
u32 val;
|
||||
|
||||
spin_lock_irqsave(pll->lock, flags);
|
||||
regmap_write(regmap, PMC_PLL_UPDT, pll->id);
|
||||
|
||||
regmap_read(regmap, PMC_PLL_CTRL0, &val);
|
||||
div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val);
|
||||
|
||||
regmap_read(regmap, PMC_PLL_CTRL1, &val);
|
||||
mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
|
||||
|
||||
if (sam9x60_pll_ready(regmap, pll->id) &&
|
||||
(div == pll->div && mul == pll->mul)) {
|
||||
spin_unlock_irqrestore(pll->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Recommended value for PMC_PLL_ACR */
|
||||
val = PMC_PLL_ACR_DEFAULT;
|
||||
regmap_write(regmap, PMC_PLL_ACR, val);
|
||||
|
||||
regmap_write(regmap, PMC_PLL_CTRL1,
|
||||
FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul));
|
||||
|
||||
if (pll->characteristics->upll) {
|
||||
/* Enable the UTMI internal bandgap */
|
||||
val |= PMC_PLL_ACR_UTMIBG;
|
||||
regmap_write(regmap, PMC_PLL_ACR, val);
|
||||
|
||||
udelay(10);
|
||||
|
||||
/* Enable the UTMI internal regulator */
|
||||
val |= PMC_PLL_ACR_UTMIVR;
|
||||
regmap_write(regmap, PMC_PLL_ACR, val);
|
||||
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
regmap_update_bits(regmap, PMC_PLL_UPDT,
|
||||
PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE);
|
||||
|
||||
regmap_write(regmap, PMC_PLL_CTRL0,
|
||||
PMC_PLL_CTRL0_ENLOCK | PMC_PLL_CTRL0_ENPLL |
|
||||
PMC_PLL_CTRL0_ENPLLCK | pll->div);
|
||||
|
||||
regmap_update_bits(regmap, PMC_PLL_UPDT,
|
||||
PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE);
|
||||
|
||||
while (!sam9x60_pll_ready(regmap, pll->id))
|
||||
cpu_relax();
|
||||
|
||||
spin_unlock_irqrestore(pll->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sam9x60_pll_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
|
||||
|
||||
return sam9x60_pll_ready(pll->regmap, pll->id);
|
||||
}
|
||||
|
||||
static void sam9x60_pll_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(pll->lock, flags);
|
||||
|
||||
regmap_write(pll->regmap, PMC_PLL_UPDT, pll->id);
|
||||
|
||||
regmap_update_bits(pll->regmap, PMC_PLL_CTRL0,
|
||||
PMC_PLL_CTRL0_ENPLLCK, 0);
|
||||
|
||||
regmap_update_bits(pll->regmap, PMC_PLL_UPDT,
|
||||
PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE);
|
||||
|
||||
regmap_update_bits(pll->regmap, PMC_PLL_CTRL0, PMC_PLL_CTRL0_ENPLL, 0);
|
||||
|
||||
if (pll->characteristics->upll)
|
||||
regmap_update_bits(pll->regmap, PMC_PLL_ACR,
|
||||
PMC_PLL_ACR_UTMIBG | PMC_PLL_ACR_UTMIVR, 0);
|
||||
|
||||
regmap_update_bits(pll->regmap, PMC_PLL_UPDT,
|
||||
PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE);
|
||||
|
||||
spin_unlock_irqrestore(pll->lock, flags);
|
||||
}
|
||||
|
||||
static unsigned long sam9x60_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
|
||||
|
||||
return (parent_rate * (pll->mul + 1)) / (pll->div + 1);
|
||||
}
|
||||
|
||||
static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate,
|
||||
bool update)
|
||||
{
|
||||
const struct clk_pll_characteristics *characteristics =
|
||||
pll->characteristics;
|
||||
unsigned long bestremainder = ULONG_MAX;
|
||||
unsigned long maxdiv, mindiv, tmpdiv;
|
||||
long bestrate = -ERANGE;
|
||||
unsigned long bestdiv = 0;
|
||||
unsigned long bestmul = 0;
|
||||
unsigned long bestfrac = 0;
|
||||
|
||||
if (rate < characteristics->output[0].min ||
|
||||
rate > characteristics->output[0].max)
|
||||
return -ERANGE;
|
||||
|
||||
if (!pll->characteristics->upll) {
|
||||
mindiv = parent_rate / rate;
|
||||
if (mindiv < 2)
|
||||
mindiv = 2;
|
||||
|
||||
maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX, rate);
|
||||
if (maxdiv > PLL_DIV_MAX)
|
||||
maxdiv = PLL_DIV_MAX;
|
||||
} else {
|
||||
mindiv = maxdiv = UPLL_DIV;
|
||||
}
|
||||
|
||||
for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) {
|
||||
unsigned long remainder;
|
||||
unsigned long tmprate;
|
||||
unsigned long tmpmul;
|
||||
unsigned long tmpfrac = 0;
|
||||
|
||||
/*
|
||||
* Calculate the multiplier associated with the current
|
||||
* divider that provide the closest rate to the requested one.
|
||||
*/
|
||||
tmpmul = mult_frac(rate, tmpdiv, parent_rate);
|
||||
tmprate = mult_frac(parent_rate, tmpmul, tmpdiv);
|
||||
remainder = rate - tmprate;
|
||||
|
||||
if (remainder) {
|
||||
tmpfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * tmpdiv * (1 << 22),
|
||||
parent_rate);
|
||||
|
||||
tmprate += DIV_ROUND_CLOSEST_ULL((u64)tmpfrac * parent_rate,
|
||||
tmpdiv * (1 << 22));
|
||||
|
||||
if (tmprate > rate)
|
||||
remainder = tmprate - rate;
|
||||
else
|
||||
remainder = rate - tmprate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare the remainder with the best remainder found until
|
||||
* now and elect a new best multiplier/divider pair if the
|
||||
* current remainder is smaller than the best one.
|
||||
*/
|
||||
if (remainder < bestremainder) {
|
||||
bestremainder = remainder;
|
||||
bestdiv = tmpdiv;
|
||||
bestmul = tmpmul;
|
||||
bestrate = tmprate;
|
||||
bestfrac = tmpfrac;
|
||||
}
|
||||
|
||||
/* We've found a perfect match! */
|
||||
if (!remainder)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if bestrate is a valid output rate */
|
||||
if (bestrate < characteristics->output[0].min &&
|
||||
bestrate > characteristics->output[0].max)
|
||||
return -ERANGE;
|
||||
|
||||
if (update) {
|
||||
pll->div = bestdiv - 1;
|
||||
pll->mul = bestmul - 1;
|
||||
pll->frac = bestfrac;
|
||||
}
|
||||
|
||||
return bestrate;
|
||||
}
|
||||
|
||||
static long sam9x60_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
|
||||
|
||||
return sam9x60_pll_get_best_div_mul(pll, rate, *parent_rate, false);
|
||||
}
|
||||
|
||||
static int sam9x60_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sam9x60_pll *pll = to_sam9x60_pll(hw);
|
||||
|
||||
return sam9x60_pll_get_best_div_mul(pll, rate, parent_rate, true);
|
||||
}
|
||||
|
||||
static const struct clk_ops pll_ops = {
|
||||
.prepare = sam9x60_pll_prepare,
|
||||
.unprepare = sam9x60_pll_unprepare,
|
||||
.is_prepared = sam9x60_pll_is_prepared,
|
||||
.recalc_rate = sam9x60_pll_recalc_rate,
|
||||
.round_rate = sam9x60_pll_round_rate,
|
||||
.set_rate = sam9x60_pll_set_rate,
|
||||
};
|
||||
|
||||
struct clk_hw * __init
|
||||
sam9x60_clk_register_pll(struct regmap *regmap, spinlock_t *lock,
|
||||
const char *name, const char *parent_name, u8 id,
|
||||
const struct clk_pll_characteristics *characteristics)
|
||||
{
|
||||
struct sam9x60_pll *pll;
|
||||
struct clk_hw *hw;
|
||||
struct clk_init_data init;
|
||||
unsigned int pllr;
|
||||
int ret;
|
||||
|
||||
if (id > PLL_MAX_ID)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &pll_ops;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
init.flags = CLK_SET_RATE_GATE;
|
||||
|
||||
pll->id = id;
|
||||
pll->hw.init = &init;
|
||||
pll->characteristics = characteristics;
|
||||
pll->regmap = regmap;
|
||||
pll->lock = lock;
|
||||
|
||||
regmap_write(regmap, PMC_PLL_UPDT, id);
|
||||
regmap_read(regmap, PMC_PLL_CTRL0, &pllr);
|
||||
pll->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, pllr);
|
||||
regmap_read(regmap, PMC_PLL_CTRL1, &pllr);
|
||||
pll->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, pllr);
|
||||
|
||||
hw = &pll->hw;
|
||||
ret = clk_hw_register(NULL, hw);
|
||||
if (ret) {
|
||||
kfree(pll);
|
||||
hw = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return hw;
|
||||
}
|
||||
|
|
@ -23,9 +23,13 @@
|
|||
#define RM9200_USB_DIV_SHIFT 28
|
||||
#define RM9200_USB_DIV_TAB_SIZE 4
|
||||
|
||||
#define SAM9X5_USBS_MASK GENMASK(0, 0)
|
||||
#define SAM9X60_USBS_MASK GENMASK(1, 0)
|
||||
|
||||
struct at91sam9x5_clk_usb {
|
||||
struct clk_hw hw;
|
||||
struct regmap *regmap;
|
||||
u32 usbs_mask;
|
||||
};
|
||||
|
||||
#define to_at91sam9x5_clk_usb(hw) \
|
||||
|
@ -111,8 +115,7 @@ static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
|
|||
if (index > 1)
|
||||
return -EINVAL;
|
||||
|
||||
regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
|
||||
index ? AT91_PMC_USBS : 0);
|
||||
regmap_update_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -124,7 +127,7 @@ static u8 at91sam9x5_clk_usb_get_parent(struct clk_hw *hw)
|
|||
|
||||
regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
|
||||
|
||||
return usbr & AT91_PMC_USBS;
|
||||
return usbr & usb->usbs_mask;
|
||||
}
|
||||
|
||||
static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
@ -190,9 +193,10 @@ static const struct clk_ops at91sam9n12_usb_ops = {
|
|||
.set_rate = at91sam9x5_clk_usb_set_rate,
|
||||
};
|
||||
|
||||
struct clk_hw * __init
|
||||
at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
|
||||
const char **parent_names, u8 num_parents)
|
||||
static struct clk_hw * __init
|
||||
_at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
|
||||
const char **parent_names, u8 num_parents,
|
||||
u32 usbs_mask)
|
||||
{
|
||||
struct at91sam9x5_clk_usb *usb;
|
||||
struct clk_hw *hw;
|
||||
|
@ -212,6 +216,7 @@ at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
|
|||
|
||||
usb->hw.init = &init;
|
||||
usb->regmap = regmap;
|
||||
usb->usbs_mask = SAM9X5_USBS_MASK;
|
||||
|
||||
hw = &usb->hw;
|
||||
ret = clk_hw_register(NULL, &usb->hw);
|
||||
|
@ -223,6 +228,22 @@ at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
|
|||
return hw;
|
||||
}
|
||||
|
||||
struct clk_hw * __init
|
||||
at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
|
||||
const char **parent_names, u8 num_parents)
|
||||
{
|
||||
return _at91sam9x5_clk_register_usb(regmap, name, parent_names,
|
||||
num_parents, SAM9X5_USBS_MASK);
|
||||
}
|
||||
|
||||
struct clk_hw * __init
|
||||
sam9x60_clk_register_usb(struct regmap *regmap, const char *name,
|
||||
const char **parent_names, u8 num_parents)
|
||||
{
|
||||
return _at91sam9x5_clk_register_usb(regmap, name, parent_names,
|
||||
num_parents, SAM9X60_USBS_MASK);
|
||||
}
|
||||
|
||||
struct clk_hw * __init
|
||||
at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
|
||||
const char *parent_name)
|
||||
|
|
|
@ -93,6 +93,14 @@ CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
|
|||
of_sama5d2_clk_audio_pll_pmc_setup);
|
||||
#endif /* CONFIG_HAVE_AT91_AUDIO_PLL */
|
||||
|
||||
static const struct clk_pcr_layout dt_pcr_layout = {
|
||||
.offset = 0x10c,
|
||||
.cmd = BIT(12),
|
||||
.pid_mask = GENMASK(5, 0),
|
||||
.div_mask = GENMASK(17, 16),
|
||||
.gckcss_mask = GENMASK(10, 8),
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HAVE_AT91_GENERATED_CLK
|
||||
#define GENERATED_SOURCE_MAX 6
|
||||
|
||||
|
@ -146,7 +154,8 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
|
|||
id == GCK_ID_CLASSD))
|
||||
pll_audio = true;
|
||||
|
||||
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name,
|
||||
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
|
||||
&dt_pcr_layout, name,
|
||||
parent_names, num_parents,
|
||||
id, pll_audio, &range);
|
||||
if (IS_ERR(hw))
|
||||
|
@ -448,6 +457,7 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type)
|
|||
|
||||
hw = at91_clk_register_sam9x5_peripheral(regmap,
|
||||
&pmc_pcr_lock,
|
||||
&dt_pcr_layout,
|
||||
name,
|
||||
parent_name,
|
||||
id, &range);
|
||||
|
|
|
@ -38,6 +38,7 @@ struct clk_range {
|
|||
#define CLK_RANGE(MIN, MAX) {.min = MIN, .max = MAX,}
|
||||
|
||||
struct clk_master_layout {
|
||||
u32 offset;
|
||||
u32 mask;
|
||||
u8 pres_shift;
|
||||
};
|
||||
|
@ -65,9 +66,10 @@ extern const struct clk_pll_layout sama5d3_pll_layout;
|
|||
struct clk_pll_characteristics {
|
||||
struct clk_range input;
|
||||
int num_output;
|
||||
struct clk_range *output;
|
||||
const struct clk_range *output;
|
||||
u16 *icpll;
|
||||
u8 *out;
|
||||
u8 upll : 1;
|
||||
};
|
||||
|
||||
struct clk_programmable_layout {
|
||||
|
@ -82,6 +84,17 @@ extern const struct clk_programmable_layout at91rm9200_programmable_layout;
|
|||
extern const struct clk_programmable_layout at91sam9g45_programmable_layout;
|
||||
extern const struct clk_programmable_layout at91sam9x5_programmable_layout;
|
||||
|
||||
struct clk_pcr_layout {
|
||||
u32 offset;
|
||||
u32 cmd;
|
||||
u32 div_mask;
|
||||
u32 gckcss_mask;
|
||||
u32 pid_mask;
|
||||
};
|
||||
|
||||
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
|
||||
#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
|
||||
|
||||
#define ndck(a, s) (a[s - 1].id + 1)
|
||||
#define nck(a) (a[ARRAY_SIZE(a) - 1].id + 1)
|
||||
struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
|
||||
|
@ -107,6 +120,7 @@ at91_clk_register_audio_pll_pmc(struct regmap *regmap, const char *name,
|
|||
|
||||
struct clk_hw * __init
|
||||
at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
|
||||
const struct clk_pcr_layout *layout,
|
||||
const char *name, const char **parent_names,
|
||||
u8 num_parents, u8 id, bool pll_audio,
|
||||
const struct clk_range *range);
|
||||
|
@ -145,6 +159,7 @@ at91_clk_register_peripheral(struct regmap *regmap, const char *name,
|
|||
const char *parent_name, u32 id);
|
||||
struct clk_hw * __init
|
||||
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
|
||||
const struct clk_pcr_layout *layout,
|
||||
const char *name, const char *parent_name,
|
||||
u32 id, const struct clk_range *range);
|
||||
|
||||
|
@ -157,6 +172,11 @@ struct clk_hw * __init
|
|||
at91_clk_register_plldiv(struct regmap *regmap, const char *name,
|
||||
const char *parent_name);
|
||||
|
||||
struct clk_hw * __init
|
||||
sam9x60_clk_register_pll(struct regmap *regmap, spinlock_t *lock,
|
||||
const char *name, const char *parent_name, u8 id,
|
||||
const struct clk_pll_characteristics *characteristics);
|
||||
|
||||
struct clk_hw * __init
|
||||
at91_clk_register_programmable(struct regmap *regmap, const char *name,
|
||||
const char **parent_names, u8 num_parents, u8 id,
|
||||
|
@ -183,6 +203,9 @@ struct clk_hw * __init
|
|||
at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
|
||||
const char *parent_name);
|
||||
struct clk_hw * __init
|
||||
sam9x60_clk_register_usb(struct regmap *regmap, const char *name,
|
||||
const char **parent_names, u8 num_parents);
|
||||
struct clk_hw * __init
|
||||
at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
|
||||
const char *parent_name, const u32 *divisors);
|
||||
|
||||
|
|
|
@ -0,0 +1,307 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <dt-bindings/clock/at91.h>
|
||||
|
||||
#include "pmc.h"
|
||||
|
||||
static DEFINE_SPINLOCK(pmc_pll_lock);
|
||||
|
||||
static const struct clk_master_characteristics mck_characteristics = {
|
||||
.output = { .min = 140000000, .max = 200000000 },
|
||||
.divisors = { 1, 2, 4, 3 },
|
||||
.have_div3_pres = 1,
|
||||
};
|
||||
|
||||
static const struct clk_master_layout sam9x60_master_layout = {
|
||||
.mask = 0x373,
|
||||
.pres_shift = 4,
|
||||
.offset = 0x28,
|
||||
};
|
||||
|
||||
static const struct clk_range plla_outputs[] = {
|
||||
{ .min = 300000000, .max = 600000000 },
|
||||
};
|
||||
|
||||
static const struct clk_pll_characteristics plla_characteristics = {
|
||||
.input = { .min = 12000000, .max = 48000000 },
|
||||
.num_output = ARRAY_SIZE(plla_outputs),
|
||||
.output = plla_outputs,
|
||||
};
|
||||
|
||||
static const struct clk_range upll_outputs[] = {
|
||||
{ .min = 300000000, .max = 500000000 },
|
||||
};
|
||||
|
||||
static const struct clk_pll_characteristics upll_characteristics = {
|
||||
.input = { .min = 12000000, .max = 48000000 },
|
||||
.num_output = ARRAY_SIZE(upll_outputs),
|
||||
.output = upll_outputs,
|
||||
.upll = true,
|
||||
};
|
||||
|
||||
static const struct clk_programmable_layout sam9x60_programmable_layout = {
|
||||
.pres_shift = 8,
|
||||
.css_mask = 0x1f,
|
||||
.have_slck_mck = 0,
|
||||
};
|
||||
|
||||
static const struct clk_pcr_layout sam9x60_pcr_layout = {
|
||||
.offset = 0x88,
|
||||
.cmd = BIT(31),
|
||||
.gckcss_mask = GENMASK(12, 8),
|
||||
.pid_mask = GENMASK(6, 0),
|
||||
};
|
||||
|
||||
static const struct {
|
||||
char *n;
|
||||
char *p;
|
||||
u8 id;
|
||||
} sam9x60_systemck[] = {
|
||||
{ .n = "ddrck", .p = "masterck", .id = 2 },
|
||||
{ .n = "uhpck", .p = "usbck", .id = 6 },
|
||||
{ .n = "pck0", .p = "prog0", .id = 8 },
|
||||
{ .n = "pck1", .p = "prog1", .id = 9 },
|
||||
{ .n = "qspick", .p = "masterck", .id = 19 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
char *n;
|
||||
u8 id;
|
||||
} sam9x60_periphck[] = {
|
||||
{ .n = "pioA_clk", .id = 2, },
|
||||
{ .n = "pioB_clk", .id = 3, },
|
||||
{ .n = "pioC_clk", .id = 4, },
|
||||
{ .n = "flex0_clk", .id = 5, },
|
||||
{ .n = "flex1_clk", .id = 6, },
|
||||
{ .n = "flex2_clk", .id = 7, },
|
||||
{ .n = "flex3_clk", .id = 8, },
|
||||
{ .n = "flex6_clk", .id = 9, },
|
||||
{ .n = "flex7_clk", .id = 10, },
|
||||
{ .n = "flex8_clk", .id = 11, },
|
||||
{ .n = "sdmmc0_clk", .id = 12, },
|
||||
{ .n = "flex4_clk", .id = 13, },
|
||||
{ .n = "flex5_clk", .id = 14, },
|
||||
{ .n = "flex9_clk", .id = 15, },
|
||||
{ .n = "flex10_clk", .id = 16, },
|
||||
{ .n = "tcb0_clk", .id = 17, },
|
||||
{ .n = "pwm_clk", .id = 18, },
|
||||
{ .n = "adc_clk", .id = 19, },
|
||||
{ .n = "dma0_clk", .id = 20, },
|
||||
{ .n = "matrix_clk", .id = 21, },
|
||||
{ .n = "uhphs_clk", .id = 22, },
|
||||
{ .n = "udphs_clk", .id = 23, },
|
||||
{ .n = "macb0_clk", .id = 24, },
|
||||
{ .n = "lcd_clk", .id = 25, },
|
||||
{ .n = "sdmmc1_clk", .id = 26, },
|
||||
{ .n = "macb1_clk", .id = 27, },
|
||||
{ .n = "ssc_clk", .id = 28, },
|
||||
{ .n = "can0_clk", .id = 29, },
|
||||
{ .n = "can1_clk", .id = 30, },
|
||||
{ .n = "flex11_clk", .id = 32, },
|
||||
{ .n = "flex12_clk", .id = 33, },
|
||||
{ .n = "i2s_clk", .id = 34, },
|
||||
{ .n = "qspi_clk", .id = 35, },
|
||||
{ .n = "gfx2d_clk", .id = 36, },
|
||||
{ .n = "pit64b_clk", .id = 37, },
|
||||
{ .n = "trng_clk", .id = 38, },
|
||||
{ .n = "aes_clk", .id = 39, },
|
||||
{ .n = "tdes_clk", .id = 40, },
|
||||
{ .n = "sha_clk", .id = 41, },
|
||||
{ .n = "classd_clk", .id = 42, },
|
||||
{ .n = "isi_clk", .id = 43, },
|
||||
{ .n = "pioD_clk", .id = 44, },
|
||||
{ .n = "tcb1_clk", .id = 45, },
|
||||
{ .n = "dbgu_clk", .id = 47, },
|
||||
{ .n = "mpddr_clk", .id = 49, },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
char *n;
|
||||
u8 id;
|
||||
struct clk_range r;
|
||||
bool pll;
|
||||
} sam9x60_gck[] = {
|
||||
{ .n = "flex0_gclk", .id = 5, },
|
||||
{ .n = "flex1_gclk", .id = 6, },
|
||||
{ .n = "flex2_gclk", .id = 7, },
|
||||
{ .n = "flex3_gclk", .id = 8, },
|
||||
{ .n = "flex6_gclk", .id = 9, },
|
||||
{ .n = "flex7_gclk", .id = 10, },
|
||||
{ .n = "flex8_gclk", .id = 11, },
|
||||
{ .n = "sdmmc0_gclk", .id = 12, .r = { .min = 0, .max = 105000000 }, },
|
||||
{ .n = "flex4_gclk", .id = 13, },
|
||||
{ .n = "flex5_gclk", .id = 14, },
|
||||
{ .n = "flex9_gclk", .id = 15, },
|
||||
{ .n = "flex10_gclk", .id = 16, },
|
||||
{ .n = "tcb0_gclk", .id = 17, },
|
||||
{ .n = "adc_gclk", .id = 19, },
|
||||
{ .n = "lcd_gclk", .id = 25, .r = { .min = 0, .max = 140000000 }, },
|
||||
{ .n = "sdmmc1_gclk", .id = 26, .r = { .min = 0, .max = 105000000 }, },
|
||||
{ .n = "flex11_gclk", .id = 32, },
|
||||
{ .n = "flex12_gclk", .id = 33, },
|
||||
{ .n = "i2s_gclk", .id = 34, .r = { .min = 0, .max = 105000000 },
|
||||
.pll = true, },
|
||||
{ .n = "pit64b_gclk", .id = 37, },
|
||||
{ .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 },
|
||||
.pll = true, },
|
||||
{ .n = "tcb1_gclk", .id = 45, },
|
||||
{ .n = "dbgu_gclk", .id = 47, },
|
||||
};
|
||||
|
||||
static void __init sam9x60_pmc_setup(struct device_node *np)
|
||||
{
|
||||
struct clk_range range = CLK_RANGE(0, 0);
|
||||
const char *td_slck_name, *md_slck_name, *mainxtal_name;
|
||||
struct pmc_data *sam9x60_pmc;
|
||||
const char *parent_names[6];
|
||||
struct regmap *regmap;
|
||||
struct clk_hw *hw;
|
||||
int i;
|
||||
bool bypass;
|
||||
|
||||
i = of_property_match_string(np, "clock-names", "td_slck");
|
||||
if (i < 0)
|
||||
return;
|
||||
|
||||
td_slck_name = of_clk_get_parent_name(np, i);
|
||||
|
||||
i = of_property_match_string(np, "clock-names", "md_slck");
|
||||
if (i < 0)
|
||||
return;
|
||||
|
||||
md_slck_name = of_clk_get_parent_name(np, i);
|
||||
|
||||
i = of_property_match_string(np, "clock-names", "main_xtal");
|
||||
if (i < 0)
|
||||
return;
|
||||
mainxtal_name = of_clk_get_parent_name(np, i);
|
||||
|
||||
regmap = syscon_node_to_regmap(np);
|
||||
if (IS_ERR(regmap))
|
||||
return;
|
||||
|
||||
sam9x60_pmc = pmc_data_allocate(PMC_MAIN + 1,
|
||||
nck(sam9x60_systemck),
|
||||
nck(sam9x60_periphck),
|
||||
nck(sam9x60_gck));
|
||||
if (!sam9x60_pmc)
|
||||
return;
|
||||
|
||||
hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 24000000,
|
||||
50000000);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
bypass = of_property_read_bool(np, "atmel,osc-bypass");
|
||||
|
||||
hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name,
|
||||
bypass);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
parent_names[0] = "main_rc_osc";
|
||||
parent_names[1] = "main_osc";
|
||||
hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sam9x60_pmc->chws[PMC_MAIN] = hw;
|
||||
|
||||
hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "pllack",
|
||||
"mainck", 0, &plla_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "upllck",
|
||||
"main_osc", 1, &upll_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sam9x60_pmc->chws[PMC_UTMI] = hw;
|
||||
|
||||
parent_names[0] = md_slck_name;
|
||||
parent_names[1] = "mainck";
|
||||
parent_names[2] = "pllack";
|
||||
hw = at91_clk_register_master(regmap, "masterck", 3, parent_names,
|
||||
&sam9x60_master_layout,
|
||||
&mck_characteristics);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sam9x60_pmc->chws[PMC_MCK] = hw;
|
||||
|
||||
parent_names[0] = "pllack";
|
||||
parent_names[1] = "upllck";
|
||||
parent_names[2] = "mainck";
|
||||
parent_names[3] = "mainck";
|
||||
hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 4);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
parent_names[0] = md_slck_name;
|
||||
parent_names[1] = td_slck_name;
|
||||
parent_names[2] = "mainck";
|
||||
parent_names[3] = "masterck";
|
||||
parent_names[4] = "pllack";
|
||||
parent_names[5] = "upllck";
|
||||
for (i = 0; i < 8; i++) {
|
||||
char name[6];
|
||||
|
||||
snprintf(name, sizeof(name), "prog%d", i);
|
||||
|
||||
hw = at91_clk_register_programmable(regmap, name,
|
||||
parent_names, 6, i,
|
||||
&sam9x60_programmable_layout);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sam9x60_systemck); i++) {
|
||||
hw = at91_clk_register_system(regmap, sam9x60_systemck[i].n,
|
||||
sam9x60_systemck[i].p,
|
||||
sam9x60_systemck[i].id);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sam9x60_pmc->shws[sam9x60_systemck[i].id] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sam9x60_periphck); i++) {
|
||||
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
|
||||
&sam9x60_pcr_layout,
|
||||
sam9x60_periphck[i].n,
|
||||
"masterck",
|
||||
sam9x60_periphck[i].id,
|
||||
&range);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sam9x60_pmc->phws[sam9x60_periphck[i].id] = hw;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sam9x60_gck); i++) {
|
||||
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
|
||||
&sam9x60_pcr_layout,
|
||||
sam9x60_gck[i].n,
|
||||
parent_names, 6,
|
||||
sam9x60_gck[i].id,
|
||||
sam9x60_gck[i].pll,
|
||||
&sam9x60_gck[i].r);
|
||||
if (IS_ERR(hw))
|
||||
goto err_free;
|
||||
|
||||
sam9x60_pmc->ghws[sam9x60_gck[i].id] = hw;
|
||||
}
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sam9x60_pmc);
|
||||
|
||||
return;
|
||||
|
||||
err_free:
|
||||
pmc_data_free(sam9x60_pmc);
|
||||
}
|
||||
/* Some clks are used for a clocksource */
|
||||
CLK_OF_DECLARE(sam9x60_pmc, "microchip,sam9x60-pmc", sam9x60_pmc_setup);
|
|
@ -16,7 +16,7 @@ static u8 plla_out[] = { 0 };
|
|||
|
||||
static u16 plla_icpll[] = { 0 };
|
||||
|
||||
static struct clk_range plla_outputs[] = {
|
||||
static const struct clk_range plla_outputs[] = {
|
||||
{ .min = 600000000, .max = 1200000000 },
|
||||
};
|
||||
|
||||
|
@ -28,6 +28,13 @@ static const struct clk_pll_characteristics plla_characteristics = {
|
|||
.out = plla_out,
|
||||
};
|
||||
|
||||
static const struct clk_pcr_layout sama5d2_pcr_layout = {
|
||||
.offset = 0x10c,
|
||||
.cmd = BIT(12),
|
||||
.gckcss_mask = GENMASK(10, 8),
|
||||
.pid_mask = GENMASK(6, 0),
|
||||
};
|
||||
|
||||
static const struct {
|
||||
char *n;
|
||||
char *p;
|
||||
|
@ -274,6 +281,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(sama5d2_periphck); i++) {
|
||||
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
|
||||
&sama5d2_pcr_layout,
|
||||
sama5d2_periphck[i].n,
|
||||
"masterck",
|
||||
sama5d2_periphck[i].id,
|
||||
|
@ -286,6 +294,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(sama5d2_periph32ck); i++) {
|
||||
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
|
||||
&sama5d2_pcr_layout,
|
||||
sama5d2_periph32ck[i].n,
|
||||
"h32mxck",
|
||||
sama5d2_periph32ck[i].id,
|
||||
|
@ -304,6 +313,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
|
|||
parent_names[5] = "audiopll_pmcck";
|
||||
for (i = 0; i < ARRAY_SIZE(sama5d2_gck); i++) {
|
||||
hw = at91_clk_register_generated(regmap, &pmc_pcr_lock,
|
||||
&sama5d2_pcr_layout,
|
||||
sama5d2_gck[i].n,
|
||||
parent_names, 6,
|
||||
sama5d2_gck[i].id,
|
||||
|
|
|
@ -16,7 +16,7 @@ static u8 plla_out[] = { 0 };
|
|||
|
||||
static u16 plla_icpll[] = { 0 };
|
||||
|
||||
static struct clk_range plla_outputs[] = {
|
||||
static const struct clk_range plla_outputs[] = {
|
||||
{ .min = 600000000, .max = 1200000000 },
|
||||
};
|
||||
|
||||
|
@ -28,6 +28,12 @@ static const struct clk_pll_characteristics plla_characteristics = {
|
|||
.out = plla_out,
|
||||
};
|
||||
|
||||
static const struct clk_pcr_layout sama5d4_pcr_layout = {
|
||||
.offset = 0x10c,
|
||||
.cmd = BIT(12),
|
||||
.pid_mask = GENMASK(6, 0),
|
||||
};
|
||||
|
||||
static const struct {
|
||||
char *n;
|
||||
char *p;
|
||||
|
@ -232,6 +238,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(sama5d4_periphck); i++) {
|
||||
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
|
||||
&sama5d4_pcr_layout,
|
||||
sama5d4_periphck[i].n,
|
||||
"masterck",
|
||||
sama5d4_periphck[i].id,
|
||||
|
@ -244,6 +251,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
|
|||
|
||||
for (i = 0; i < ARRAY_SIZE(sama5d4_periph32ck); i++) {
|
||||
hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock,
|
||||
&sama5d4_pcr_layout,
|
||||
sama5d4_periph32ck[i].n,
|
||||
"h32mxck",
|
||||
sama5d4_periph32ck[i].id,
|
||||
|
|
|
@ -152,28 +152,6 @@ at91_clk_register_slow_osc(void __iomem *sckcr,
|
|||
return hw;
|
||||
}
|
||||
|
||||
static void __init
|
||||
of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, void __iomem *sckcr)
|
||||
{
|
||||
struct clk_hw *hw;
|
||||
const char *parent_name;
|
||||
const char *name = np->name;
|
||||
u32 startup;
|
||||
bool bypass;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
of_property_read_string(np, "clock-output-names", &name);
|
||||
of_property_read_u32(np, "atmel,startup-time-usec", &startup);
|
||||
bypass = of_property_read_bool(np, "atmel,osc-bypass");
|
||||
|
||||
hw = at91_clk_register_slow_osc(sckcr, name, parent_name, startup,
|
||||
bypass);
|
||||
if (IS_ERR(hw))
|
||||
return;
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
|
||||
}
|
||||
|
||||
static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
|
@ -266,28 +244,6 @@ at91_clk_register_slow_rc_osc(void __iomem *sckcr,
|
|||
return hw;
|
||||
}
|
||||
|
||||
static void __init
|
||||
of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, void __iomem *sckcr)
|
||||
{
|
||||
struct clk_hw *hw;
|
||||
u32 frequency = 0;
|
||||
u32 accuracy = 0;
|
||||
u32 startup = 0;
|
||||
const char *name = np->name;
|
||||
|
||||
of_property_read_string(np, "clock-output-names", &name);
|
||||
of_property_read_u32(np, "clock-frequency", &frequency);
|
||||
of_property_read_u32(np, "clock-accuracy", &accuracy);
|
||||
of_property_read_u32(np, "atmel,startup-time-usec", &startup);
|
||||
|
||||
hw = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy,
|
||||
startup);
|
||||
if (IS_ERR(hw))
|
||||
return;
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
|
||||
}
|
||||
|
||||
static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
|
||||
|
@ -365,68 +321,72 @@ at91_clk_register_sam9x5_slow(void __iomem *sckcr,
|
|||
return hw;
|
||||
}
|
||||
|
||||
static void __init
|
||||
of_at91sam9x5_clk_slow_setup(struct device_node *np, void __iomem *sckcr)
|
||||
static void __init at91sam9x5_sckc_register(struct device_node *np,
|
||||
unsigned int rc_osc_startup_us)
|
||||
{
|
||||
struct clk_hw *hw;
|
||||
const char *parent_names[2];
|
||||
unsigned int num_parents;
|
||||
const char *name = np->name;
|
||||
|
||||
num_parents = of_clk_get_parent_count(np);
|
||||
if (num_parents == 0 || num_parents > 2)
|
||||
return;
|
||||
|
||||
of_clk_parent_fill(np, parent_names, num_parents);
|
||||
|
||||
of_property_read_string(np, "clock-output-names", &name);
|
||||
|
||||
hw = at91_clk_register_sam9x5_slow(sckcr, name, parent_names,
|
||||
num_parents);
|
||||
if (IS_ERR(hw))
|
||||
return;
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
|
||||
}
|
||||
|
||||
static const struct of_device_id sckc_clk_ids[] __initconst = {
|
||||
/* Slow clock */
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-clk-slow-osc",
|
||||
.data = of_at91sam9x5_clk_slow_osc_setup,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-clk-slow-rc-osc",
|
||||
.data = of_at91sam9x5_clk_slow_rc_osc_setup,
|
||||
},
|
||||
{
|
||||
.compatible = "atmel,at91sam9x5-clk-slow",
|
||||
.data = of_at91sam9x5_clk_slow_setup,
|
||||
},
|
||||
{ /*sentinel*/ }
|
||||
};
|
||||
|
||||
static void __init of_at91sam9x5_sckc_setup(struct device_node *np)
|
||||
{
|
||||
struct device_node *childnp;
|
||||
void (*clk_setup)(struct device_node *, void __iomem *);
|
||||
const struct of_device_id *clk_id;
|
||||
const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
|
||||
void __iomem *regbase = of_iomap(np, 0);
|
||||
struct device_node *child = NULL;
|
||||
const char *xtal_name;
|
||||
struct clk_hw *hw;
|
||||
bool bypass;
|
||||
|
||||
if (!regbase)
|
||||
return;
|
||||
|
||||
for_each_child_of_node(np, childnp) {
|
||||
clk_id = of_match_node(sckc_clk_ids, childnp);
|
||||
if (!clk_id)
|
||||
continue;
|
||||
clk_setup = clk_id->data;
|
||||
clk_setup(childnp, regbase);
|
||||
hw = at91_clk_register_slow_rc_osc(regbase, parent_names[0], 32768,
|
||||
50000000, rc_osc_startup_us);
|
||||
if (IS_ERR(hw))
|
||||
return;
|
||||
|
||||
xtal_name = of_clk_get_parent_name(np, 0);
|
||||
if (!xtal_name) {
|
||||
/* DT backward compatibility */
|
||||
child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc");
|
||||
if (!child)
|
||||
return;
|
||||
|
||||
xtal_name = of_clk_get_parent_name(child, 0);
|
||||
bypass = of_property_read_bool(child, "atmel,osc-bypass");
|
||||
|
||||
child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow");
|
||||
} else {
|
||||
bypass = of_property_read_bool(np, "atmel,osc-bypass");
|
||||
}
|
||||
|
||||
if (!xtal_name)
|
||||
return;
|
||||
|
||||
hw = at91_clk_register_slow_osc(regbase, parent_names[1], xtal_name,
|
||||
1200000, bypass);
|
||||
if (IS_ERR(hw))
|
||||
return;
|
||||
|
||||
hw = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2);
|
||||
if (IS_ERR(hw))
|
||||
return;
|
||||
|
||||
of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
|
||||
|
||||
/* DT backward compatibility */
|
||||
if (child)
|
||||
of_clk_add_hw_provider(child, of_clk_hw_simple_get, hw);
|
||||
}
|
||||
|
||||
static void __init of_at91sam9x5_sckc_setup(struct device_node *np)
|
||||
{
|
||||
at91sam9x5_sckc_register(np, 75);
|
||||
}
|
||||
CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
|
||||
of_at91sam9x5_sckc_setup);
|
||||
|
||||
static void __init of_sama5d3_sckc_setup(struct device_node *np)
|
||||
{
|
||||
at91sam9x5_sckc_register(np, 500);
|
||||
}
|
||||
CLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc",
|
||||
of_sama5d3_sckc_setup);
|
||||
|
||||
static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
|
||||
|
|
|
@ -87,10 +87,10 @@ struct aspeed_clk_gate {
|
|||
/* TODO: ask Aspeed about the actual parent data */
|
||||
static const struct aspeed_gate_data aspeed_gates[] = {
|
||||
/* clk rst name parent flags */
|
||||
[ASPEED_CLK_GATE_ECLK] = { 0, -1, "eclk-gate", "eclk", 0 }, /* Video Engine */
|
||||
[ASPEED_CLK_GATE_ECLK] = { 0, 6, "eclk-gate", "eclk", 0 }, /* Video Engine */
|
||||
[ASPEED_CLK_GATE_GCLK] = { 1, 7, "gclk-gate", NULL, 0 }, /* 2D engine */
|
||||
[ASPEED_CLK_GATE_MCLK] = { 2, -1, "mclk-gate", "mpll", CLK_IS_CRITICAL }, /* SDRAM */
|
||||
[ASPEED_CLK_GATE_VCLK] = { 3, 6, "vclk-gate", NULL, 0 }, /* Video Capture */
|
||||
[ASPEED_CLK_GATE_VCLK] = { 3, -1, "vclk-gate", NULL, 0 }, /* Video Capture */
|
||||
[ASPEED_CLK_GATE_BCLK] = { 4, 8, "bclk-gate", "bclk", CLK_IS_CRITICAL }, /* PCIe/PCI */
|
||||
[ASPEED_CLK_GATE_DCLK] = { 5, -1, "dclk-gate", NULL, CLK_IS_CRITICAL }, /* DAC */
|
||||
[ASPEED_CLK_GATE_REFCLK] = { 6, -1, "refclk-gate", "clkin", CLK_IS_CRITICAL },
|
||||
|
@ -113,6 +113,24 @@ static const struct aspeed_gate_data aspeed_gates[] = {
|
|||
[ASPEED_CLK_GATE_LHCCLK] = { 28, -1, "lhclk-gate", "lhclk", 0 }, /* LPC master/LPC+ */
|
||||
};
|
||||
|
||||
static const char * const eclk_parent_names[] = {
|
||||
"mpll",
|
||||
"hpll",
|
||||
"dpll",
|
||||
};
|
||||
|
||||
static const struct clk_div_table ast2500_eclk_div_table[] = {
|
||||
{ 0x0, 2 },
|
||||
{ 0x1, 2 },
|
||||
{ 0x2, 3 },
|
||||
{ 0x3, 4 },
|
||||
{ 0x4, 5 },
|
||||
{ 0x5, 6 },
|
||||
{ 0x6, 7 },
|
||||
{ 0x7, 8 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static const struct clk_div_table ast2500_mac_div_table[] = {
|
||||
{ 0x0, 4 }, /* Yep, really. Aspeed confirmed this is correct */
|
||||
{ 0x1, 4 },
|
||||
|
@ -192,18 +210,21 @@ static struct clk_hw *aspeed_ast2500_calc_pll(const char *name, u32 val)
|
|||
|
||||
struct aspeed_clk_soc_data {
|
||||
const struct clk_div_table *div_table;
|
||||
const struct clk_div_table *eclk_div_table;
|
||||
const struct clk_div_table *mac_div_table;
|
||||
struct clk_hw *(*calc_pll)(const char *name, u32 val);
|
||||
};
|
||||
|
||||
static const struct aspeed_clk_soc_data ast2500_data = {
|
||||
.div_table = ast2500_div_table,
|
||||
.eclk_div_table = ast2500_eclk_div_table,
|
||||
.mac_div_table = ast2500_mac_div_table,
|
||||
.calc_pll = aspeed_ast2500_calc_pll,
|
||||
};
|
||||
|
||||
static const struct aspeed_clk_soc_data ast2400_data = {
|
||||
.div_table = ast2400_div_table,
|
||||
.eclk_div_table = ast2400_div_table,
|
||||
.mac_div_table = ast2400_div_table,
|
||||
.calc_pll = aspeed_ast2400_calc_pll,
|
||||
};
|
||||
|
@ -522,6 +543,22 @@ static int aspeed_clk_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(hw);
|
||||
aspeed_clk_data->hws[ASPEED_CLK_24M] = hw;
|
||||
|
||||
hw = clk_hw_register_mux(dev, "eclk-mux", eclk_parent_names,
|
||||
ARRAY_SIZE(eclk_parent_names), 0,
|
||||
scu_base + ASPEED_CLK_SELECTION, 2, 0x3, 0,
|
||||
&aspeed_clk_lock);
|
||||
if (IS_ERR(hw))
|
||||
return PTR_ERR(hw);
|
||||
aspeed_clk_data->hws[ASPEED_CLK_ECLK_MUX] = hw;
|
||||
|
||||
hw = clk_hw_register_divider_table(dev, "eclk", "eclk-mux", 0,
|
||||
scu_base + ASPEED_CLK_SELECTION, 28,
|
||||
3, 0, soc_data->eclk_div_table,
|
||||
&aspeed_clk_lock);
|
||||
if (IS_ERR(hw))
|
||||
return PTR_ERR(hw);
|
||||
aspeed_clk_data->hws[ASPEED_CLK_ECLK] = hw;
|
||||
|
||||
/*
|
||||
* TODO: There are a number of clocks that not included in this driver
|
||||
* as more information is required:
|
||||
|
@ -531,7 +568,6 @@ static int aspeed_clk_probe(struct platform_device *pdev)
|
|||
* RGMII
|
||||
* RMII
|
||||
* UART[1..5] clock source mux
|
||||
* Video Engine (ECLK) mux and clock divider
|
||||
*/
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) {
|
||||
|
|
|
@ -218,7 +218,7 @@ struct clk_hw *clk_hw_register_composite(struct device *dev, const char *name,
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.flags = flags;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
hw = &composite->hw;
|
||||
|
|
|
@ -25,6 +25,22 @@
|
|||
* parent - fixed parent. No clk_set_parent support
|
||||
*/
|
||||
|
||||
static inline u32 clk_div_readl(struct clk_divider *divider)
|
||||
{
|
||||
if (divider->flags & CLK_DIVIDER_BIG_ENDIAN)
|
||||
return ioread32be(divider->reg);
|
||||
|
||||
return readl(divider->reg);
|
||||
}
|
||||
|
||||
static inline void clk_div_writel(struct clk_divider *divider, u32 val)
|
||||
{
|
||||
if (divider->flags & CLK_DIVIDER_BIG_ENDIAN)
|
||||
iowrite32be(val, divider->reg);
|
||||
else
|
||||
writel(val, divider->reg);
|
||||
}
|
||||
|
||||
static unsigned int _get_table_maxdiv(const struct clk_div_table *table,
|
||||
u8 width)
|
||||
{
|
||||
|
@ -135,7 +151,7 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
|
|||
struct clk_divider *divider = to_clk_divider(hw);
|
||||
unsigned int val;
|
||||
|
||||
val = clk_readl(divider->reg) >> divider->shift;
|
||||
val = clk_div_readl(divider) >> divider->shift;
|
||||
val &= clk_div_mask(divider->width);
|
||||
|
||||
return divider_recalc_rate(hw, parent_rate, val, divider->table,
|
||||
|
@ -370,7 +386,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
|
||||
u32 val;
|
||||
|
||||
val = clk_readl(divider->reg) >> divider->shift;
|
||||
val = clk_div_readl(divider) >> divider->shift;
|
||||
val &= clk_div_mask(divider->width);
|
||||
|
||||
return divider_ro_round_rate(hw, rate, prate, divider->table,
|
||||
|
@ -420,11 +436,11 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
|
||||
val = clk_div_mask(divider->width) << (divider->shift + 16);
|
||||
} else {
|
||||
val = clk_readl(divider->reg);
|
||||
val = clk_div_readl(divider);
|
||||
val &= ~(clk_div_mask(divider->width) << divider->shift);
|
||||
}
|
||||
val |= (u32)value << divider->shift;
|
||||
clk_writel(val, divider->reg);
|
||||
clk_div_writel(divider, val);
|
||||
|
||||
if (divider->lock)
|
||||
spin_unlock_irqrestore(divider->lock, flags);
|
||||
|
@ -475,7 +491,7 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name,
|
|||
init.ops = &clk_divider_ro_ops;
|
||||
else
|
||||
init.ops = &clk_divider_ops;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.flags = flags;
|
||||
init.parent_names = (parent_name ? &parent_name: NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
|
||||
|
|
|
@ -64,12 +64,14 @@ const struct clk_ops clk_fixed_factor_ops = {
|
|||
};
|
||||
EXPORT_SYMBOL_GPL(clk_fixed_factor_ops);
|
||||
|
||||
struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
|
||||
const char *name, const char *parent_name, unsigned long flags,
|
||||
unsigned int mult, unsigned int div)
|
||||
static struct clk_hw *
|
||||
__clk_hw_register_fixed_factor(struct device *dev, struct device_node *np,
|
||||
const char *name, const char *parent_name, int index,
|
||||
unsigned long flags, unsigned int mult, unsigned int div)
|
||||
{
|
||||
struct clk_fixed_factor *fix;
|
||||
struct clk_init_data init;
|
||||
struct clk_init_data init = { };
|
||||
struct clk_parent_data pdata = { .index = index };
|
||||
struct clk_hw *hw;
|
||||
int ret;
|
||||
|
||||
|
@ -84,12 +86,18 @@ struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
|
|||
|
||||
init.name = name;
|
||||
init.ops = &clk_fixed_factor_ops;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.parent_names = &parent_name;
|
||||
init.flags = flags;
|
||||
if (parent_name)
|
||||
init.parent_names = &parent_name;
|
||||
else
|
||||
init.parent_data = &pdata;
|
||||
init.num_parents = 1;
|
||||
|
||||
hw = &fix->hw;
|
||||
ret = clk_hw_register(dev, hw);
|
||||
if (dev)
|
||||
ret = clk_hw_register(dev, hw);
|
||||
else
|
||||
ret = of_clk_hw_register(np, hw);
|
||||
if (ret) {
|
||||
kfree(fix);
|
||||
hw = ERR_PTR(ret);
|
||||
|
@ -97,6 +105,14 @@ struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
|
|||
|
||||
return hw;
|
||||
}
|
||||
|
||||
struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
|
||||
const char *name, const char *parent_name, unsigned long flags,
|
||||
unsigned int mult, unsigned int div)
|
||||
{
|
||||
return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, -1,
|
||||
flags, mult, div);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor);
|
||||
|
||||
struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
|
||||
|
@ -143,11 +159,10 @@ static const struct of_device_id set_rate_parent_matches[] = {
|
|||
{ /* Sentinel */ },
|
||||
};
|
||||
|
||||
static struct clk *_of_fixed_factor_clk_setup(struct device_node *node)
|
||||
static struct clk_hw *_of_fixed_factor_clk_setup(struct device_node *node)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_hw *hw;
|
||||
const char *clk_name = node->name;
|
||||
const char *parent_name;
|
||||
unsigned long flags = 0;
|
||||
u32 div, mult;
|
||||
int ret;
|
||||
|
@ -165,30 +180,28 @@ static struct clk *_of_fixed_factor_clk_setup(struct device_node *node)
|
|||
}
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &clk_name);
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
|
||||
if (of_match_node(set_rate_parent_matches, node))
|
||||
flags |= CLK_SET_RATE_PARENT;
|
||||
|
||||
clk = clk_register_fixed_factor(NULL, clk_name, parent_name, flags,
|
||||
mult, div);
|
||||
if (IS_ERR(clk)) {
|
||||
hw = __clk_hw_register_fixed_factor(NULL, node, clk_name, NULL, 0,
|
||||
flags, mult, div);
|
||||
if (IS_ERR(hw)) {
|
||||
/*
|
||||
* If parent clock is not registered, registration would fail.
|
||||
* Clear OF_POPULATED flag so that clock registration can be
|
||||
* attempted again from probe function.
|
||||
*/
|
||||
of_node_clear_flag(node, OF_POPULATED);
|
||||
return clk;
|
||||
return ERR_CAST(hw);
|
||||
}
|
||||
|
||||
ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
|
||||
if (ret) {
|
||||
clk_unregister(clk);
|
||||
clk_hw_unregister_fixed_factor(hw);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return clk;
|
||||
return hw;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -203,17 +216,17 @@ CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
|
|||
|
||||
static int of_fixed_factor_clk_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct clk *clk = platform_get_drvdata(pdev);
|
||||
struct clk_hw *clk = platform_get_drvdata(pdev);
|
||||
|
||||
of_clk_del_provider(pdev->dev.of_node);
|
||||
clk_unregister_fixed_factor(clk);
|
||||
clk_hw_unregister_fixed_factor(clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int of_fixed_factor_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct clk_hw *clk;
|
||||
|
||||
/*
|
||||
* This function is not executed when of_fixed_factor_clk_setup
|
||||
|
|
|
@ -68,7 +68,7 @@ struct clk_hw *clk_hw_register_fixed_rate_with_accuracy(struct device *dev,
|
|||
|
||||
init.name = name;
|
||||
init.ops = &clk_fixed_rate_ops;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.flags = flags;
|
||||
init.parent_names = (parent_name ? &parent_name: NULL);
|
||||
init.num_parents = (parent_name ? 1 : 0);
|
||||
|
||||
|
|
|
@ -13,6 +13,22 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/rational.h>
|
||||
|
||||
static inline u32 clk_fd_readl(struct clk_fractional_divider *fd)
|
||||
{
|
||||
if (fd->flags & CLK_FRAC_DIVIDER_BIG_ENDIAN)
|
||||
return ioread32be(fd->reg);
|
||||
|
||||
return readl(fd->reg);
|
||||
}
|
||||
|
||||
static inline void clk_fd_writel(struct clk_fractional_divider *fd, u32 val)
|
||||
{
|
||||
if (fd->flags & CLK_FRAC_DIVIDER_BIG_ENDIAN)
|
||||
iowrite32be(val, fd->reg);
|
||||
else
|
||||
writel(val, fd->reg);
|
||||
}
|
||||
|
||||
static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
|
@ -27,7 +43,7 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
|
|||
else
|
||||
__acquire(fd->lock);
|
||||
|
||||
val = clk_readl(fd->reg);
|
||||
val = clk_fd_readl(fd);
|
||||
|
||||
if (fd->lock)
|
||||
spin_unlock_irqrestore(fd->lock, flags);
|
||||
|
@ -115,10 +131,10 @@ static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
else
|
||||
__acquire(fd->lock);
|
||||
|
||||
val = clk_readl(fd->reg);
|
||||
val = clk_fd_readl(fd);
|
||||
val &= ~(fd->mmask | fd->nmask);
|
||||
val |= (m << fd->mshift) | (n << fd->nshift);
|
||||
clk_writel(val, fd->reg);
|
||||
clk_fd_writel(fd, val);
|
||||
|
||||
if (fd->lock)
|
||||
spin_unlock_irqrestore(fd->lock, flags);
|
||||
|
@ -151,7 +167,7 @@ struct clk_hw *clk_hw_register_fractional_divider(struct device *dev,
|
|||
|
||||
init.name = name;
|
||||
init.ops = &clk_fractional_divider_ops;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.flags = flags;
|
||||
init.parent_names = parent_name ? &parent_name : NULL;
|
||||
init.num_parents = parent_name ? 1 : 0;
|
||||
|
||||
|
|
|
@ -23,6 +23,22 @@
|
|||
* parent - fixed parent. No clk_set_parent support
|
||||
*/
|
||||
|
||||
static inline u32 clk_gate_readl(struct clk_gate *gate)
|
||||
{
|
||||
if (gate->flags & CLK_GATE_BIG_ENDIAN)
|
||||
return ioread32be(gate->reg);
|
||||
|
||||
return readl(gate->reg);
|
||||
}
|
||||
|
||||
static inline void clk_gate_writel(struct clk_gate *gate, u32 val)
|
||||
{
|
||||
if (gate->flags & CLK_GATE_BIG_ENDIAN)
|
||||
iowrite32be(val, gate->reg);
|
||||
else
|
||||
writel(val, gate->reg);
|
||||
}
|
||||
|
||||
/*
|
||||
* It works on following logic:
|
||||
*
|
||||
|
@ -55,7 +71,7 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable)
|
|||
if (set)
|
||||
reg |= BIT(gate->bit_idx);
|
||||
} else {
|
||||
reg = clk_readl(gate->reg);
|
||||
reg = clk_gate_readl(gate);
|
||||
|
||||
if (set)
|
||||
reg |= BIT(gate->bit_idx);
|
||||
|
@ -63,7 +79,7 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable)
|
|||
reg &= ~BIT(gate->bit_idx);
|
||||
}
|
||||
|
||||
clk_writel(reg, gate->reg);
|
||||
clk_gate_writel(gate, reg);
|
||||
|
||||
if (gate->lock)
|
||||
spin_unlock_irqrestore(gate->lock, flags);
|
||||
|
@ -88,7 +104,7 @@ int clk_gate_is_enabled(struct clk_hw *hw)
|
|||
u32 reg;
|
||||
struct clk_gate *gate = to_clk_gate(hw);
|
||||
|
||||
reg = clk_readl(gate->reg);
|
||||
reg = clk_gate_readl(gate);
|
||||
|
||||
/* if a set bit disables this clk, flip it before masking */
|
||||
if (gate->flags & CLK_GATE_SET_TO_DISABLE)
|
||||
|
@ -142,7 +158,7 @@ struct clk_hw *clk_hw_register_gate(struct device *dev, const char *name,
|
|||
|
||||
init.name = name;
|
||||
init.ops = &clk_gate_ops;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.flags = flags;
|
||||
init.parent_names = parent_name ? &parent_name : NULL;
|
||||
init.num_parents = parent_name ? 1 : 0;
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ static struct clk_hw *clk_register_gpio(struct device *dev, const char *name,
|
|||
|
||||
init.name = name;
|
||||
init.ops = clk_gpio_ops;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.flags = flags;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
|
@ -272,7 +271,7 @@ static const struct clk_ops periclk_ops = {
|
|||
.set_rate = clk_periclk_set_rate,
|
||||
};
|
||||
|
||||
static __init struct clk *hb_clk_init(struct device_node *node, const struct clk_ops *ops)
|
||||
static void __init hb_clk_init(struct device_node *node, const struct clk_ops *ops, unsigned long clkflags)
|
||||
{
|
||||
u32 reg;
|
||||
struct hb_clk *hb_clk;
|
||||
|
@ -284,11 +283,11 @@ static __init struct clk *hb_clk_init(struct device_node *node, const struct clk
|
|||
|
||||
rc = of_property_read_u32(node, "reg", ®);
|
||||
if (WARN_ON(rc))
|
||||
return NULL;
|
||||
return;
|
||||
|
||||
hb_clk = kzalloc(sizeof(*hb_clk), GFP_KERNEL);
|
||||
if (WARN_ON(!hb_clk))
|
||||
return NULL;
|
||||
return;
|
||||
|
||||
/* Map system registers */
|
||||
srnp = of_find_compatible_node(NULL, NULL, "calxeda,hb-sregs");
|
||||
|
@ -301,7 +300,7 @@ static __init struct clk *hb_clk_init(struct device_node *node, const struct clk
|
|||
|
||||
init.name = clk_name;
|
||||
init.ops = ops;
|
||||
init.flags = 0;
|
||||
init.flags = clkflags;
|
||||
parent_name = of_clk_get_parent_name(node, 0);
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
@ -311,33 +310,31 @@ static __init struct clk *hb_clk_init(struct device_node *node, const struct clk
|
|||
rc = clk_hw_register(NULL, &hb_clk->hw);
|
||||
if (WARN_ON(rc)) {
|
||||
kfree(hb_clk);
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &hb_clk->hw);
|
||||
return hb_clk->hw.clk;
|
||||
of_clk_add_hw_provider(node, of_clk_hw_simple_get, &hb_clk->hw);
|
||||
}
|
||||
|
||||
static void __init hb_pll_init(struct device_node *node)
|
||||
{
|
||||
hb_clk_init(node, &clk_pll_ops);
|
||||
hb_clk_init(node, &clk_pll_ops, 0);
|
||||
}
|
||||
CLK_OF_DECLARE(hb_pll, "calxeda,hb-pll-clock", hb_pll_init);
|
||||
|
||||
static void __init hb_a9periph_init(struct device_node *node)
|
||||
{
|
||||
hb_clk_init(node, &a9periphclk_ops);
|
||||
hb_clk_init(node, &a9periphclk_ops, 0);
|
||||
}
|
||||
CLK_OF_DECLARE(hb_a9periph, "calxeda,hb-a9periph-clock", hb_a9periph_init);
|
||||
|
||||
static void __init hb_a9bus_init(struct device_node *node)
|
||||
{
|
||||
struct clk *clk = hb_clk_init(node, &a9bclk_ops);
|
||||
clk_prepare_enable(clk);
|
||||
hb_clk_init(node, &a9bclk_ops, CLK_IS_CRITICAL);
|
||||
}
|
||||
CLK_OF_DECLARE(hb_a9bus, "calxeda,hb-a9bus-clock", hb_a9bus_init);
|
||||
|
||||
static void __init hb_emmc_init(struct device_node *node)
|
||||
{
|
||||
hb_clk_init(node, &periclk_ops);
|
||||
hb_clk_init(node, &periclk_ops, 0);
|
||||
}
|
||||
CLK_OF_DECLARE(hb_emmc, "calxeda,hb-emmc-clock", hb_emmc_init);
|
||||
|
|
|
@ -0,0 +1,336 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Lochnagar clock control
|
||||
*
|
||||
* Copyright (c) 2017-2018 Cirrus Logic, Inc. and
|
||||
* Cirrus Logic International Semiconductor Ltd.
|
||||
*
|
||||
* Author: Charles Keepax <ckeepax@opensource.cirrus.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/mfd/lochnagar.h>
|
||||
#include <linux/mfd/lochnagar1_regs.h>
|
||||
#include <linux/mfd/lochnagar2_regs.h>
|
||||
|
||||
#include <dt-bindings/clk/lochnagar.h>
|
||||
|
||||
#define LOCHNAGAR_NUM_CLOCKS (LOCHNAGAR_SPDIF_CLKOUT + 1)
|
||||
|
||||
struct lochnagar_clk {
|
||||
const char * const name;
|
||||
struct clk_hw hw;
|
||||
|
||||
struct lochnagar_clk_priv *priv;
|
||||
|
||||
u16 cfg_reg;
|
||||
u16 ena_mask;
|
||||
|
||||
u16 src_reg;
|
||||
u16 src_mask;
|
||||
};
|
||||
|
||||
struct lochnagar_clk_priv {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
enum lochnagar_type type;
|
||||
|
||||
const char **parents;
|
||||
unsigned int nparents;
|
||||
|
||||
struct lochnagar_clk lclks[LOCHNAGAR_NUM_CLOCKS];
|
||||
};
|
||||
|
||||
static const char * const lochnagar1_clk_parents[] = {
|
||||
"ln-none",
|
||||
"ln-spdif-mclk",
|
||||
"ln-psia1-mclk",
|
||||
"ln-psia2-mclk",
|
||||
"ln-cdc-clkout",
|
||||
"ln-dsp-clkout",
|
||||
"ln-pmic-32k",
|
||||
"ln-gf-mclk1",
|
||||
"ln-gf-mclk3",
|
||||
"ln-gf-mclk2",
|
||||
"ln-gf-mclk4",
|
||||
};
|
||||
|
||||
static const char * const lochnagar2_clk_parents[] = {
|
||||
"ln-none",
|
||||
"ln-cdc-clkout",
|
||||
"ln-dsp-clkout",
|
||||
"ln-pmic-32k",
|
||||
"ln-spdif-mclk",
|
||||
"ln-clk-12m",
|
||||
"ln-clk-11m",
|
||||
"ln-clk-24m",
|
||||
"ln-clk-22m",
|
||||
"ln-clk-8m",
|
||||
"ln-usb-clk-24m",
|
||||
"ln-gf-mclk1",
|
||||
"ln-gf-mclk3",
|
||||
"ln-gf-mclk2",
|
||||
"ln-psia1-mclk",
|
||||
"ln-psia2-mclk",
|
||||
"ln-spdif-clkout",
|
||||
"ln-adat-mclk",
|
||||
"ln-usb-clk-12m",
|
||||
};
|
||||
|
||||
#define LN1_CLK(ID, NAME, REG) \
|
||||
[LOCHNAGAR_##ID] = { \
|
||||
.name = NAME, \
|
||||
.cfg_reg = LOCHNAGAR1_##REG, \
|
||||
.ena_mask = LOCHNAGAR1_##ID##_ENA_MASK, \
|
||||
.src_reg = LOCHNAGAR1_##ID##_SEL, \
|
||||
.src_mask = LOCHNAGAR1_SRC_MASK, \
|
||||
}
|
||||
|
||||
#define LN2_CLK(ID, NAME) \
|
||||
[LOCHNAGAR_##ID] = { \
|
||||
.name = NAME, \
|
||||
.cfg_reg = LOCHNAGAR2_##ID##_CTRL, \
|
||||
.src_reg = LOCHNAGAR2_##ID##_CTRL, \
|
||||
.ena_mask = LOCHNAGAR2_CLK_ENA_MASK, \
|
||||
.src_mask = LOCHNAGAR2_CLK_SRC_MASK, \
|
||||
}
|
||||
|
||||
static const struct lochnagar_clk lochnagar1_clks[LOCHNAGAR_NUM_CLOCKS] = {
|
||||
LN1_CLK(CDC_MCLK1, "ln-cdc-mclk1", CDC_AIF_CTRL2),
|
||||
LN1_CLK(CDC_MCLK2, "ln-cdc-mclk2", CDC_AIF_CTRL2),
|
||||
LN1_CLK(DSP_CLKIN, "ln-dsp-clkin", DSP_AIF),
|
||||
LN1_CLK(GF_CLKOUT1, "ln-gf-clkout1", GF_AIF1),
|
||||
};
|
||||
|
||||
static const struct lochnagar_clk lochnagar2_clks[LOCHNAGAR_NUM_CLOCKS] = {
|
||||
LN2_CLK(CDC_MCLK1, "ln-cdc-mclk1"),
|
||||
LN2_CLK(CDC_MCLK2, "ln-cdc-mclk2"),
|
||||
LN2_CLK(DSP_CLKIN, "ln-dsp-clkin"),
|
||||
LN2_CLK(GF_CLKOUT1, "ln-gf-clkout1"),
|
||||
LN2_CLK(GF_CLKOUT2, "ln-gf-clkout2"),
|
||||
LN2_CLK(PSIA1_MCLK, "ln-psia1-mclk"),
|
||||
LN2_CLK(PSIA2_MCLK, "ln-psia2-mclk"),
|
||||
LN2_CLK(SPDIF_MCLK, "ln-spdif-mclk"),
|
||||
LN2_CLK(ADAT_MCLK, "ln-adat-mclk"),
|
||||
LN2_CLK(SOUNDCARD_MCLK, "ln-soundcard-mclk"),
|
||||
};
|
||||
|
||||
static inline struct lochnagar_clk *lochnagar_hw_to_lclk(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct lochnagar_clk, hw);
|
||||
}
|
||||
|
||||
static int lochnagar_clk_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
|
||||
struct lochnagar_clk_priv *priv = lclk->priv;
|
||||
struct regmap *regmap = priv->regmap;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(regmap, lclk->cfg_reg,
|
||||
lclk->ena_mask, lclk->ena_mask);
|
||||
if (ret < 0)
|
||||
dev_dbg(priv->dev, "Failed to prepare %s: %d\n",
|
||||
lclk->name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lochnagar_clk_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
|
||||
struct lochnagar_clk_priv *priv = lclk->priv;
|
||||
struct regmap *regmap = priv->regmap;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(regmap, lclk->cfg_reg, lclk->ena_mask, 0);
|
||||
if (ret < 0)
|
||||
dev_dbg(priv->dev, "Failed to unprepare %s: %d\n",
|
||||
lclk->name, ret);
|
||||
}
|
||||
|
||||
static int lochnagar_clk_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
|
||||
struct lochnagar_clk_priv *priv = lclk->priv;
|
||||
struct regmap *regmap = priv->regmap;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(regmap, lclk->src_reg, lclk->src_mask, index);
|
||||
if (ret < 0)
|
||||
dev_dbg(priv->dev, "Failed to reparent %s: %d\n",
|
||||
lclk->name, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 lochnagar_clk_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
|
||||
struct lochnagar_clk_priv *priv = lclk->priv;
|
||||
struct regmap *regmap = priv->regmap;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(regmap, lclk->src_reg, &val);
|
||||
if (ret < 0) {
|
||||
dev_dbg(priv->dev, "Failed to read parent of %s: %d\n",
|
||||
lclk->name, ret);
|
||||
return priv->nparents;
|
||||
}
|
||||
|
||||
val &= lclk->src_mask;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static const struct clk_ops lochnagar_clk_ops = {
|
||||
.prepare = lochnagar_clk_prepare,
|
||||
.unprepare = lochnagar_clk_unprepare,
|
||||
.set_parent = lochnagar_clk_set_parent,
|
||||
.get_parent = lochnagar_clk_get_parent,
|
||||
};
|
||||
|
||||
static int lochnagar_init_parents(struct lochnagar_clk_priv *priv)
|
||||
{
|
||||
struct device_node *np = priv->dev->of_node;
|
||||
int i, j;
|
||||
|
||||
switch (priv->type) {
|
||||
case LOCHNAGAR1:
|
||||
memcpy(priv->lclks, lochnagar1_clks, sizeof(lochnagar1_clks));
|
||||
|
||||
priv->nparents = ARRAY_SIZE(lochnagar1_clk_parents);
|
||||
priv->parents = devm_kmemdup(priv->dev, lochnagar1_clk_parents,
|
||||
sizeof(lochnagar1_clk_parents),
|
||||
GFP_KERNEL);
|
||||
break;
|
||||
case LOCHNAGAR2:
|
||||
memcpy(priv->lclks, lochnagar2_clks, sizeof(lochnagar2_clks));
|
||||
|
||||
priv->nparents = ARRAY_SIZE(lochnagar2_clk_parents);
|
||||
priv->parents = devm_kmemdup(priv->dev, lochnagar2_clk_parents,
|
||||
sizeof(lochnagar2_clk_parents),
|
||||
GFP_KERNEL);
|
||||
break;
|
||||
default:
|
||||
dev_err(priv->dev, "Unknown Lochnagar type: %d\n", priv->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!priv->parents)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < priv->nparents; i++) {
|
||||
j = of_property_match_string(np, "clock-names",
|
||||
priv->parents[i]);
|
||||
if (j >= 0)
|
||||
priv->parents[i] = of_clk_get_parent_name(np, j);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct clk_hw *
|
||||
lochnagar_of_clk_hw_get(struct of_phandle_args *clkspec, void *data)
|
||||
{
|
||||
struct lochnagar_clk_priv *priv = data;
|
||||
unsigned int idx = clkspec->args[0];
|
||||
|
||||
if (idx >= ARRAY_SIZE(priv->lclks)) {
|
||||
dev_err(priv->dev, "Invalid index %u\n", idx);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return &priv->lclks[idx].hw;
|
||||
}
|
||||
|
||||
static int lochnagar_init_clks(struct lochnagar_clk_priv *priv)
|
||||
{
|
||||
struct clk_init_data clk_init = {
|
||||
.ops = &lochnagar_clk_ops,
|
||||
.parent_names = priv->parents,
|
||||
.num_parents = priv->nparents,
|
||||
};
|
||||
struct lochnagar_clk *lclk;
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(priv->lclks); i++) {
|
||||
lclk = &priv->lclks[i];
|
||||
|
||||
if (!lclk->name)
|
||||
continue;
|
||||
|
||||
clk_init.name = lclk->name;
|
||||
|
||||
lclk->priv = priv;
|
||||
lclk->hw.init = &clk_init;
|
||||
|
||||
ret = devm_clk_hw_register(priv->dev, &lclk->hw);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Failed to register %s: %d\n",
|
||||
lclk->name, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = devm_of_clk_add_hw_provider(priv->dev, lochnagar_of_clk_hw_get,
|
||||
priv);
|
||||
if (ret < 0)
|
||||
dev_err(priv->dev, "Failed to register provider: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id lochnagar_of_match[] = {
|
||||
{ .compatible = "cirrus,lochnagar1-clk", .data = (void *)LOCHNAGAR1 },
|
||||
{ .compatible = "cirrus,lochnagar2-clk", .data = (void *)LOCHNAGAR2 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lochnagar_of_match);
|
||||
|
||||
static int lochnagar_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct lochnagar_clk_priv *priv;
|
||||
const struct of_device_id *of_id;
|
||||
int ret;
|
||||
|
||||
of_id = of_match_device(lochnagar_of_match, dev);
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->dev = dev;
|
||||
priv->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
priv->type = (enum lochnagar_type)of_id->data;
|
||||
|
||||
ret = lochnagar_init_parents(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return lochnagar_init_clks(priv);
|
||||
}
|
||||
|
||||
static struct platform_driver lochnagar_clk_driver = {
|
||||
.driver = {
|
||||
.name = "lochnagar-clk",
|
||||
.of_match_table = lochnagar_of_match,
|
||||
},
|
||||
.probe = lochnagar_clk_probe,
|
||||
};
|
||||
module_platform_driver(lochnagar_clk_driver);
|
||||
|
||||
MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
|
||||
MODULE_DESCRIPTION("Clock driver for Cirrus Logic Lochnagar Board");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,663 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018 Socionext Inc.
|
||||
* Copyright (C) 2016 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define M10V_CLKSEL1 0x0
|
||||
#define CLKSEL(n) (((n) - 1) * 4 + M10V_CLKSEL1)
|
||||
|
||||
#define M10V_PLL1 "pll1"
|
||||
#define M10V_PLL1DIV2 "pll1-2"
|
||||
#define M10V_PLL2 "pll2"
|
||||
#define M10V_PLL2DIV2 "pll2-2"
|
||||
#define M10V_PLL6 "pll6"
|
||||
#define M10V_PLL6DIV2 "pll6-2"
|
||||
#define M10V_PLL6DIV3 "pll6-3"
|
||||
#define M10V_PLL7 "pll7"
|
||||
#define M10V_PLL7DIV2 "pll7-2"
|
||||
#define M10V_PLL7DIV5 "pll7-5"
|
||||
#define M10V_PLL9 "pll9"
|
||||
#define M10V_PLL10 "pll10"
|
||||
#define M10V_PLL10DIV2 "pll10-2"
|
||||
#define M10V_PLL11 "pll11"
|
||||
|
||||
#define M10V_SPI_PARENT0 "spi-parent0"
|
||||
#define M10V_SPI_PARENT1 "spi-parent1"
|
||||
#define M10V_SPI_PARENT2 "spi-parent2"
|
||||
#define M10V_UHS1CLK2_PARENT0 "uhs1clk2-parent0"
|
||||
#define M10V_UHS1CLK2_PARENT1 "uhs1clk2-parent1"
|
||||
#define M10V_UHS1CLK2_PARENT2 "uhs1clk2-parent2"
|
||||
#define M10V_UHS1CLK1_PARENT0 "uhs1clk1-parent0"
|
||||
#define M10V_UHS1CLK1_PARENT1 "uhs1clk1-parent1"
|
||||
#define M10V_NFCLK_PARENT0 "nfclk-parent0"
|
||||
#define M10V_NFCLK_PARENT1 "nfclk-parent1"
|
||||
#define M10V_NFCLK_PARENT2 "nfclk-parent2"
|
||||
#define M10V_NFCLK_PARENT3 "nfclk-parent3"
|
||||
#define M10V_NFCLK_PARENT4 "nfclk-parent4"
|
||||
#define M10V_NFCLK_PARENT5 "nfclk-parent5"
|
||||
|
||||
#define M10V_DCHREQ 1
|
||||
#define M10V_UPOLL_RATE 1
|
||||
#define M10V_UTIMEOUT 250
|
||||
|
||||
#define M10V_EMMCCLK_ID 0
|
||||
#define M10V_ACLK_ID 1
|
||||
#define M10V_HCLK_ID 2
|
||||
#define M10V_PCLK_ID 3
|
||||
#define M10V_RCLK_ID 4
|
||||
#define M10V_SPICLK_ID 5
|
||||
#define M10V_NFCLK_ID 6
|
||||
#define M10V_UHS1CLK2_ID 7
|
||||
#define M10V_NUM_CLKS 8
|
||||
|
||||
#define to_m10v_div(_hw) container_of(_hw, struct m10v_clk_divider, hw)
|
||||
|
||||
static struct clk_hw_onecell_data *m10v_clk_data;
|
||||
|
||||
static DEFINE_SPINLOCK(m10v_crglock);
|
||||
|
||||
struct m10v_clk_div_factors {
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
u32 offset;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
const struct clk_div_table *table;
|
||||
unsigned long div_flags;
|
||||
int onecell_idx;
|
||||
};
|
||||
|
||||
struct m10v_clk_div_fixed_data {
|
||||
const char *name;
|
||||
const char *parent_name;
|
||||
u8 div;
|
||||
u8 mult;
|
||||
int onecell_idx;
|
||||
};
|
||||
|
||||
struct m10v_clk_mux_factors {
|
||||
const char *name;
|
||||
const char * const *parent_names;
|
||||
u8 num_parents;
|
||||
u32 offset;
|
||||
u8 shift;
|
||||
u8 mask;
|
||||
u32 *table;
|
||||
unsigned long mux_flags;
|
||||
int onecell_idx;
|
||||
};
|
||||
|
||||
static const struct clk_div_table emmcclk_table[] = {
|
||||
{ .val = 0, .div = 8 },
|
||||
{ .val = 1, .div = 9 },
|
||||
{ .val = 2, .div = 10 },
|
||||
{ .val = 3, .div = 15 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table mclk400_table[] = {
|
||||
{ .val = 1, .div = 2 },
|
||||
{ .val = 3, .div = 4 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table mclk200_table[] = {
|
||||
{ .val = 3, .div = 4 },
|
||||
{ .val = 7, .div = 8 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table aclk400_table[] = {
|
||||
{ .val = 1, .div = 2 },
|
||||
{ .val = 3, .div = 4 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table aclk300_table[] = {
|
||||
{ .val = 0, .div = 2 },
|
||||
{ .val = 1, .div = 3 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table aclk_table[] = {
|
||||
{ .val = 3, .div = 4 },
|
||||
{ .val = 7, .div = 8 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table aclkexs_table[] = {
|
||||
{ .val = 3, .div = 4 },
|
||||
{ .val = 4, .div = 5 },
|
||||
{ .val = 5, .div = 6 },
|
||||
{ .val = 7, .div = 8 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table hclk_table[] = {
|
||||
{ .val = 7, .div = 8 },
|
||||
{ .val = 15, .div = 16 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table hclkbmh_table[] = {
|
||||
{ .val = 3, .div = 4 },
|
||||
{ .val = 7, .div = 8 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table pclk_table[] = {
|
||||
{ .val = 15, .div = 16 },
|
||||
{ .val = 31, .div = 32 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table rclk_table[] = {
|
||||
{ .val = 0, .div = 8 },
|
||||
{ .val = 1, .div = 16 },
|
||||
{ .val = 2, .div = 24 },
|
||||
{ .val = 3, .div = 32 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table uhs1clk0_table[] = {
|
||||
{ .val = 0, .div = 2 },
|
||||
{ .val = 1, .div = 3 },
|
||||
{ .val = 2, .div = 4 },
|
||||
{ .val = 3, .div = 8 },
|
||||
{ .val = 4, .div = 16 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table uhs2clk_table[] = {
|
||||
{ .val = 0, .div = 9 },
|
||||
{ .val = 1, .div = 10 },
|
||||
{ .val = 2, .div = 11 },
|
||||
{ .val = 3, .div = 12 },
|
||||
{ .val = 4, .div = 13 },
|
||||
{ .val = 5, .div = 14 },
|
||||
{ .val = 6, .div = 16 },
|
||||
{ .val = 7, .div = 18 },
|
||||
{ .div = 0 },
|
||||
};
|
||||
|
||||
static u32 spi_mux_table[] = {0, 1, 2};
|
||||
static const char * const spi_mux_names[] = {
|
||||
M10V_SPI_PARENT0, M10V_SPI_PARENT1, M10V_SPI_PARENT2
|
||||
};
|
||||
|
||||
static u32 uhs1clk2_mux_table[] = {2, 3, 4, 8};
|
||||
static const char * const uhs1clk2_mux_names[] = {
|
||||
M10V_UHS1CLK2_PARENT0, M10V_UHS1CLK2_PARENT1,
|
||||
M10V_UHS1CLK2_PARENT2, M10V_PLL6DIV2
|
||||
};
|
||||
|
||||
static u32 uhs1clk1_mux_table[] = {3, 4, 8};
|
||||
static const char * const uhs1clk1_mux_names[] = {
|
||||
M10V_UHS1CLK1_PARENT0, M10V_UHS1CLK1_PARENT1, M10V_PLL6DIV2
|
||||
};
|
||||
|
||||
static u32 nfclk_mux_table[] = {0, 1, 2, 3, 4, 8};
|
||||
static const char * const nfclk_mux_names[] = {
|
||||
M10V_NFCLK_PARENT0, M10V_NFCLK_PARENT1, M10V_NFCLK_PARENT2,
|
||||
M10V_NFCLK_PARENT3, M10V_NFCLK_PARENT4, M10V_NFCLK_PARENT5
|
||||
};
|
||||
|
||||
static const struct m10v_clk_div_fixed_data m10v_pll_fixed_data[] = {
|
||||
{M10V_PLL1, NULL, 1, 40, -1},
|
||||
{M10V_PLL2, NULL, 1, 30, -1},
|
||||
{M10V_PLL6, NULL, 1, 35, -1},
|
||||
{M10V_PLL7, NULL, 1, 40, -1},
|
||||
{M10V_PLL9, NULL, 1, 33, -1},
|
||||
{M10V_PLL10, NULL, 5, 108, -1},
|
||||
{M10V_PLL10DIV2, M10V_PLL10, 2, 1, -1},
|
||||
{M10V_PLL11, NULL, 2, 75, -1},
|
||||
};
|
||||
|
||||
static const struct m10v_clk_div_fixed_data m10v_div_fixed_data[] = {
|
||||
{"usb2", NULL, 2, 1, -1},
|
||||
{"pcisuppclk", NULL, 20, 1, -1},
|
||||
{M10V_PLL1DIV2, M10V_PLL1, 2, 1, -1},
|
||||
{M10V_PLL2DIV2, M10V_PLL2, 2, 1, -1},
|
||||
{M10V_PLL6DIV2, M10V_PLL6, 2, 1, -1},
|
||||
{M10V_PLL6DIV3, M10V_PLL6, 3, 1, -1},
|
||||
{M10V_PLL7DIV2, M10V_PLL7, 2, 1, -1},
|
||||
{M10V_PLL7DIV5, M10V_PLL7, 5, 1, -1},
|
||||
{"ca7wd", M10V_PLL2DIV2, 12, 1, -1},
|
||||
{"pclkca7wd", M10V_PLL1DIV2, 16, 1, -1},
|
||||
{M10V_SPI_PARENT0, M10V_PLL10DIV2, 2, 1, -1},
|
||||
{M10V_SPI_PARENT1, M10V_PLL10DIV2, 4, 1, -1},
|
||||
{M10V_SPI_PARENT2, M10V_PLL7DIV2, 8, 1, -1},
|
||||
{M10V_UHS1CLK2_PARENT0, M10V_PLL7, 4, 1, -1},
|
||||
{M10V_UHS1CLK2_PARENT1, M10V_PLL7, 8, 1, -1},
|
||||
{M10V_UHS1CLK2_PARENT2, M10V_PLL7, 16, 1, -1},
|
||||
{M10V_UHS1CLK1_PARENT0, M10V_PLL7, 8, 1, -1},
|
||||
{M10V_UHS1CLK1_PARENT1, M10V_PLL7, 16, 1, -1},
|
||||
{M10V_NFCLK_PARENT0, M10V_PLL7DIV2, 8, 1, -1},
|
||||
{M10V_NFCLK_PARENT1, M10V_PLL7DIV2, 10, 1, -1},
|
||||
{M10V_NFCLK_PARENT2, M10V_PLL7DIV2, 13, 1, -1},
|
||||
{M10V_NFCLK_PARENT3, M10V_PLL7DIV2, 16, 1, -1},
|
||||
{M10V_NFCLK_PARENT4, M10V_PLL7DIV2, 40, 1, -1},
|
||||
{M10V_NFCLK_PARENT5, M10V_PLL7DIV5, 10, 1, -1},
|
||||
};
|
||||
|
||||
static const struct m10v_clk_div_factors m10v_div_factor_data[] = {
|
||||
{"emmc", M10V_PLL11, CLKSEL(1), 28, 3, emmcclk_table, 0,
|
||||
M10V_EMMCCLK_ID},
|
||||
{"mclk400", M10V_PLL1DIV2, CLKSEL(10), 7, 3, mclk400_table, 0, -1},
|
||||
{"mclk200", M10V_PLL1DIV2, CLKSEL(10), 3, 4, mclk200_table, 0, -1},
|
||||
{"aclk400", M10V_PLL1DIV2, CLKSEL(10), 0, 3, aclk400_table, 0, -1},
|
||||
{"aclk300", M10V_PLL2DIV2, CLKSEL(12), 0, 2, aclk300_table, 0, -1},
|
||||
{"aclk", M10V_PLL1DIV2, CLKSEL(9), 20, 4, aclk_table, 0, M10V_ACLK_ID},
|
||||
{"aclkexs", M10V_PLL1DIV2, CLKSEL(9), 16, 4, aclkexs_table, 0, -1},
|
||||
{"hclk", M10V_PLL1DIV2, CLKSEL(9), 7, 5, hclk_table, 0, M10V_HCLK_ID},
|
||||
{"hclkbmh", M10V_PLL1DIV2, CLKSEL(9), 12, 4, hclkbmh_table, 0, -1},
|
||||
{"pclk", M10V_PLL1DIV2, CLKSEL(9), 0, 7, pclk_table, 0, M10V_PCLK_ID},
|
||||
{"uhs1clk0", M10V_PLL7, CLKSEL(1), 3, 5, uhs1clk0_table, 0, -1},
|
||||
{"uhs2clk", M10V_PLL6DIV3, CLKSEL(1), 18, 4, uhs2clk_table, 0, -1},
|
||||
};
|
||||
|
||||
static const struct m10v_clk_mux_factors m10v_mux_factor_data[] = {
|
||||
{"spi", spi_mux_names, ARRAY_SIZE(spi_mux_names),
|
||||
CLKSEL(8), 3, 7, spi_mux_table, 0, M10V_SPICLK_ID},
|
||||
{"uhs1clk2", uhs1clk2_mux_names, ARRAY_SIZE(uhs1clk2_mux_names),
|
||||
CLKSEL(1), 13, 31, uhs1clk2_mux_table, 0, M10V_UHS1CLK2_ID},
|
||||
{"uhs1clk1", uhs1clk1_mux_names, ARRAY_SIZE(uhs1clk1_mux_names),
|
||||
CLKSEL(1), 8, 31, uhs1clk1_mux_table, 0, -1},
|
||||
{"nfclk", nfclk_mux_names, ARRAY_SIZE(nfclk_mux_names),
|
||||
CLKSEL(1), 22, 127, nfclk_mux_table, 0, M10V_NFCLK_ID},
|
||||
};
|
||||
|
||||
static u8 m10v_mux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(hw);
|
||||
u32 val;
|
||||
|
||||
val = readl(mux->reg) >> mux->shift;
|
||||
val &= mux->mask;
|
||||
|
||||
return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
|
||||
}
|
||||
|
||||
static int m10v_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_mux *mux = to_clk_mux(hw);
|
||||
u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
|
||||
unsigned long flags = 0;
|
||||
u32 reg;
|
||||
u32 write_en = BIT(fls(mux->mask) - 1);
|
||||
|
||||
if (mux->lock)
|
||||
spin_lock_irqsave(mux->lock, flags);
|
||||
else
|
||||
__acquire(mux->lock);
|
||||
|
||||
reg = readl(mux->reg);
|
||||
reg &= ~(mux->mask << mux->shift);
|
||||
|
||||
val = (val | write_en) << mux->shift;
|
||||
reg |= val;
|
||||
writel(reg, mux->reg);
|
||||
|
||||
if (mux->lock)
|
||||
spin_unlock_irqrestore(mux->lock, flags);
|
||||
else
|
||||
__release(mux->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops m10v_mux_ops = {
|
||||
.get_parent = m10v_mux_get_parent,
|
||||
.set_parent = m10v_mux_set_parent,
|
||||
.determine_rate = __clk_mux_determine_rate,
|
||||
};
|
||||
|
||||
static struct clk_hw *m10v_clk_hw_register_mux(struct device *dev,
|
||||
const char *name, const char * const *parent_names,
|
||||
u8 num_parents, unsigned long flags, void __iomem *reg,
|
||||
u8 shift, u32 mask, u8 clk_mux_flags, u32 *table,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
struct clk_mux *mux;
|
||||
struct clk_hw *hw;
|
||||
struct clk_init_data init;
|
||||
int ret;
|
||||
|
||||
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
|
||||
if (!mux)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &m10v_mux_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
|
||||
mux->reg = reg;
|
||||
mux->shift = shift;
|
||||
mux->mask = mask;
|
||||
mux->flags = clk_mux_flags;
|
||||
mux->lock = lock;
|
||||
mux->table = table;
|
||||
mux->hw.init = &init;
|
||||
|
||||
hw = &mux->hw;
|
||||
ret = clk_hw_register(dev, hw);
|
||||
if (ret) {
|
||||
kfree(mux);
|
||||
hw = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return hw;
|
||||
|
||||
}
|
||||
|
||||
struct m10v_clk_divider {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
u8 shift;
|
||||
u8 width;
|
||||
u8 flags;
|
||||
const struct clk_div_table *table;
|
||||
spinlock_t *lock;
|
||||
void __iomem *write_valid_reg;
|
||||
};
|
||||
|
||||
static unsigned long m10v_clk_divider_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct m10v_clk_divider *divider = to_m10v_div(hw);
|
||||
unsigned int val;
|
||||
|
||||
val = readl(divider->reg) >> divider->shift;
|
||||
val &= clk_div_mask(divider->width);
|
||||
|
||||
return divider_recalc_rate(hw, parent_rate, val, divider->table,
|
||||
divider->flags, divider->width);
|
||||
}
|
||||
|
||||
static long m10v_clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
struct m10v_clk_divider *divider = to_m10v_div(hw);
|
||||
|
||||
/* if read only, just return current value */
|
||||
if (divider->flags & CLK_DIVIDER_READ_ONLY) {
|
||||
u32 val;
|
||||
|
||||
val = readl(divider->reg) >> divider->shift;
|
||||
val &= clk_div_mask(divider->width);
|
||||
|
||||
return divider_ro_round_rate(hw, rate, prate, divider->table,
|
||||
divider->width, divider->flags,
|
||||
val);
|
||||
}
|
||||
|
||||
return divider_round_rate(hw, rate, prate, divider->table,
|
||||
divider->width, divider->flags);
|
||||
}
|
||||
|
||||
static int m10v_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct m10v_clk_divider *divider = to_m10v_div(hw);
|
||||
int value;
|
||||
unsigned long flags = 0;
|
||||
u32 val;
|
||||
u32 write_en = BIT(divider->width - 1);
|
||||
|
||||
value = divider_get_val(rate, parent_rate, divider->table,
|
||||
divider->width, divider->flags);
|
||||
if (value < 0)
|
||||
return value;
|
||||
|
||||
if (divider->lock)
|
||||
spin_lock_irqsave(divider->lock, flags);
|
||||
else
|
||||
__acquire(divider->lock);
|
||||
|
||||
val = readl(divider->reg);
|
||||
val &= ~(clk_div_mask(divider->width) << divider->shift);
|
||||
|
||||
val |= ((u32)value | write_en) << divider->shift;
|
||||
writel(val, divider->reg);
|
||||
|
||||
if (divider->write_valid_reg) {
|
||||
writel(M10V_DCHREQ, divider->write_valid_reg);
|
||||
if (readl_poll_timeout(divider->write_valid_reg, val,
|
||||
!val, M10V_UPOLL_RATE, M10V_UTIMEOUT))
|
||||
pr_err("%s:%s couldn't stabilize\n",
|
||||
__func__, divider->hw.init->name);
|
||||
}
|
||||
|
||||
if (divider->lock)
|
||||
spin_unlock_irqrestore(divider->lock, flags);
|
||||
else
|
||||
__release(divider->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops m10v_clk_divider_ops = {
|
||||
.recalc_rate = m10v_clk_divider_recalc_rate,
|
||||
.round_rate = m10v_clk_divider_round_rate,
|
||||
.set_rate = m10v_clk_divider_set_rate,
|
||||
};
|
||||
|
||||
static struct clk_hw *m10v_clk_hw_register_divider(struct device *dev,
|
||||
const char *name, const char *parent_name, unsigned long flags,
|
||||
void __iomem *reg, u8 shift, u8 width,
|
||||
u8 clk_divider_flags, const struct clk_div_table *table,
|
||||
spinlock_t *lock, void __iomem *write_valid_reg)
|
||||
{
|
||||
struct m10v_clk_divider *div;
|
||||
struct clk_hw *hw;
|
||||
struct clk_init_data init;
|
||||
int ret;
|
||||
|
||||
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
||||
if (!div)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = name;
|
||||
init.ops = &m10v_clk_divider_ops;
|
||||
init.flags = flags;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
div->reg = reg;
|
||||
div->shift = shift;
|
||||
div->width = width;
|
||||
div->flags = clk_divider_flags;
|
||||
div->lock = lock;
|
||||
div->hw.init = &init;
|
||||
div->table = table;
|
||||
div->write_valid_reg = write_valid_reg;
|
||||
|
||||
/* register the clock */
|
||||
hw = &div->hw;
|
||||
ret = clk_hw_register(dev, hw);
|
||||
if (ret) {
|
||||
kfree(div);
|
||||
hw = ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return hw;
|
||||
}
|
||||
|
||||
static void m10v_reg_div_pre(const struct m10v_clk_div_factors *factors,
|
||||
struct clk_hw_onecell_data *clk_data,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct clk_hw *hw;
|
||||
void __iomem *write_valid_reg;
|
||||
|
||||
/*
|
||||
* The registers on CLKSEL(9) or CLKSEL(10) need additional
|
||||
* writing to become valid.
|
||||
*/
|
||||
if ((factors->offset == CLKSEL(9)) || (factors->offset == CLKSEL(10)))
|
||||
write_valid_reg = base + CLKSEL(11);
|
||||
else
|
||||
write_valid_reg = NULL;
|
||||
|
||||
hw = m10v_clk_hw_register_divider(NULL, factors->name,
|
||||
factors->parent_name,
|
||||
CLK_SET_RATE_PARENT,
|
||||
base + factors->offset,
|
||||
factors->shift,
|
||||
factors->width, factors->div_flags,
|
||||
factors->table,
|
||||
&m10v_crglock, write_valid_reg);
|
||||
|
||||
if (factors->onecell_idx >= 0)
|
||||
clk_data->hws[factors->onecell_idx] = hw;
|
||||
}
|
||||
|
||||
static void m10v_reg_fixed_pre(const struct m10v_clk_div_fixed_data *factors,
|
||||
struct clk_hw_onecell_data *clk_data,
|
||||
const char *parent_name)
|
||||
{
|
||||
struct clk_hw *hw;
|
||||
const char *pn = factors->parent_name ?
|
||||
factors->parent_name : parent_name;
|
||||
|
||||
hw = clk_hw_register_fixed_factor(NULL, factors->name, pn, 0,
|
||||
factors->mult, factors->div);
|
||||
|
||||
if (factors->onecell_idx >= 0)
|
||||
clk_data->hws[factors->onecell_idx] = hw;
|
||||
}
|
||||
|
||||
static void m10v_reg_mux_pre(const struct m10v_clk_mux_factors *factors,
|
||||
struct clk_hw_onecell_data *clk_data,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct clk_hw *hw;
|
||||
|
||||
hw = m10v_clk_hw_register_mux(NULL, factors->name,
|
||||
factors->parent_names,
|
||||
factors->num_parents,
|
||||
CLK_SET_RATE_PARENT,
|
||||
base + factors->offset, factors->shift,
|
||||
factors->mask, factors->mux_flags,
|
||||
factors->table, &m10v_crglock);
|
||||
|
||||
if (factors->onecell_idx >= 0)
|
||||
clk_data->hws[factors->onecell_idx] = hw;
|
||||
}
|
||||
|
||||
static int m10v_clk_probe(struct platform_device *pdev)
|
||||
{
|
||||
int id;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
void __iomem *base;
|
||||
const char *parent_name;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
|
||||
for (id = 0; id < ARRAY_SIZE(m10v_div_factor_data); ++id)
|
||||
m10v_reg_div_pre(&m10v_div_factor_data[id],
|
||||
m10v_clk_data, base);
|
||||
|
||||
for (id = 0; id < ARRAY_SIZE(m10v_div_fixed_data); ++id)
|
||||
m10v_reg_fixed_pre(&m10v_div_fixed_data[id],
|
||||
m10v_clk_data, parent_name);
|
||||
|
||||
for (id = 0; id < ARRAY_SIZE(m10v_mux_factor_data); ++id)
|
||||
m10v_reg_mux_pre(&m10v_mux_factor_data[id],
|
||||
m10v_clk_data, base);
|
||||
|
||||
for (id = 0; id < M10V_NUM_CLKS; id++) {
|
||||
if (IS_ERR(m10v_clk_data->hws[id]))
|
||||
return PTR_ERR(m10v_clk_data->hws[id]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id m10v_clk_dt_ids[] = {
|
||||
{ .compatible = "socionext,milbeaut-m10v-ccu", },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver m10v_clk_driver = {
|
||||
.probe = m10v_clk_probe,
|
||||
.driver = {
|
||||
.name = "m10v-ccu",
|
||||
.of_match_table = m10v_clk_dt_ids,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver(m10v_clk_driver);
|
||||
|
||||
static void __init m10v_cc_init(struct device_node *np)
|
||||
{
|
||||
int id;
|
||||
void __iomem *base;
|
||||
const char *parent_name;
|
||||
struct clk_hw *hw;
|
||||
|
||||
m10v_clk_data = kzalloc(struct_size(m10v_clk_data, hws,
|
||||
M10V_NUM_CLKS),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!m10v_clk_data)
|
||||
return;
|
||||
|
||||
base = of_iomap(np, 0);
|
||||
if (!base) {
|
||||
kfree(m10v_clk_data);
|
||||
return;
|
||||
}
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (!parent_name) {
|
||||
kfree(m10v_clk_data);
|
||||
iounmap(base);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This way all clocks fetched before the platform device probes,
|
||||
* except those we assign here for early use, will be deferred.
|
||||
*/
|
||||
for (id = 0; id < M10V_NUM_CLKS; id++)
|
||||
m10v_clk_data->hws[id] = ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
/*
|
||||
* PLLs are set by bootloader so this driver registers them as the
|
||||
* fixed factor.
|
||||
*/
|
||||
for (id = 0; id < ARRAY_SIZE(m10v_pll_fixed_data); ++id)
|
||||
m10v_reg_fixed_pre(&m10v_pll_fixed_data[id],
|
||||
m10v_clk_data, parent_name);
|
||||
|
||||
/*
|
||||
* timer consumes "rclk" so it needs to register here.
|
||||
*/
|
||||
hw = m10v_clk_hw_register_divider(NULL, "rclk", M10V_PLL10DIV2, 0,
|
||||
base + CLKSEL(1), 0, 3, 0, rclk_table,
|
||||
&m10v_crglock, NULL);
|
||||
m10v_clk_data->hws[M10V_RCLK_ID] = hw;
|
||||
|
||||
m10v_clk_data->num = M10V_NUM_CLKS;
|
||||
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, m10v_clk_data);
|
||||
}
|
||||
CLK_OF_DECLARE_DRIVER(m10v_cc, "socionext,milbeaut-m10v-ccu", m10v_cc_init);
|
|
@ -11,6 +11,22 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
static inline u32 clk_mult_readl(struct clk_multiplier *mult)
|
||||
{
|
||||
if (mult->flags & CLK_MULTIPLIER_BIG_ENDIAN)
|
||||
return ioread32be(mult->reg);
|
||||
|
||||
return readl(mult->reg);
|
||||
}
|
||||
|
||||
static inline void clk_mult_writel(struct clk_multiplier *mult, u32 val)
|
||||
{
|
||||
if (mult->flags & CLK_MULTIPLIER_BIG_ENDIAN)
|
||||
iowrite32be(val, mult->reg);
|
||||
else
|
||||
writel(val, mult->reg);
|
||||
}
|
||||
|
||||
static unsigned long __get_mult(struct clk_multiplier *mult,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
|
@ -27,7 +43,7 @@ static unsigned long clk_multiplier_recalc_rate(struct clk_hw *hw,
|
|||
struct clk_multiplier *mult = to_clk_multiplier(hw);
|
||||
unsigned long val;
|
||||
|
||||
val = clk_readl(mult->reg) >> mult->shift;
|
||||
val = clk_mult_readl(mult) >> mult->shift;
|
||||
val &= GENMASK(mult->width - 1, 0);
|
||||
|
||||
if (!val && mult->flags & CLK_MULTIPLIER_ZERO_BYPASS)
|
||||
|
@ -118,10 +134,10 @@ static int clk_multiplier_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
else
|
||||
__acquire(mult->lock);
|
||||
|
||||
val = clk_readl(mult->reg);
|
||||
val = clk_mult_readl(mult);
|
||||
val &= ~GENMASK(mult->width + mult->shift - 1, mult->shift);
|
||||
val |= factor << mult->shift;
|
||||
clk_writel(val, mult->reg);
|
||||
clk_mult_writel(mult, val);
|
||||
|
||||
if (mult->lock)
|
||||
spin_unlock_irqrestore(mult->lock, flags);
|
||||
|
|
|
@ -23,6 +23,22 @@
|
|||
* parent - parent is adjustable through clk_set_parent
|
||||
*/
|
||||
|
||||
static inline u32 clk_mux_readl(struct clk_mux *mux)
|
||||
{
|
||||
if (mux->flags & CLK_MUX_BIG_ENDIAN)
|
||||
return ioread32be(mux->reg);
|
||||
|
||||
return readl(mux->reg);
|
||||
}
|
||||
|
||||
static inline void clk_mux_writel(struct clk_mux *mux, u32 val)
|
||||
{
|
||||
if (mux->flags & CLK_MUX_BIG_ENDIAN)
|
||||
iowrite32be(val, mux->reg);
|
||||
else
|
||||
writel(val, mux->reg);
|
||||
}
|
||||
|
||||
int clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags,
|
||||
unsigned int val)
|
||||
{
|
||||
|
@ -73,7 +89,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
|
|||
struct clk_mux *mux = to_clk_mux(hw);
|
||||
u32 val;
|
||||
|
||||
val = clk_readl(mux->reg) >> mux->shift;
|
||||
val = clk_mux_readl(mux) >> mux->shift;
|
||||
val &= mux->mask;
|
||||
|
||||
return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
|
||||
|
@ -94,12 +110,12 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
|
|||
if (mux->flags & CLK_MUX_HIWORD_MASK) {
|
||||
reg = mux->mask << (mux->shift + 16);
|
||||
} else {
|
||||
reg = clk_readl(mux->reg);
|
||||
reg = clk_mux_readl(mux);
|
||||
reg &= ~(mux->mask << mux->shift);
|
||||
}
|
||||
val = val << mux->shift;
|
||||
reg |= val;
|
||||
clk_writel(reg, mux->reg);
|
||||
clk_mux_writel(mux, reg);
|
||||
|
||||
if (mux->lock)
|
||||
spin_unlock_irqrestore(mux->lock, flags);
|
||||
|
@ -159,7 +175,7 @@ struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name,
|
|||
init.ops = &clk_mux_ro_ops;
|
||||
else
|
||||
init.ops = &clk_mux_ops;
|
||||
init.flags = flags | CLK_IS_BASIC;
|
||||
init.flags = flags;
|
||||
init.parent_names = parent_names;
|
||||
init.num_parents = num_parents;
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ static int clk_pwm_probe(struct platform_device *pdev)
|
|||
|
||||
init.name = clk_name;
|
||||
init.ops = &clk_pwm_ops;
|
||||
init.flags = CLK_IS_BASIC;
|
||||
init.flags = 0;
|
||||
init.num_parents = 0;
|
||||
|
||||
clk_pwm->pwm = pwm;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#define CGA_PLL4 4 /* only on clockgen-1.0, which lacks CGB */
|
||||
#define CGB_PLL1 4
|
||||
#define CGB_PLL2 5
|
||||
#define MAX_PLL_DIV 16
|
||||
|
||||
struct clockgen_pll_div {
|
||||
struct clk *clk;
|
||||
|
@ -41,7 +42,7 @@ struct clockgen_pll_div {
|
|||
};
|
||||
|
||||
struct clockgen_pll {
|
||||
struct clockgen_pll_div div[8];
|
||||
struct clockgen_pll_div div[MAX_PLL_DIV];
|
||||
};
|
||||
|
||||
#define CLKSEL_VALID 1
|
||||
|
@ -79,7 +80,7 @@ struct clockgen_chipinfo {
|
|||
const struct clockgen_muxinfo *cmux_groups[2];
|
||||
const struct clockgen_muxinfo *hwaccel[NUM_HWACCEL];
|
||||
void (*init_periph)(struct clockgen *cg);
|
||||
int cmux_to_group[NUM_CMUX]; /* -1 terminates if fewer than NUM_CMUX */
|
||||
int cmux_to_group[NUM_CMUX + 1]; /* array should be -1 terminated */
|
||||
u32 pll_mask; /* 1 << n bit set if PLL n is valid */
|
||||
u32 flags; /* CG_xxx */
|
||||
};
|
||||
|
@ -245,6 +246,58 @@ static const struct clockgen_muxinfo clockgen2_cmux_cgb = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct clockgen_muxinfo ls1028a_hwa1 = {
|
||||
{
|
||||
{ CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 },
|
||||
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
|
||||
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
|
||||
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV3 },
|
||||
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV4 },
|
||||
{},
|
||||
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
|
||||
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
|
||||
},
|
||||
};
|
||||
|
||||
static const struct clockgen_muxinfo ls1028a_hwa2 = {
|
||||
{
|
||||
{ CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 },
|
||||
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
|
||||
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
|
||||
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
|
||||
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV4 },
|
||||
{},
|
||||
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
|
||||
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV3 },
|
||||
},
|
||||
};
|
||||
|
||||
static const struct clockgen_muxinfo ls1028a_hwa3 = {
|
||||
{
|
||||
{ CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 },
|
||||
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV1 },
|
||||
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
|
||||
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV3 },
|
||||
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV4 },
|
||||
{},
|
||||
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
|
||||
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
|
||||
},
|
||||
};
|
||||
|
||||
static const struct clockgen_muxinfo ls1028a_hwa4 = {
|
||||
{
|
||||
{ CLKSEL_VALID, PLATFORM_PLL, PLL_DIV1 },
|
||||
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV1 },
|
||||
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV2 },
|
||||
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV3 },
|
||||
{ CLKSEL_VALID, CGA_PLL2, PLL_DIV4 },
|
||||
{},
|
||||
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV2 },
|
||||
{ CLKSEL_VALID, CGA_PLL1, PLL_DIV3 },
|
||||
},
|
||||
};
|
||||
|
||||
static const struct clockgen_muxinfo ls1043a_hwa1 = {
|
||||
{
|
||||
{},
|
||||
|
@ -507,6 +560,21 @@ static const struct clockgen_chipinfo chipinfo[] = {
|
|||
},
|
||||
.pll_mask = 0x03,
|
||||
},
|
||||
{
|
||||
.compat = "fsl,ls1028a-clockgen",
|
||||
.cmux_groups = {
|
||||
&clockgen2_cmux_cga12
|
||||
},
|
||||
.hwaccel = {
|
||||
&ls1028a_hwa1, &ls1028a_hwa2,
|
||||
&ls1028a_hwa3, &ls1028a_hwa4
|
||||
},
|
||||
.cmux_to_group = {
|
||||
0, 0, 0, 0, -1
|
||||
},
|
||||
.pll_mask = 0x07,
|
||||
.flags = CG_VER3 | CG_LITTLE_ENDIAN,
|
||||
},
|
||||
{
|
||||
.compat = "fsl,ls1043a-clockgen",
|
||||
.init_periph = t2080_init_periph,
|
||||
|
@ -601,7 +669,7 @@ static const struct clockgen_chipinfo chipinfo[] = {
|
|||
&p4080_cmux_grp1, &p4080_cmux_grp2
|
||||
},
|
||||
.cmux_to_group = {
|
||||
0, 0, 0, 0, 1, 1, 1, 1
|
||||
0, 0, 0, 0, 1, 1, 1, 1, -1
|
||||
},
|
||||
.pll_mask = 0x1f,
|
||||
},
|
||||
|
@ -1128,7 +1196,7 @@ static void __init create_one_pll(struct clockgen *cg, int idx)
|
|||
int ret;
|
||||
|
||||
/*
|
||||
* For platform PLL, there are 8 divider clocks.
|
||||
* For platform PLL, there are MAX_PLL_DIV divider clocks.
|
||||
* For core PLL, there are 4 divider clocks at most.
|
||||
*/
|
||||
if (idx != PLATFORM_PLL && i >= 4)
|
||||
|
@ -1423,6 +1491,7 @@ CLK_OF_DECLARE(qoriq_clockgen_b4420, "fsl,b4420-clockgen", clockgen_init);
|
|||
CLK_OF_DECLARE(qoriq_clockgen_b4860, "fsl,b4860-clockgen", clockgen_init);
|
||||
CLK_OF_DECLARE(qoriq_clockgen_ls1012a, "fsl,ls1012a-clockgen", clockgen_init);
|
||||
CLK_OF_DECLARE(qoriq_clockgen_ls1021a, "fsl,ls1021a-clockgen", clockgen_init);
|
||||
CLK_OF_DECLARE(qoriq_clockgen_ls1028a, "fsl,ls1028a-clockgen", clockgen_init);
|
||||
CLK_OF_DECLARE(qoriq_clockgen_ls1043a, "fsl,ls1043a-clockgen", clockgen_init);
|
||||
CLK_OF_DECLARE(qoriq_clockgen_ls1046a, "fsl,ls1046a-clockgen", clockgen_init);
|
||||
CLK_OF_DECLARE(qoriq_clockgen_ls1088a, "fsl,ls1088a-clockgen", clockgen_init);
|
||||
|
|
|
@ -300,6 +300,85 @@ static const struct stm32f4_gate_data stm32f746_gates[] __initconst = {
|
|||
{ STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" },
|
||||
};
|
||||
|
||||
static const struct stm32f4_gate_data stm32f769_gates[] __initconst = {
|
||||
{ STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 20, "dtcmram", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" },
|
||||
|
||||
{ STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB2ENR, 1, "jpeg", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" },
|
||||
{ STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" },
|
||||
{ STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" },
|
||||
|
||||
{ STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div",
|
||||
CLK_IGNORE_UNUSED },
|
||||
{ STM32F4_RCC_AHB3ENR, 1, "qspi", "ahb_div",
|
||||
CLK_IGNORE_UNUSED },
|
||||
|
||||
{ STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 10, "rtcapb", "apb1_mul" },
|
||||
{ STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 13, "can3", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 16, "spdifrx", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 27, "cec", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" },
|
||||
{ STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" },
|
||||
|
||||
{ STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" },
|
||||
{ STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" },
|
||||
{ STM32F4_RCC_APB2ENR, 7, "sdmmc2", "sdmux2" },
|
||||
{ STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 11, "sdmmc1", "sdmux1" },
|
||||
{ STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" },
|
||||
{ STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" },
|
||||
{ STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" },
|
||||
{ STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 23, "sai2", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" },
|
||||
{ STM32F4_RCC_APB2ENR, 30, "mdio", "apb2_div" },
|
||||
};
|
||||
|
||||
/*
|
||||
* This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx
|
||||
* have gate bits associated with them. Its combined hweight is 71.
|
||||
|
@ -318,6 +397,10 @@ static const u64 stm32f746_gate_map[MAX_GATE_MAP] = { 0x000000f17ef417ffull,
|
|||
0x0000000000000003ull,
|
||||
0x04f77f833e01c9ffull };
|
||||
|
||||
static const u64 stm32f769_gate_map[MAX_GATE_MAP] = { 0x000000f37ef417ffull,
|
||||
0x0000000000000003ull,
|
||||
0x44F77F833E01EDFFull };
|
||||
|
||||
static const u64 *stm32f4_gate_map;
|
||||
|
||||
static struct clk_hw **clks;
|
||||
|
@ -1048,6 +1131,10 @@ static const char *rtc_parents[4] = {
|
|||
"no-clock", "lse", "lsi", "hse-rtc"
|
||||
};
|
||||
|
||||
static const char *pll_src = "pll-src";
|
||||
|
||||
static const char *pllsrc_parent[2] = { "hsi", NULL };
|
||||
|
||||
static const char *dsi_parent[2] = { NULL, "pll-r" };
|
||||
|
||||
static const char *lcd_parent[1] = { "pllsai-r-div" };
|
||||
|
@ -1072,6 +1159,9 @@ static const char *uart_parents2[4] = { "apb1_div", "sys", "hsi", "lse" };
|
|||
|
||||
static const char *i2c_parents[4] = { "apb1_div", "sys", "hsi", "no-clock" };
|
||||
|
||||
static const char * const dfsdm1_src[] = { "apb2_div", "sys" };
|
||||
static const char * const adsfdm1_parent[] = { "sai1_clk", "sai2_clk" };
|
||||
|
||||
struct stm32_aux_clk {
|
||||
int idx;
|
||||
const char *name;
|
||||
|
@ -1313,6 +1403,177 @@ static const struct stm32_aux_clk stm32f746_aux_clk[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct stm32_aux_clk stm32f769_aux_clk[] = {
|
||||
{
|
||||
CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent),
|
||||
NO_MUX, 0, 0,
|
||||
STM32F4_RCC_APB2ENR, 26,
|
||||
CLK_SET_RATE_PARENT
|
||||
},
|
||||
{
|
||||
CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents),
|
||||
STM32F4_RCC_CFGR, 23, 1,
|
||||
NO_GATE, 0,
|
||||
CLK_SET_RATE_PARENT
|
||||
},
|
||||
{
|
||||
CLK_SAI1, "sai1_clk", sai_parents, ARRAY_SIZE(sai_parents),
|
||||
STM32F4_RCC_DCKCFGR, 20, 3,
|
||||
STM32F4_RCC_APB2ENR, 22,
|
||||
CLK_SET_RATE_PARENT
|
||||
},
|
||||
{
|
||||
CLK_SAI2, "sai2_clk", sai_parents, ARRAY_SIZE(sai_parents),
|
||||
STM32F4_RCC_DCKCFGR, 22, 3,
|
||||
STM32F4_RCC_APB2ENR, 23,
|
||||
CLK_SET_RATE_PARENT
|
||||
},
|
||||
{
|
||||
NO_IDX, "pll48", pll48_parents, ARRAY_SIZE(pll48_parents),
|
||||
STM32F7_RCC_DCKCFGR2, 27, 1,
|
||||
NO_GATE, 0,
|
||||
0
|
||||
},
|
||||
{
|
||||
NO_IDX, "sdmux1", sdmux_parents, ARRAY_SIZE(sdmux_parents),
|
||||
STM32F7_RCC_DCKCFGR2, 28, 1,
|
||||
NO_GATE, 0,
|
||||
0
|
||||
},
|
||||
{
|
||||
NO_IDX, "sdmux2", sdmux_parents, ARRAY_SIZE(sdmux_parents),
|
||||
STM32F7_RCC_DCKCFGR2, 29, 1,
|
||||
NO_GATE, 0,
|
||||
0
|
||||
},
|
||||
{
|
||||
CLK_HDMI_CEC, "hdmi-cec",
|
||||
hdmi_parents, ARRAY_SIZE(hdmi_parents),
|
||||
STM32F7_RCC_DCKCFGR2, 26, 1,
|
||||
NO_GATE, 0,
|
||||
0
|
||||
},
|
||||
{
|
||||
CLK_SPDIF, "spdif-rx",
|
||||
spdif_parent, ARRAY_SIZE(spdif_parent),
|
||||
STM32F7_RCC_DCKCFGR2, 22, 3,
|
||||
STM32F4_RCC_APB2ENR, 23,
|
||||
CLK_SET_RATE_PARENT
|
||||
},
|
||||
{
|
||||
CLK_USART1, "usart1",
|
||||
uart_parents1, ARRAY_SIZE(uart_parents1),
|
||||
STM32F7_RCC_DCKCFGR2, 0, 3,
|
||||
STM32F4_RCC_APB2ENR, 4,
|
||||
CLK_SET_RATE_PARENT,
|
||||
},
|
||||
{
|
||||
CLK_USART2, "usart2",
|
||||
uart_parents2, ARRAY_SIZE(uart_parents1),
|
||||
STM32F7_RCC_DCKCFGR2, 2, 3,
|
||||
STM32F4_RCC_APB1ENR, 17,
|
||||
CLK_SET_RATE_PARENT,
|
||||
},
|
||||
{
|
||||
CLK_USART3, "usart3",
|
||||
uart_parents2, ARRAY_SIZE(uart_parents1),
|
||||
STM32F7_RCC_DCKCFGR2, 4, 3,
|
||||
STM32F4_RCC_APB1ENR, 18,
|
||||
CLK_SET_RATE_PARENT,
|
||||
},
|
||||
{
|
||||
CLK_UART4, "uart4",
|
||||
uart_parents2, ARRAY_SIZE(uart_parents1),
|
||||
STM32F7_RCC_DCKCFGR2, 6, 3,
|
||||
STM32F4_RCC_APB1ENR, 19,
|
||||
CLK_SET_RATE_PARENT,
|
||||
},
|
||||
{
|
||||
CLK_UART5, "uart5",
|
||||
uart_parents2, ARRAY_SIZE(uart_parents1),
|
||||
STM32F7_RCC_DCKCFGR2, 8, 3,
|
||||
STM32F4_RCC_APB1ENR, 20,
|
||||
CLK_SET_RATE_PARENT,
|
||||
},
|
||||
{
|
||||
CLK_USART6, "usart6",
|
||||
uart_parents1, ARRAY_SIZE(uart_parents1),
|
||||
STM32F7_RCC_DCKCFGR2, 10, 3,
|
||||
STM32F4_RCC_APB2ENR, 5,
|
||||
CLK_SET_RATE_PARENT,
|
||||
},
|
||||
{
|
||||
CLK_UART7, "uart7",
|
||||
uart_parents2, ARRAY_SIZE(uart_parents1),
|
||||
STM32F7_RCC_DCKCFGR2, 12, 3,
|
||||
STM32F4_RCC_APB1ENR, 30,
|
||||
CLK_SET_RATE_PARENT,
|
||||
},
|
||||
{
|
||||
CLK_UART8, "uart8",
|
||||
uart_parents2, ARRAY_SIZE(uart_parents1),
|
||||
STM32F7_RCC_DCKCFGR2, 14, 3,
|
||||
STM32F4_RCC_APB1ENR, 31,
|
||||
CLK_SET_RATE_PARENT,
|
||||
},
|
||||
{
|
||||
CLK_I2C1, "i2c1",
|
||||
i2c_parents, ARRAY_SIZE(i2c_parents),
|
||||
STM32F7_RCC_DCKCFGR2, 16, 3,
|
||||
STM32F4_RCC_APB1ENR, 21,
|
||||
CLK_SET_RATE_PARENT,
|
||||
},
|
||||
{
|
||||
CLK_I2C2, "i2c2",
|
||||
i2c_parents, ARRAY_SIZE(i2c_parents),
|
||||
STM32F7_RCC_DCKCFGR2, 18, 3,
|
||||
STM32F4_RCC_APB1ENR, 22,
|
||||
CLK_SET_RATE_PARENT,
|
||||
},
|
||||
{
|
||||
CLK_I2C3, "i2c3",
|
||||
i2c_parents, ARRAY_SIZE(i2c_parents),
|
||||
STM32F7_RCC_DCKCFGR2, 20, 3,
|
||||
STM32F4_RCC_APB1ENR, 23,
|
||||
CLK_SET_RATE_PARENT,
|
||||
},
|
||||
{
|
||||
CLK_I2C4, "i2c4",
|
||||
i2c_parents, ARRAY_SIZE(i2c_parents),
|
||||
STM32F7_RCC_DCKCFGR2, 22, 3,
|
||||
STM32F4_RCC_APB1ENR, 24,
|
||||
CLK_SET_RATE_PARENT,
|
||||
},
|
||||
{
|
||||
CLK_LPTIMER, "lptim1",
|
||||
lptim_parent, ARRAY_SIZE(lptim_parent),
|
||||
STM32F7_RCC_DCKCFGR2, 24, 3,
|
||||
STM32F4_RCC_APB1ENR, 9,
|
||||
CLK_SET_RATE_PARENT
|
||||
},
|
||||
{
|
||||
CLK_F769_DSI, "dsi",
|
||||
dsi_parent, ARRAY_SIZE(dsi_parent),
|
||||
STM32F7_RCC_DCKCFGR2, 0, 1,
|
||||
STM32F4_RCC_APB2ENR, 27,
|
||||
CLK_SET_RATE_PARENT
|
||||
},
|
||||
{
|
||||
CLK_DFSDM1, "dfsdm1",
|
||||
dfsdm1_src, ARRAY_SIZE(dfsdm1_src),
|
||||
STM32F4_RCC_DCKCFGR, 25, 1,
|
||||
STM32F4_RCC_APB2ENR, 29,
|
||||
CLK_SET_RATE_PARENT
|
||||
},
|
||||
{
|
||||
CLK_ADFSDM1, "adfsdm1",
|
||||
adsfdm1_parent, ARRAY_SIZE(adsfdm1_parent),
|
||||
STM32F4_RCC_DCKCFGR, 26, 1,
|
||||
STM32F4_RCC_APB2ENR, 29,
|
||||
CLK_SET_RATE_PARENT
|
||||
},
|
||||
};
|
||||
|
||||
static const struct stm32f4_clk_data stm32f429_clk_data = {
|
||||
.end_primary = END_PRIMARY_CLK,
|
||||
.gates_data = stm32f429_gates,
|
||||
|
@ -1343,6 +1604,16 @@ static const struct stm32f4_clk_data stm32f746_clk_data = {
|
|||
.aux_clk_num = ARRAY_SIZE(stm32f746_aux_clk),
|
||||
};
|
||||
|
||||
static const struct stm32f4_clk_data stm32f769_clk_data = {
|
||||
.end_primary = END_PRIMARY_CLK_F7,
|
||||
.gates_data = stm32f769_gates,
|
||||
.gates_map = stm32f769_gate_map,
|
||||
.gates_num = ARRAY_SIZE(stm32f769_gates),
|
||||
.pll_data = stm32f469_pll,
|
||||
.aux_clk = stm32f769_aux_clk,
|
||||
.aux_clk_num = ARRAY_SIZE(stm32f769_aux_clk),
|
||||
};
|
||||
|
||||
static const struct of_device_id stm32f4_of_match[] = {
|
||||
{
|
||||
.compatible = "st,stm32f42xx-rcc",
|
||||
|
@ -1356,6 +1627,10 @@ static const struct of_device_id stm32f4_of_match[] = {
|
|||
.compatible = "st,stm32f746-rcc",
|
||||
.data = &stm32f746_clk_data
|
||||
},
|
||||
{
|
||||
.compatible = "st,stm32f769-rcc",
|
||||
.data = &stm32f769_clk_data
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -1427,9 +1702,8 @@ static void __init stm32f4_rcc_init(struct device_node *np)
|
|||
int n;
|
||||
const struct of_device_id *match;
|
||||
const struct stm32f4_clk_data *data;
|
||||
unsigned long pllcfgr;
|
||||
const char *pllsrc;
|
||||
unsigned long pllm;
|
||||
struct clk_hw *pll_src_hw;
|
||||
|
||||
base = of_iomap(np, 0);
|
||||
if (!base) {
|
||||
|
@ -1460,21 +1734,33 @@ static void __init stm32f4_rcc_init(struct device_node *np)
|
|||
|
||||
hse_clk = of_clk_get_parent_name(np, 0);
|
||||
dsi_parent[0] = hse_clk;
|
||||
pllsrc_parent[1] = hse_clk;
|
||||
|
||||
i2s_in_clk = of_clk_get_parent_name(np, 1);
|
||||
|
||||
i2s_parents[1] = i2s_in_clk;
|
||||
sai_parents[2] = i2s_in_clk;
|
||||
|
||||
if (of_device_is_compatible(np, "st,stm32f769-rcc")) {
|
||||
clk_hw_register_gate(NULL, "dfsdm1_apb", "apb2_div", 0,
|
||||
base + STM32F4_RCC_APB2ENR, 29,
|
||||
CLK_IGNORE_UNUSED, &stm32f4_clk_lock);
|
||||
dsi_parent[0] = pll_src;
|
||||
sai_parents[3] = pll_src;
|
||||
}
|
||||
|
||||
clks[CLK_HSI] = clk_hw_register_fixed_rate_with_accuracy(NULL, "hsi",
|
||||
NULL, 0, 16000000, 160000);
|
||||
|
||||
pllcfgr = readl(base + STM32F4_RCC_PLLCFGR);
|
||||
pllsrc = pllcfgr & BIT(22) ? hse_clk : "hsi";
|
||||
pllm = pllcfgr & 0x3f;
|
||||
pll_src_hw = clk_hw_register_mux(NULL, pll_src, pllsrc_parent,
|
||||
ARRAY_SIZE(pllsrc_parent), 0,
|
||||
base + STM32F4_RCC_PLLCFGR, 22, 1, 0,
|
||||
&stm32f4_clk_lock);
|
||||
|
||||
clk_hw_register_fixed_factor(NULL, "vco_in", pllsrc,
|
||||
0, 1, pllm);
|
||||
pllm = readl(base + STM32F4_RCC_PLLCFGR) & 0x3f;
|
||||
|
||||
clk_hw_register_fixed_factor(NULL, "vco_in", pll_src,
|
||||
0, 1, pllm);
|
||||
|
||||
stm32f4_rcc_register_pll("vco_in", &data->pll_data[0],
|
||||
&stm32f4_clk_lock);
|
||||
|
@ -1612,12 +1898,16 @@ static void __init stm32f4_rcc_init(struct device_node *np)
|
|||
clks[aux_clk->idx] = hw;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(np, "st,stm32f746-rcc"))
|
||||
if (of_device_is_compatible(np, "st,stm32f746-rcc")) {
|
||||
|
||||
clk_hw_register_fixed_factor(NULL, "hsi_div488", "hsi", 0,
|
||||
1, 488);
|
||||
|
||||
clks[CLK_PLL_SRC] = pll_src_hw;
|
||||
}
|
||||
|
||||
of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL);
|
||||
|
||||
return;
|
||||
fail:
|
||||
kfree(clks);
|
||||
|
@ -1626,3 +1916,4 @@ fail:
|
|||
CLK_OF_DECLARE_DRIVER(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init);
|
||||
CLK_OF_DECLARE_DRIVER(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init);
|
||||
CLK_OF_DECLARE_DRIVER(stm32f746_rcc, "st,stm32f746-rcc", stm32f4_rcc_init);
|
||||
CLK_OF_DECLARE_DRIVER(stm32f769_rcc, "st,stm32f769-rcc", stm32f4_rcc_init);
|
||||
|
|
|
@ -1402,6 +1402,7 @@ enum {
|
|||
G_CRYP1,
|
||||
G_HASH1,
|
||||
G_BKPSRAM,
|
||||
G_DDRPERFM,
|
||||
|
||||
G_LAST
|
||||
};
|
||||
|
@ -1488,6 +1489,7 @@ static struct stm32_gate_cfg per_gate_cfg[G_LAST] = {
|
|||
K_GATE(G_STGENRO, RCC_APB4ENSETR, 20, 0),
|
||||
K_MGATE(G_USBPHY, RCC_APB4ENSETR, 16, 0),
|
||||
K_GATE(G_IWDG2, RCC_APB4ENSETR, 15, 0),
|
||||
K_GATE(G_DDRPERFM, RCC_APB4ENSETR, 8, 0),
|
||||
K_MGATE(G_DSI, RCC_APB4ENSETR, 4, 0),
|
||||
K_MGATE(G_LTDC, RCC_APB4ENSETR, 0, 0),
|
||||
|
||||
|
@ -1899,6 +1901,7 @@ static const struct clock_config stm32mp1_clock_cfg[] = {
|
|||
PCLK(CRC1, "crc1", "ck_axi", 0, G_CRC1),
|
||||
PCLK(USBH, "usbh", "ck_axi", 0, G_USBH),
|
||||
PCLK(ETHSTP, "ethstp", "ck_axi", 0, G_ETHSTP),
|
||||
PCLK(DDRPERFM, "ddrperfm", "pclk4", 0, G_DDRPERFM),
|
||||
|
||||
/* Kernel clocks */
|
||||
KCLK(SDMMC1_K, "sdmmc1_k", sdmmc12_src, 0, G_SDMMC1, M_SDMMC12),
|
||||
|
|
|
@ -262,7 +262,7 @@ static unsigned long xgene_clk_pmd_recalc_rate(struct clk_hw *hw,
|
|||
else
|
||||
__acquire(fd->lock);
|
||||
|
||||
val = clk_readl(fd->reg);
|
||||
val = readl(fd->reg);
|
||||
|
||||
if (fd->lock)
|
||||
spin_unlock_irqrestore(fd->lock, flags);
|
||||
|
@ -333,10 +333,10 @@ static int xgene_clk_pmd_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
else
|
||||
__acquire(fd->lock);
|
||||
|
||||
val = clk_readl(fd->reg);
|
||||
val = readl(fd->reg);
|
||||
val &= ~fd->mask;
|
||||
val |= (scale << fd->shift);
|
||||
clk_writel(val, fd->reg);
|
||||
writel(val, fd->reg);
|
||||
|
||||
if (fd->lock)
|
||||
spin_unlock_irqrestore(fd->lock, flags);
|
||||
|
|
|
@ -39,15 +39,23 @@ static LIST_HEAD(clk_notifier_list);
|
|||
|
||||
/*** private data structures ***/
|
||||
|
||||
struct clk_parent_map {
|
||||
const struct clk_hw *hw;
|
||||
struct clk_core *core;
|
||||
const char *fw_name;
|
||||
const char *name;
|
||||
int index;
|
||||
};
|
||||
|
||||
struct clk_core {
|
||||
const char *name;
|
||||
const struct clk_ops *ops;
|
||||
struct clk_hw *hw;
|
||||
struct module *owner;
|
||||
struct device *dev;
|
||||
struct device_node *of_node;
|
||||
struct clk_core *parent;
|
||||
const char **parent_names;
|
||||
struct clk_core **parents;
|
||||
struct clk_parent_map *parents;
|
||||
u8 num_parents;
|
||||
u8 new_parent_index;
|
||||
unsigned long rate;
|
||||
|
@ -316,17 +324,102 @@ static struct clk_core *clk_core_lookup(const char *name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_core_get - Find the clk_core parent of a clk
|
||||
* @core: clk to find parent of
|
||||
* @p_index: parent index to search for
|
||||
*
|
||||
* This is the preferred method for clk providers to find the parent of a
|
||||
* clk when that parent is external to the clk controller. The parent_names
|
||||
* array is indexed and treated as a local name matching a string in the device
|
||||
* node's 'clock-names' property or as the 'con_id' matching the device's
|
||||
* dev_name() in a clk_lookup. This allows clk providers to use their own
|
||||
* namespace instead of looking for a globally unique parent string.
|
||||
*
|
||||
* For example the following DT snippet would allow a clock registered by the
|
||||
* clock-controller@c001 that has a clk_init_data::parent_data array
|
||||
* with 'xtal' in the 'name' member to find the clock provided by the
|
||||
* clock-controller@f00abcd without needing to get the globally unique name of
|
||||
* the xtal clk.
|
||||
*
|
||||
* parent: clock-controller@f00abcd {
|
||||
* reg = <0xf00abcd 0xabcd>;
|
||||
* #clock-cells = <0>;
|
||||
* };
|
||||
*
|
||||
* clock-controller@c001 {
|
||||
* reg = <0xc001 0xf00d>;
|
||||
* clocks = <&parent>;
|
||||
* clock-names = "xtal";
|
||||
* #clock-cells = <1>;
|
||||
* };
|
||||
*
|
||||
* Returns: -ENOENT when the provider can't be found or the clk doesn't
|
||||
* exist in the provider. -EINVAL when the name can't be found. NULL when the
|
||||
* provider knows about the clk but it isn't provided on this system.
|
||||
* A valid clk_core pointer when the clk can be found in the provider.
|
||||
*/
|
||||
static struct clk_core *clk_core_get(struct clk_core *core, u8 p_index)
|
||||
{
|
||||
const char *name = core->parents[p_index].fw_name;
|
||||
int index = core->parents[p_index].index;
|
||||
struct clk_hw *hw = ERR_PTR(-ENOENT);
|
||||
struct device *dev = core->dev;
|
||||
const char *dev_id = dev ? dev_name(dev) : NULL;
|
||||
struct device_node *np = core->of_node;
|
||||
|
||||
if (np && index >= 0)
|
||||
hw = of_clk_get_hw(np, index, name);
|
||||
|
||||
/*
|
||||
* If the DT search above couldn't find the provider or the provider
|
||||
* didn't know about this clk, fallback to looking up via clkdev based
|
||||
* clk_lookups
|
||||
*/
|
||||
if (PTR_ERR(hw) == -ENOENT && name)
|
||||
hw = clk_find_hw(dev_id, name);
|
||||
|
||||
if (IS_ERR(hw))
|
||||
return ERR_CAST(hw);
|
||||
|
||||
return hw->core;
|
||||
}
|
||||
|
||||
static void clk_core_fill_parent_index(struct clk_core *core, u8 index)
|
||||
{
|
||||
struct clk_parent_map *entry = &core->parents[index];
|
||||
struct clk_core *parent = ERR_PTR(-ENOENT);
|
||||
|
||||
if (entry->hw) {
|
||||
parent = entry->hw->core;
|
||||
/*
|
||||
* We have a direct reference but it isn't registered yet?
|
||||
* Orphan it and let clk_reparent() update the orphan status
|
||||
* when the parent is registered.
|
||||
*/
|
||||
if (!parent)
|
||||
parent = ERR_PTR(-EPROBE_DEFER);
|
||||
} else {
|
||||
parent = clk_core_get(core, index);
|
||||
if (IS_ERR(parent) && PTR_ERR(parent) == -ENOENT)
|
||||
parent = clk_core_lookup(entry->name);
|
||||
}
|
||||
|
||||
/* Only cache it if it's not an error */
|
||||
if (!IS_ERR(parent))
|
||||
entry->core = parent;
|
||||
}
|
||||
|
||||
static struct clk_core *clk_core_get_parent_by_index(struct clk_core *core,
|
||||
u8 index)
|
||||
{
|
||||
if (!core || index >= core->num_parents)
|
||||
if (!core || index >= core->num_parents || !core->parents)
|
||||
return NULL;
|
||||
|
||||
if (!core->parents[index])
|
||||
core->parents[index] =
|
||||
clk_core_lookup(core->parent_names[index]);
|
||||
if (!core->parents[index].core)
|
||||
clk_core_fill_parent_index(core, index);
|
||||
|
||||
return core->parents[index];
|
||||
return core->parents[index].core;
|
||||
}
|
||||
|
||||
struct clk_hw *
|
||||
|
@ -347,23 +440,18 @@ unsigned int __clk_get_enable_count(struct clk *clk)
|
|||
|
||||
static unsigned long clk_core_get_rate_nolock(struct clk_core *core)
|
||||
{
|
||||
unsigned long ret;
|
||||
if (!core)
|
||||
return 0;
|
||||
|
||||
if (!core) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
if (!core->num_parents || core->parent)
|
||||
return core->rate;
|
||||
|
||||
ret = core->rate;
|
||||
|
||||
if (!core->num_parents)
|
||||
goto out;
|
||||
|
||||
if (!core->parent)
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
/*
|
||||
* Clk must have a parent because num_parents > 0 but the parent isn't
|
||||
* known yet. Best to return 0 as the rate of this clk until we can
|
||||
* properly recalc the rate based on the parent's rate.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long clk_hw_get_rate(const struct clk_hw *hw)
|
||||
|
@ -524,9 +612,15 @@ void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate,
|
|||
EXPORT_SYMBOL_GPL(clk_hw_set_rate_range);
|
||||
|
||||
/*
|
||||
* __clk_mux_determine_rate - clk_ops::determine_rate implementation for a mux type clk
|
||||
* @hw: mux type clk to determine rate on
|
||||
* @req: rate request, also used to return preferred parent and frequencies
|
||||
*
|
||||
* Helper for finding best parent to provide a given frequency. This can be used
|
||||
* directly as a determine_rate callback (e.g. for a mux), or from a more
|
||||
* complex clock that may combine a mux with other operations.
|
||||
*
|
||||
* Returns: 0 on success, -EERROR value on error
|
||||
*/
|
||||
int __clk_mux_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
|
@ -1519,20 +1613,37 @@ static int clk_fetch_parent_index(struct clk_core *core,
|
|||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < core->num_parents; i++) {
|
||||
if (core->parents[i] == parent)
|
||||
/* Found it first try! */
|
||||
if (core->parents[i].core == parent)
|
||||
return i;
|
||||
|
||||
if (core->parents[i])
|
||||
/* Something else is here, so keep looking */
|
||||
if (core->parents[i].core)
|
||||
continue;
|
||||
|
||||
/* Fallback to comparing globally unique names */
|
||||
if (!strcmp(parent->name, core->parent_names[i])) {
|
||||
core->parents[i] = parent;
|
||||
return i;
|
||||
/* Maybe core hasn't been cached but the hw is all we know? */
|
||||
if (core->parents[i].hw) {
|
||||
if (core->parents[i].hw == parent->hw)
|
||||
break;
|
||||
|
||||
/* Didn't match, but we're expecting a clk_hw */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Maybe it hasn't been cached (clk_set_parent() path) */
|
||||
if (parent == clk_core_get(core, i))
|
||||
break;
|
||||
|
||||
/* Fallback to comparing globally unique names */
|
||||
if (!strcmp(parent->name, core->parents[i].name))
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
if (i == core->num_parents)
|
||||
return -EINVAL;
|
||||
|
||||
core->parents[i].core = parent;
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2293,6 +2404,7 @@ void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent)
|
|||
bool clk_has_parent(struct clk *clk, struct clk *parent)
|
||||
{
|
||||
struct clk_core *core, *parent_core;
|
||||
int i;
|
||||
|
||||
/* NULL clocks should be nops, so return success if either is NULL. */
|
||||
if (!clk || !parent)
|
||||
|
@ -2305,8 +2417,11 @@ bool clk_has_parent(struct clk *clk, struct clk *parent)
|
|||
if (core->parent == parent_core)
|
||||
return true;
|
||||
|
||||
return match_string(core->parent_names, core->num_parents,
|
||||
parent_core->name) >= 0;
|
||||
for (i = 0; i < core->num_parents; i++)
|
||||
if (!strcmp(core->parents[i].name, parent_core->name))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_has_parent);
|
||||
|
||||
|
@ -2850,7 +2965,6 @@ static const struct {
|
|||
ENTRY(CLK_SET_PARENT_GATE),
|
||||
ENTRY(CLK_SET_RATE_PARENT),
|
||||
ENTRY(CLK_IGNORE_UNUSED),
|
||||
ENTRY(CLK_IS_BASIC),
|
||||
ENTRY(CLK_GET_RATE_NOCACHE),
|
||||
ENTRY(CLK_SET_RATE_NO_REPARENT),
|
||||
ENTRY(CLK_GET_ACCURACY_NOCACHE),
|
||||
|
@ -2889,9 +3003,9 @@ static int possible_parents_show(struct seq_file *s, void *data)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < core->num_parents - 1; i++)
|
||||
seq_printf(s, "%s ", core->parent_names[i]);
|
||||
seq_printf(s, "%s ", core->parents[i].name);
|
||||
|
||||
seq_printf(s, "%s\n", core->parent_names[i]);
|
||||
seq_printf(s, "%s\n", core->parents[i].name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -3025,7 +3139,7 @@ static inline void clk_debug_unregister(struct clk_core *core)
|
|||
*/
|
||||
static int __clk_core_init(struct clk_core *core)
|
||||
{
|
||||
int i, ret;
|
||||
int ret;
|
||||
struct clk_core *orphan;
|
||||
struct hlist_node *tmp2;
|
||||
unsigned long rate;
|
||||
|
@ -3079,12 +3193,6 @@ static int __clk_core_init(struct clk_core *core)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* throw a WARN if any entries in parent_names are NULL */
|
||||
for (i = 0; i < core->num_parents; i++)
|
||||
WARN(!core->parent_names[i],
|
||||
"%s: invalid NULL in %s's .parent_names\n",
|
||||
__func__, core->name);
|
||||
|
||||
core->parent = __clk_init_parent(core);
|
||||
|
||||
/*
|
||||
|
@ -3313,20 +3421,104 @@ struct clk *clk_hw_create_clk(struct device *dev, struct clk_hw *hw,
|
|||
return clk;
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_register - allocate a new clock, register it and return an opaque cookie
|
||||
* @dev: device that is registering this clock
|
||||
* @hw: link to hardware-specific clock data
|
||||
*
|
||||
* clk_register is the primary interface for populating the clock tree with new
|
||||
* clock nodes. It returns a pointer to the newly allocated struct clk which
|
||||
* cannot be dereferenced by driver code but may be used in conjunction with the
|
||||
* rest of the clock API. In the event of an error clk_register will return an
|
||||
* error code; drivers must test for an error code after calling clk_register.
|
||||
*/
|
||||
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
|
||||
static int clk_cpy_name(const char **dst_p, const char *src, bool must_exist)
|
||||
{
|
||||
int i, ret;
|
||||
const char *dst;
|
||||
|
||||
if (!src) {
|
||||
if (must_exist)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*dst_p = dst = kstrdup_const(src, GFP_KERNEL);
|
||||
if (!dst)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_core_populate_parent_map(struct clk_core *core)
|
||||
{
|
||||
const struct clk_init_data *init = core->hw->init;
|
||||
u8 num_parents = init->num_parents;
|
||||
const char * const *parent_names = init->parent_names;
|
||||
const struct clk_hw **parent_hws = init->parent_hws;
|
||||
const struct clk_parent_data *parent_data = init->parent_data;
|
||||
int i, ret = 0;
|
||||
struct clk_parent_map *parents, *parent;
|
||||
|
||||
if (!num_parents)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Avoid unnecessary string look-ups of clk_core's possible parents by
|
||||
* having a cache of names/clk_hw pointers to clk_core pointers.
|
||||
*/
|
||||
parents = kcalloc(num_parents, sizeof(*parents), GFP_KERNEL);
|
||||
core->parents = parents;
|
||||
if (!parents)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Copy everything over because it might be __initdata */
|
||||
for (i = 0, parent = parents; i < num_parents; i++, parent++) {
|
||||
parent->index = -1;
|
||||
if (parent_names) {
|
||||
/* throw a WARN if any entries are NULL */
|
||||
WARN(!parent_names[i],
|
||||
"%s: invalid NULL in %s's .parent_names\n",
|
||||
__func__, core->name);
|
||||
ret = clk_cpy_name(&parent->name, parent_names[i],
|
||||
true);
|
||||
} else if (parent_data) {
|
||||
parent->hw = parent_data[i].hw;
|
||||
parent->index = parent_data[i].index;
|
||||
ret = clk_cpy_name(&parent->fw_name,
|
||||
parent_data[i].fw_name, false);
|
||||
if (!ret)
|
||||
ret = clk_cpy_name(&parent->name,
|
||||
parent_data[i].name,
|
||||
false);
|
||||
} else if (parent_hws) {
|
||||
parent->hw = parent_hws[i];
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
WARN(1, "Must specify parents if num_parents > 0\n");
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
do {
|
||||
kfree_const(parents[i].name);
|
||||
kfree_const(parents[i].fw_name);
|
||||
} while (--i >= 0);
|
||||
kfree(parents);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clk_core_free_parent_map(struct clk_core *core)
|
||||
{
|
||||
int i = core->num_parents;
|
||||
|
||||
if (!core->num_parents)
|
||||
return;
|
||||
|
||||
while (--i >= 0) {
|
||||
kfree_const(core->parents[i].name);
|
||||
kfree_const(core->parents[i].fw_name);
|
||||
}
|
||||
|
||||
kfree(core->parents);
|
||||
}
|
||||
|
||||
static struct clk *
|
||||
__clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
|
||||
{
|
||||
int ret;
|
||||
struct clk_core *core;
|
||||
|
||||
core = kzalloc(sizeof(*core), GFP_KERNEL);
|
||||
|
@ -3350,6 +3542,7 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
|
|||
if (dev && pm_runtime_enabled(dev))
|
||||
core->rpm_enabled = true;
|
||||
core->dev = dev;
|
||||
core->of_node = np;
|
||||
if (dev && dev->driver)
|
||||
core->owner = dev->driver->owner;
|
||||
core->hw = hw;
|
||||
|
@ -3359,33 +3552,9 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
|
|||
core->max_rate = ULONG_MAX;
|
||||
hw->core = core;
|
||||
|
||||
/* allocate local copy in case parent_names is __initdata */
|
||||
core->parent_names = kcalloc(core->num_parents, sizeof(char *),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!core->parent_names) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_parent_names;
|
||||
}
|
||||
|
||||
|
||||
/* copy each string name in case parent_names is __initdata */
|
||||
for (i = 0; i < core->num_parents; i++) {
|
||||
core->parent_names[i] = kstrdup_const(hw->init->parent_names[i],
|
||||
GFP_KERNEL);
|
||||
if (!core->parent_names[i]) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_parent_names_copy;
|
||||
}
|
||||
}
|
||||
|
||||
/* avoid unnecessary string look-ups of clk_core's possible parents. */
|
||||
core->parents = kcalloc(core->num_parents, sizeof(*core->parents),
|
||||
GFP_KERNEL);
|
||||
if (!core->parents) {
|
||||
ret = -ENOMEM;
|
||||
ret = clk_core_populate_parent_map(core);
|
||||
if (ret)
|
||||
goto fail_parents;
|
||||
};
|
||||
|
||||
INIT_HLIST_HEAD(&core->clks);
|
||||
|
||||
|
@ -3396,7 +3565,7 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
|
|||
hw->clk = alloc_clk(core, NULL, NULL);
|
||||
if (IS_ERR(hw->clk)) {
|
||||
ret = PTR_ERR(hw->clk);
|
||||
goto fail_parents;
|
||||
goto fail_create_clk;
|
||||
}
|
||||
|
||||
clk_core_link_consumer(hw->core, hw->clk);
|
||||
|
@ -3412,13 +3581,9 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
|
|||
free_clk(hw->clk);
|
||||
hw->clk = NULL;
|
||||
|
||||
fail_create_clk:
|
||||
clk_core_free_parent_map(core);
|
||||
fail_parents:
|
||||
kfree(core->parents);
|
||||
fail_parent_names_copy:
|
||||
while (--i >= 0)
|
||||
kfree_const(core->parent_names[i]);
|
||||
kfree(core->parent_names);
|
||||
fail_parent_names:
|
||||
fail_ops:
|
||||
kfree_const(core->name);
|
||||
fail_name:
|
||||
|
@ -3426,6 +3591,24 @@ fail_name:
|
|||
fail_out:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* clk_register - allocate a new clock, register it and return an opaque cookie
|
||||
* @dev: device that is registering this clock
|
||||
* @hw: link to hardware-specific clock data
|
||||
*
|
||||
* clk_register is the *deprecated* interface for populating the clock tree with
|
||||
* new clock nodes. Use clk_hw_register() instead.
|
||||
*
|
||||
* Returns: a pointer to the newly allocated struct clk which
|
||||
* cannot be dereferenced by driver code but may be used in conjunction with the
|
||||
* rest of the clock API. In the event of an error clk_register will return an
|
||||
* error code; drivers must test for an error code after calling clk_register.
|
||||
*/
|
||||
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
|
||||
{
|
||||
return __clk_register(dev, dev_of_node(dev), hw);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_register);
|
||||
|
||||
/**
|
||||
|
@ -3440,23 +3623,35 @@ EXPORT_SYMBOL_GPL(clk_register);
|
|||
*/
|
||||
int clk_hw_register(struct device *dev, struct clk_hw *hw)
|
||||
{
|
||||
return PTR_ERR_OR_ZERO(clk_register(dev, hw));
|
||||
return PTR_ERR_OR_ZERO(__clk_register(dev, dev_of_node(dev), hw));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clk_hw_register);
|
||||
|
||||
/*
|
||||
* of_clk_hw_register - register a clk_hw and return an error code
|
||||
* @node: device_node of device that is registering this clock
|
||||
* @hw: link to hardware-specific clock data
|
||||
*
|
||||
* of_clk_hw_register() is the primary interface for populating the clock tree
|
||||
* with new clock nodes when a struct device is not available, but a struct
|
||||
* device_node is. It returns an integer equal to zero indicating success or
|
||||
* less than zero indicating failure. Drivers must test for an error code after
|
||||
* calling of_clk_hw_register().
|
||||
*/
|
||||
int of_clk_hw_register(struct device_node *node, struct clk_hw *hw)
|
||||
{
|
||||
return PTR_ERR_OR_ZERO(__clk_register(NULL, node, hw));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_clk_hw_register);
|
||||
|
||||
/* Free memory allocated for a clock. */
|
||||
static void __clk_release(struct kref *ref)
|
||||
{
|
||||
struct clk_core *core = container_of(ref, struct clk_core, ref);
|
||||
int i = core->num_parents;
|
||||
|
||||
lockdep_assert_held(&prepare_lock);
|
||||
|
||||
kfree(core->parents);
|
||||
while (--i >= 0)
|
||||
kfree_const(core->parent_names[i]);
|
||||
|
||||
kfree(core->parent_names);
|
||||
clk_core_free_parent_map(core);
|
||||
kfree_const(core->name);
|
||||
kfree(core);
|
||||
}
|
||||
|
@ -3575,9 +3770,10 @@ static void devm_clk_hw_release(struct device *dev, void *res)
|
|||
* @dev: device that is registering this clock
|
||||
* @hw: link to hardware-specific clock data
|
||||
*
|
||||
* Managed clk_register(). Clocks returned from this function are
|
||||
* automatically clk_unregister()ed on driver detach. See clk_register() for
|
||||
* more information.
|
||||
* Managed clk_register(). This function is *deprecated*, use devm_clk_hw_register() instead.
|
||||
*
|
||||
* Clocks returned from this function are automatically clk_unregister()ed on
|
||||
* driver detach. See clk_register() for more information.
|
||||
*/
|
||||
struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw)
|
||||
{
|
||||
|
@ -3895,6 +4091,8 @@ EXPORT_SYMBOL_GPL(of_clk_hw_onecell_get);
|
|||
* @np: Device node pointer associated with clock provider
|
||||
* @clk_src_get: callback for decoding clock
|
||||
* @data: context pointer for @clk_src_get callback.
|
||||
*
|
||||
* This function is *deprecated*. Use of_clk_add_hw_provider() instead.
|
||||
*/
|
||||
int of_clk_add_provider(struct device_node *np,
|
||||
struct clk *(*clk_src_get)(struct of_phandle_args *clkspec,
|
||||
|
|
|
@ -19,6 +19,8 @@ static inline struct clk_hw *of_clk_get_hw(struct device_node *np,
|
|||
}
|
||||
#endif
|
||||
|
||||
struct clk_hw *clk_find_hw(const char *dev_id, const char *con_id);
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
struct clk *clk_hw_create_clk(struct device *dev, struct clk_hw *hw,
|
||||
const char *dev_id, const char *con_id);
|
||||
|
|
|
@ -72,25 +72,26 @@ static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
|
|||
return cl;
|
||||
}
|
||||
|
||||
struct clk_hw *clk_find_hw(const char *dev_id, const char *con_id)
|
||||
{
|
||||
struct clk_lookup *cl;
|
||||
struct clk_hw *hw = ERR_PTR(-ENOENT);
|
||||
|
||||
mutex_lock(&clocks_mutex);
|
||||
cl = clk_find(dev_id, con_id);
|
||||
if (cl)
|
||||
hw = cl->clk_hw;
|
||||
mutex_unlock(&clocks_mutex);
|
||||
|
||||
return hw;
|
||||
}
|
||||
|
||||
static struct clk *__clk_get_sys(struct device *dev, const char *dev_id,
|
||||
const char *con_id)
|
||||
{
|
||||
struct clk_lookup *cl;
|
||||
struct clk *clk = NULL;
|
||||
struct clk_hw *hw = clk_find_hw(dev_id, con_id);
|
||||
|
||||
mutex_lock(&clocks_mutex);
|
||||
|
||||
cl = clk_find(dev_id, con_id);
|
||||
if (!cl)
|
||||
goto out;
|
||||
|
||||
clk = clk_hw_create_clk(dev, cl->clk_hw, dev_id, con_id);
|
||||
if (IS_ERR(clk))
|
||||
cl = NULL;
|
||||
out:
|
||||
mutex_unlock(&clocks_mutex);
|
||||
|
||||
return cl ? clk : ERR_PTR(-ENOENT);
|
||||
return clk_hw_create_clk(dev, hw, dev_id, con_id);
|
||||
}
|
||||
|
||||
struct clk *clk_get_sys(const char *dev_id, const char *con_id)
|
||||
|
|
|
@ -160,10 +160,8 @@ static int __init da8xx_cfgchip_register_div4p5(struct device *dev,
|
|||
struct da8xx_cfgchip_gate_clk *gate;
|
||||
|
||||
gate = da8xx_cfgchip_gate_clk_register(dev, &da8xx_div4p5ena_info, regmap);
|
||||
if (IS_ERR(gate))
|
||||
return PTR_ERR(gate);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(gate);
|
||||
}
|
||||
|
||||
static int __init
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Clock driver for TI Davinci PSC controllers
|
||||
*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Clock driver for TI Davinci PSC controllers
|
||||
*
|
||||
|
|
|
@ -163,8 +163,12 @@ static const struct hisi_gate_clock hi3660_crgctrl_gate_sep_clks[] = {
|
|||
"clk_isp_snclk_mux", CLK_SET_RATE_PARENT, 0x50, 17, 0, },
|
||||
{ HI3660_CLK_GATE_ISP_SNCLK2, "clk_gate_isp_snclk2",
|
||||
"clk_isp_snclk_mux", CLK_SET_RATE_PARENT, 0x50, 18, 0, },
|
||||
/*
|
||||
* clk_gate_ufs_subsys is a system bus clock, mark it as critical
|
||||
* clock and keep it on for system suspend and resume.
|
||||
*/
|
||||
{ HI3660_CLK_GATE_UFS_SUBSYS, "clk_gate_ufs_subsys", "clk_div_sysbus",
|
||||
CLK_SET_RATE_PARENT, 0x50, 21, 0, },
|
||||
CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, 0x50, 21, 0, },
|
||||
{ HI3660_PCLK_GATE_DSI0, "pclk_gate_dsi0", "clk_div_cfgbus",
|
||||
CLK_SET_RATE_PARENT, 0x50, 28, 0, },
|
||||
{ HI3660_PCLK_GATE_DSI1, "pclk_gate_dsi1", "clk_div_cfgbus",
|
||||
|
|
|
@ -75,10 +75,10 @@ static int hisi_clk_set_phase(struct clk_hw *hw, int degrees)
|
|||
|
||||
spin_lock_irqsave(phase->lock, flags);
|
||||
|
||||
val = clk_readl(phase->reg);
|
||||
val = readl(phase->reg);
|
||||
val &= ~phase->mask;
|
||||
val |= regval << phase->shift;
|
||||
clk_writel(val, phase->reg);
|
||||
writel(val, phase->reg);
|
||||
|
||||
spin_unlock_irqrestore(phase->lock, flags);
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ obj-$(CONFIG_SOC_IMX25) += clk-imx25.o
|
|||
obj-$(CONFIG_SOC_IMX27) += clk-imx27.o
|
||||
obj-$(CONFIG_SOC_IMX31) += clk-imx31.o
|
||||
obj-$(CONFIG_SOC_IMX35) += clk-imx35.o
|
||||
obj-$(CONFIG_SOC_IMX5) += clk-imx51-imx53.o
|
||||
obj-$(CONFIG_SOC_IMX5) += clk-imx5.o
|
||||
obj-$(CONFIG_SOC_IMX6Q) += clk-imx6q.o
|
||||
obj-$(CONFIG_SOC_IMX6SL) += clk-imx6sl.o
|
||||
obj-$(CONFIG_SOC_IMX6SLL) += clk-imx6sll.o
|
||||
|
|
|
@ -29,7 +29,7 @@ static unsigned long clk_divider_gate_recalc_rate_ro(struct clk_hw *hw,
|
|||
struct clk_divider *div = to_clk_divider(hw);
|
||||
unsigned int val;
|
||||
|
||||
val = clk_readl(div->reg) >> div->shift;
|
||||
val = readl(div->reg) >> div->shift;
|
||||
val &= clk_div_mask(div->width);
|
||||
if (!val)
|
||||
return 0;
|
||||
|
@ -51,7 +51,7 @@ static unsigned long clk_divider_gate_recalc_rate(struct clk_hw *hw,
|
|||
if (!clk_hw_is_enabled(hw)) {
|
||||
val = div_gate->cached_val;
|
||||
} else {
|
||||
val = clk_readl(div->reg) >> div->shift;
|
||||
val = readl(div->reg) >> div->shift;
|
||||
val &= clk_div_mask(div->width);
|
||||
}
|
||||
|
||||
|
@ -87,10 +87,10 @@ static int clk_divider_gate_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
spin_lock_irqsave(div->lock, flags);
|
||||
|
||||
if (clk_hw_is_enabled(hw)) {
|
||||
val = clk_readl(div->reg);
|
||||
val = readl(div->reg);
|
||||
val &= ~(clk_div_mask(div->width) << div->shift);
|
||||
val |= (u32)value << div->shift;
|
||||
clk_writel(val, div->reg);
|
||||
writel(val, div->reg);
|
||||
} else {
|
||||
div_gate->cached_val = value;
|
||||
}
|
||||
|
@ -114,9 +114,9 @@ static int clk_divider_enable(struct clk_hw *hw)
|
|||
|
||||
spin_lock_irqsave(div->lock, flags);
|
||||
/* restore div val */
|
||||
val = clk_readl(div->reg);
|
||||
val = readl(div->reg);
|
||||
val |= div_gate->cached_val << div->shift;
|
||||
clk_writel(val, div->reg);
|
||||
writel(val, div->reg);
|
||||
|
||||
spin_unlock_irqrestore(div->lock, flags);
|
||||
|
||||
|
@ -133,10 +133,10 @@ static void clk_divider_disable(struct clk_hw *hw)
|
|||
spin_lock_irqsave(div->lock, flags);
|
||||
|
||||
/* store the current div val */
|
||||
val = clk_readl(div->reg) >> div->shift;
|
||||
val = readl(div->reg) >> div->shift;
|
||||
val &= clk_div_mask(div->width);
|
||||
div_gate->cached_val = val;
|
||||
clk_writel(0, div->reg);
|
||||
writel(0, div->reg);
|
||||
|
||||
spin_unlock_irqrestore(div->lock, flags);
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ static int clk_divider_is_enabled(struct clk_hw *hw)
|
|||
struct clk_divider *div = to_clk_divider(hw);
|
||||
u32 val;
|
||||
|
||||
val = clk_readl(div->reg) >> div->shift;
|
||||
val = readl(div->reg) >> div->shift;
|
||||
val &= clk_div_mask(div->width);
|
||||
|
||||
return val ? 1 : 0;
|
||||
|
@ -206,7 +206,7 @@ struct clk_hw *imx_clk_divider_gate(const char *name, const char *parent_name,
|
|||
div_gate->divider.hw.init = &init;
|
||||
div_gate->divider.flags = CLK_DIVIDER_ONE_BASED | clk_divider_flags;
|
||||
/* cache gate status */
|
||||
val = clk_readl(reg) >> shift;
|
||||
val = readl(reg) >> shift;
|
||||
val &= clk_div_mask(width);
|
||||
div_gate->cached_val = val;
|
||||
|
||||
|
|
|
@ -164,10 +164,6 @@ static void __init mx5_clocks_common_init(void __iomem *ccm_base)
|
|||
clk[IMX5_CLK_CKIH1] = imx_obtain_fixed_clock("ckih1", 0);
|
||||
clk[IMX5_CLK_CKIH2] = imx_obtain_fixed_clock("ckih2", 0);
|
||||
|
||||
clk[IMX5_CLK_PERIPH_APM] = imx_clk_mux("periph_apm", MXC_CCM_CBCMR, 12, 2,
|
||||
periph_apm_sel, ARRAY_SIZE(periph_apm_sel));
|
||||
clk[IMX5_CLK_MAIN_BUS] = imx_clk_mux("main_bus", MXC_CCM_CBCDR, 25, 1,
|
||||
main_bus_sel, ARRAY_SIZE(main_bus_sel));
|
||||
clk[IMX5_CLK_PER_LP_APM] = imx_clk_mux("per_lp_apm", MXC_CCM_CBCMR, 1, 1,
|
||||
per_lp_apm_sel, ARRAY_SIZE(per_lp_apm_sel));
|
||||
clk[IMX5_CLK_PER_PRED1] = imx_clk_divider("per_pred1", "per_lp_apm", MXC_CCM_CBCDR, 6, 2);
|
||||
|
@ -191,16 +187,10 @@ static void __init mx5_clocks_common_init(void __iomem *ccm_base)
|
|||
clk[IMX5_CLK_UART_PRED] = imx_clk_divider("uart_pred", "uart_sel", MXC_CCM_CSCDR1, 3, 3);
|
||||
clk[IMX5_CLK_UART_ROOT] = imx_clk_divider("uart_root", "uart_pred", MXC_CCM_CSCDR1, 0, 3);
|
||||
|
||||
clk[IMX5_CLK_ESDHC_A_SEL] = imx_clk_mux("esdhc_a_sel", MXC_CCM_CSCMR1, 20, 2,
|
||||
standard_pll_sel, ARRAY_SIZE(standard_pll_sel));
|
||||
clk[IMX5_CLK_ESDHC_B_SEL] = imx_clk_mux("esdhc_b_sel", MXC_CCM_CSCMR1, 16, 2,
|
||||
standard_pll_sel, ARRAY_SIZE(standard_pll_sel));
|
||||
clk[IMX5_CLK_ESDHC_A_PRED] = imx_clk_divider("esdhc_a_pred", "esdhc_a_sel", MXC_CCM_CSCDR1, 16, 3);
|
||||
clk[IMX5_CLK_ESDHC_A_PODF] = imx_clk_divider("esdhc_a_podf", "esdhc_a_pred", MXC_CCM_CSCDR1, 11, 3);
|
||||
clk[IMX5_CLK_ESDHC_B_PRED] = imx_clk_divider("esdhc_b_pred", "esdhc_b_sel", MXC_CCM_CSCDR1, 22, 3);
|
||||
clk[IMX5_CLK_ESDHC_B_PODF] = imx_clk_divider("esdhc_b_podf", "esdhc_b_pred", MXC_CCM_CSCDR1, 19, 3);
|
||||
clk[IMX5_CLK_ESDHC_C_SEL] = imx_clk_mux("esdhc_c_sel", MXC_CCM_CSCMR1, 19, 1, esdhc_c_sel, ARRAY_SIZE(esdhc_c_sel));
|
||||
clk[IMX5_CLK_ESDHC_D_SEL] = imx_clk_mux("esdhc_d_sel", MXC_CCM_CSCMR1, 18, 1, esdhc_d_sel, ARRAY_SIZE(esdhc_d_sel));
|
||||
|
||||
clk[IMX5_CLK_EMI_SEL] = imx_clk_mux("emi_sel", MXC_CCM_CBCDR, 26, 1,
|
||||
emi_slow_sel, ARRAY_SIZE(emi_slow_sel));
|
||||
|
@ -311,10 +301,6 @@ static void __init mx5_clocks_common_init(void __iomem *ccm_base)
|
|||
clk_register_clkdev(clk[IMX5_CLK_CPU_PODF], NULL, "cpu0");
|
||||
clk_register_clkdev(clk[IMX5_CLK_GPC_DVFS], "gpc_dvfs", NULL);
|
||||
|
||||
/* Set SDHC parents to be PLL2 */
|
||||
clk_set_parent(clk[IMX5_CLK_ESDHC_A_SEL], clk[IMX5_CLK_PLL2_SW]);
|
||||
clk_set_parent(clk[IMX5_CLK_ESDHC_B_SEL], clk[IMX5_CLK_PLL2_SW]);
|
||||
|
||||
/* move usb phy clk to 24MHz */
|
||||
clk_set_parent(clk[IMX5_CLK_USB_PHY_SEL], clk[IMX5_CLK_OSC]);
|
||||
}
|
||||
|
@ -342,8 +328,21 @@ static void __init mx50_clocks_init(struct device_node *np)
|
|||
|
||||
mx5_clocks_common_init(ccm_base);
|
||||
|
||||
/*
|
||||
* This clock is called periph_clk in the i.MX50 Reference Manual, but
|
||||
* it comes closest in scope to the main_bus_clk of i.MX51 and i.MX53
|
||||
*/
|
||||
clk[IMX5_CLK_MAIN_BUS] = imx_clk_mux("main_bus", MXC_CCM_CBCDR, 25, 2,
|
||||
standard_pll_sel, ARRAY_SIZE(standard_pll_sel));
|
||||
|
||||
clk[IMX5_CLK_LP_APM] = imx_clk_mux("lp_apm", MXC_CCM_CCSR, 10, 1,
|
||||
lp_apm_sel, ARRAY_SIZE(lp_apm_sel));
|
||||
clk[IMX5_CLK_ESDHC_A_SEL] = imx_clk_mux("esdhc_a_sel", MXC_CCM_CSCMR1, 21, 2,
|
||||
standard_pll_sel, ARRAY_SIZE(standard_pll_sel));
|
||||
clk[IMX5_CLK_ESDHC_B_SEL] = imx_clk_mux("esdhc_b_sel", MXC_CCM_CSCMR1, 16, 2,
|
||||
standard_pll_sel, ARRAY_SIZE(standard_pll_sel));
|
||||
clk[IMX5_CLK_ESDHC_C_SEL] = imx_clk_mux("esdhc_c_sel", MXC_CCM_CSCMR1, 20, 1, esdhc_c_sel, ARRAY_SIZE(esdhc_c_sel));
|
||||
clk[IMX5_CLK_ESDHC_D_SEL] = imx_clk_mux("esdhc_d_sel", MXC_CCM_CSCMR1, 19, 1, esdhc_d_sel, ARRAY_SIZE(esdhc_d_sel));
|
||||
clk[IMX5_CLK_ESDHC1_PER_GATE] = imx_clk_gate2("esdhc1_per_gate", "esdhc_a_podf", MXC_CCM_CCGR3, 2);
|
||||
clk[IMX5_CLK_ESDHC2_PER_GATE] = imx_clk_gate2("esdhc2_per_gate", "esdhc_c_sel", MXC_CCM_CCGR3, 6);
|
||||
clk[IMX5_CLK_ESDHC3_PER_GATE] = imx_clk_gate2("esdhc3_per_gate", "esdhc_b_podf", MXC_CCM_CCGR3, 10);
|
||||
|
@ -372,6 +371,10 @@ static void __init mx50_clocks_init(struct device_node *np)
|
|||
clk_data.clk_num = ARRAY_SIZE(clk);
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
||||
|
||||
/* Set SDHC parents to be PLL2 */
|
||||
clk_set_parent(clk[IMX5_CLK_ESDHC_A_SEL], clk[IMX5_CLK_PLL2_SW]);
|
||||
clk_set_parent(clk[IMX5_CLK_ESDHC_B_SEL], clk[IMX5_CLK_PLL2_SW]);
|
||||
|
||||
/* set SDHC root clock to 200MHZ*/
|
||||
clk_set_rate(clk[IMX5_CLK_ESDHC_A_PODF], 200000000);
|
||||
clk_set_rate(clk[IMX5_CLK_ESDHC_B_PODF], 200000000);
|
||||
|
@ -410,6 +413,10 @@ static void __init mx51_clocks_init(struct device_node *np)
|
|||
|
||||
mx5_clocks_common_init(ccm_base);
|
||||
|
||||
clk[IMX5_CLK_PERIPH_APM] = imx_clk_mux("periph_apm", MXC_CCM_CBCMR, 12, 2,
|
||||
periph_apm_sel, ARRAY_SIZE(periph_apm_sel));
|
||||
clk[IMX5_CLK_MAIN_BUS] = imx_clk_mux("main_bus", MXC_CCM_CBCDR, 25, 1,
|
||||
main_bus_sel, ARRAY_SIZE(main_bus_sel));
|
||||
clk[IMX5_CLK_LP_APM] = imx_clk_mux("lp_apm", MXC_CCM_CCSR, 9, 1,
|
||||
lp_apm_sel, ARRAY_SIZE(lp_apm_sel));
|
||||
clk[IMX5_CLK_IPU_DI0_SEL] = imx_clk_mux_flags("ipu_di0_sel", MXC_CCM_CSCMR2, 26, 3,
|
||||
|
@ -422,6 +429,12 @@ static void __init mx51_clocks_init(struct device_node *np)
|
|||
mx51_tve_sel, ARRAY_SIZE(mx51_tve_sel));
|
||||
clk[IMX5_CLK_TVE_GATE] = imx_clk_gate2("tve_gate", "tve_sel", MXC_CCM_CCGR2, 30);
|
||||
clk[IMX5_CLK_TVE_PRED] = imx_clk_divider("tve_pred", "pll3_sw", MXC_CCM_CDCDR, 28, 3);
|
||||
clk[IMX5_CLK_ESDHC_A_SEL] = imx_clk_mux("esdhc_a_sel", MXC_CCM_CSCMR1, 20, 2,
|
||||
standard_pll_sel, ARRAY_SIZE(standard_pll_sel));
|
||||
clk[IMX5_CLK_ESDHC_B_SEL] = imx_clk_mux("esdhc_b_sel", MXC_CCM_CSCMR1, 16, 2,
|
||||
standard_pll_sel, ARRAY_SIZE(standard_pll_sel));
|
||||
clk[IMX5_CLK_ESDHC_C_SEL] = imx_clk_mux("esdhc_c_sel", MXC_CCM_CSCMR1, 19, 1, esdhc_c_sel, ARRAY_SIZE(esdhc_c_sel));
|
||||
clk[IMX5_CLK_ESDHC_D_SEL] = imx_clk_mux("esdhc_d_sel", MXC_CCM_CSCMR1, 18, 1, esdhc_d_sel, ARRAY_SIZE(esdhc_d_sel));
|
||||
clk[IMX5_CLK_ESDHC1_PER_GATE] = imx_clk_gate2("esdhc1_per_gate", "esdhc_a_podf", MXC_CCM_CCGR3, 2);
|
||||
clk[IMX5_CLK_ESDHC2_PER_GATE] = imx_clk_gate2("esdhc2_per_gate", "esdhc_b_podf", MXC_CCM_CCGR3, 6);
|
||||
clk[IMX5_CLK_ESDHC3_PER_GATE] = imx_clk_gate2("esdhc3_per_gate", "esdhc_c_sel", MXC_CCM_CCGR3, 10);
|
||||
|
@ -452,6 +465,10 @@ static void __init mx51_clocks_init(struct device_node *np)
|
|||
/* set the usboh3 parent to pll2_sw */
|
||||
clk_set_parent(clk[IMX5_CLK_USBOH3_SEL], clk[IMX5_CLK_PLL2_SW]);
|
||||
|
||||
/* Set SDHC parents to be PLL2 */
|
||||
clk_set_parent(clk[IMX5_CLK_ESDHC_A_SEL], clk[IMX5_CLK_PLL2_SW]);
|
||||
clk_set_parent(clk[IMX5_CLK_ESDHC_B_SEL], clk[IMX5_CLK_PLL2_SW]);
|
||||
|
||||
/* set SDHC root clock to 166.25MHZ*/
|
||||
clk_set_rate(clk[IMX5_CLK_ESDHC_A_PODF], 166250000);
|
||||
clk_set_rate(clk[IMX5_CLK_ESDHC_B_PODF], 166250000);
|
||||
|
@ -506,6 +523,10 @@ static void __init mx53_clocks_init(struct device_node *np)
|
|||
|
||||
mx5_clocks_common_init(ccm_base);
|
||||
|
||||
clk[IMX5_CLK_PERIPH_APM] = imx_clk_mux("periph_apm", MXC_CCM_CBCMR, 12, 2,
|
||||
periph_apm_sel, ARRAY_SIZE(periph_apm_sel));
|
||||
clk[IMX5_CLK_MAIN_BUS] = imx_clk_mux("main_bus", MXC_CCM_CBCDR, 25, 1,
|
||||
main_bus_sel, ARRAY_SIZE(main_bus_sel));
|
||||
clk[IMX5_CLK_LP_APM] = imx_clk_mux("lp_apm", MXC_CCM_CCSR, 10, 1,
|
||||
lp_apm_sel, ARRAY_SIZE(lp_apm_sel));
|
||||
clk[IMX5_CLK_LDB_DI1_DIV_3_5] = imx_clk_fixed_factor("ldb_di1_div_3_5", "ldb_di1_sel", 2, 7);
|
||||
|
@ -527,6 +548,12 @@ static void __init mx53_clocks_init(struct device_node *np)
|
|||
mx53_tve_ext_sel, ARRAY_SIZE(mx53_tve_ext_sel), CLK_SET_RATE_PARENT);
|
||||
clk[IMX5_CLK_TVE_GATE] = imx_clk_gate2("tve_gate", "tve_pred", MXC_CCM_CCGR2, 30);
|
||||
clk[IMX5_CLK_TVE_PRED] = imx_clk_divider("tve_pred", "tve_ext_sel", MXC_CCM_CDCDR, 28, 3);
|
||||
clk[IMX5_CLK_ESDHC_A_SEL] = imx_clk_mux("esdhc_a_sel", MXC_CCM_CSCMR1, 20, 2,
|
||||
standard_pll_sel, ARRAY_SIZE(standard_pll_sel));
|
||||
clk[IMX5_CLK_ESDHC_B_SEL] = imx_clk_mux("esdhc_b_sel", MXC_CCM_CSCMR1, 16, 2,
|
||||
standard_pll_sel, ARRAY_SIZE(standard_pll_sel));
|
||||
clk[IMX5_CLK_ESDHC_C_SEL] = imx_clk_mux("esdhc_c_sel", MXC_CCM_CSCMR1, 19, 1, esdhc_c_sel, ARRAY_SIZE(esdhc_c_sel));
|
||||
clk[IMX5_CLK_ESDHC_D_SEL] = imx_clk_mux("esdhc_d_sel", MXC_CCM_CSCMR1, 18, 1, esdhc_d_sel, ARRAY_SIZE(esdhc_d_sel));
|
||||
clk[IMX5_CLK_ESDHC1_PER_GATE] = imx_clk_gate2("esdhc1_per_gate", "esdhc_a_podf", MXC_CCM_CCGR3, 2);
|
||||
clk[IMX5_CLK_ESDHC2_PER_GATE] = imx_clk_gate2("esdhc2_per_gate", "esdhc_c_sel", MXC_CCM_CCGR3, 6);
|
||||
clk[IMX5_CLK_ESDHC3_PER_GATE] = imx_clk_gate2("esdhc3_per_gate", "esdhc_b_podf", MXC_CCM_CCGR3, 10);
|
||||
|
@ -589,6 +616,10 @@ static void __init mx53_clocks_init(struct device_node *np)
|
|||
clk_data.clk_num = ARRAY_SIZE(clk);
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
||||
|
||||
/* Set SDHC parents to be PLL2 */
|
||||
clk_set_parent(clk[IMX5_CLK_ESDHC_A_SEL], clk[IMX5_CLK_PLL2_SW]);
|
||||
clk_set_parent(clk[IMX5_CLK_ESDHC_B_SEL], clk[IMX5_CLK_PLL2_SW]);
|
||||
|
||||
/* set SDHC root clock to 200MHZ*/
|
||||
clk_set_rate(clk[IMX5_CLK_ESDHC_A_PODF], 200000000);
|
||||
clk_set_rate(clk[IMX5_CLK_ESDHC_B_PODF], 200000000);
|
|
@ -76,6 +76,20 @@ static u32 share_count_ssi1;
|
|||
static u32 share_count_ssi2;
|
||||
static u32 share_count_ssi3;
|
||||
|
||||
static struct clk ** const uart_clks[] __initconst = {
|
||||
&clks[IMX6SLL_CLK_UART1_IPG],
|
||||
&clks[IMX6SLL_CLK_UART1_SERIAL],
|
||||
&clks[IMX6SLL_CLK_UART2_IPG],
|
||||
&clks[IMX6SLL_CLK_UART2_SERIAL],
|
||||
&clks[IMX6SLL_CLK_UART3_IPG],
|
||||
&clks[IMX6SLL_CLK_UART3_SERIAL],
|
||||
&clks[IMX6SLL_CLK_UART4_IPG],
|
||||
&clks[IMX6SLL_CLK_UART4_SERIAL],
|
||||
&clks[IMX6SLL_CLK_UART5_IPG],
|
||||
&clks[IMX6SLL_CLK_UART5_SERIAL],
|
||||
NULL
|
||||
};
|
||||
|
||||
static void __init imx6sll_clocks_init(struct device_node *ccm_node)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
@ -268,7 +282,7 @@ static void __init imx6sll_clocks_init(struct device_node *ccm_node)
|
|||
clks[IMX6SLL_CLK_GPT_BUS] = imx_clk_gate2("gpt1_bus", "perclk", base + 0x6c, 20);
|
||||
clks[IMX6SLL_CLK_GPT_SERIAL] = imx_clk_gate2("gpt1_serial", "perclk", base + 0x6c, 22);
|
||||
clks[IMX6SLL_CLK_UART4_IPG] = imx_clk_gate2("uart4_ipg", "ipg", base + 0x6c, 24);
|
||||
clks[IMX6SLL_CLK_UART4_SERIAL] = imx_clk_gate2("uart4_serail", "uart_podf", base + 0x6c, 24);
|
||||
clks[IMX6SLL_CLK_UART4_SERIAL] = imx_clk_gate2("uart4_serial", "uart_podf", base + 0x6c, 24);
|
||||
clks[IMX6SLL_CLK_GPIO1] = imx_clk_gate2("gpio1", "ipg", base + 0x6c, 26);
|
||||
clks[IMX6SLL_CLK_GPIO5] = imx_clk_gate2("gpio5", "ipg", base + 0x6c, 30);
|
||||
|
||||
|
@ -334,6 +348,8 @@ static void __init imx6sll_clocks_init(struct device_node *ccm_node)
|
|||
clk_data.clk_num = ARRAY_SIZE(clks);
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
||||
|
||||
imx_register_uart_clocks(uart_clks);
|
||||
|
||||
/* Lower the AHB clock rate before changing the clock source. */
|
||||
clk_set_rate(clks[IMX6SLL_CLK_AHB], 99000000);
|
||||
|
||||
|
|
|
@ -417,8 +417,8 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
|
|||
clks[IMX7D_PLL_DRAM_MAIN] = imx_clk_pllv3(IMX_PLLV3_DDR_IMX7, "pll_dram_main", "osc", base + 0x70, 0x7f);
|
||||
clks[IMX7D_PLL_SYS_MAIN] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll_sys_main", "osc", base + 0xb0, 0x1);
|
||||
clks[IMX7D_PLL_ENET_MAIN] = imx_clk_pllv3(IMX_PLLV3_ENET_IMX7, "pll_enet_main", "osc", base + 0xe0, 0x0);
|
||||
clks[IMX7D_PLL_AUDIO_MAIN] = imx_clk_pllv3(IMX_PLLV3_AV, "pll_audio_main", "osc", base + 0xf0, 0x7f);
|
||||
clks[IMX7D_PLL_VIDEO_MAIN] = imx_clk_pllv3(IMX_PLLV3_AV, "pll_video_main", "osc", base + 0x130, 0x7f);
|
||||
clks[IMX7D_PLL_AUDIO_MAIN] = imx_clk_pllv3(IMX_PLLV3_AV_IMX7, "pll_audio_main", "osc", base + 0xf0, 0x7f);
|
||||
clks[IMX7D_PLL_VIDEO_MAIN] = imx_clk_pllv3(IMX_PLLV3_AV_IMX7, "pll_video_main", "osc", base + 0x130, 0x7f);
|
||||
|
||||
clks[IMX7D_PLL_ARM_MAIN_BYPASS] = imx_clk_mux_flags("pll_arm_main_bypass", base + 0x60, 16, 1, pll_arm_bypass_sel, ARRAY_SIZE(pll_arm_bypass_sel), CLK_SET_RATE_PARENT);
|
||||
clks[IMX7D_PLL_DRAM_MAIN_BYPASS] = imx_clk_mux_flags("pll_dram_main_bypass", base + 0x70, 16, 1, pll_dram_bypass_sel, ARRAY_SIZE(pll_dram_bypass_sel), CLK_SET_RATE_PARENT);
|
||||
|
|
|
@ -151,7 +151,6 @@ static void __init imx7ulp_clk_pcc2_init(struct device_node *np)
|
|||
clks[IMX7ULP_CLK_DMA1] = imx_clk_hw_gate("dma1", "nic1_clk", base + 0x20, 30);
|
||||
clks[IMX7ULP_CLK_RGPIO2P1] = imx_clk_hw_gate("rgpio2p1", "nic1_bus_clk", base + 0x3c, 30);
|
||||
clks[IMX7ULP_CLK_DMA_MUX1] = imx_clk_hw_gate("dma_mux1", "nic1_bus_clk", base + 0x84, 30);
|
||||
clks[IMX7ULP_CLK_SNVS] = imx_clk_hw_gate("snvs", "nic1_bus_clk", base + 0x8c, 30);
|
||||
clks[IMX7ULP_CLK_CAAM] = imx_clk_hw_gate("caam", "nic1_clk", base + 0x90, 30);
|
||||
clks[IMX7ULP_CLK_LPTPM4] = imx7ulp_clk_composite("lptpm4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94);
|
||||
clks[IMX7ULP_CLK_LPTPM5] = imx7ulp_clk_composite("lptpm5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98);
|
||||
|
|
|
@ -458,6 +458,7 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
|
|||
clks[IMX8MQ_CLK_DSI_DBI] = imx8m_clk_composite("dsi_dbi", imx8mq_dsi_dbi_sels, base + 0xbc00);
|
||||
clks[IMX8MQ_CLK_DSI_ESC] = imx8m_clk_composite("dsi_esc", imx8mq_dsi_esc_sels, base + 0xbc80);
|
||||
clks[IMX8MQ_CLK_DSI_AHB] = imx8m_clk_composite("dsi_ahb", imx8mq_dsi_ahb_sels, base + 0x9200);
|
||||
clks[IMX8MQ_CLK_DSI_IPG_DIV] = imx_clk_divider2("dsi_ipg_div", "dsi_ahb", base + 0x9280, 0, 6);
|
||||
clks[IMX8MQ_CLK_CSI1_CORE] = imx8m_clk_composite("csi1_core", imx8mq_csi1_core_sels, base + 0xbd00);
|
||||
clks[IMX8MQ_CLK_CSI1_PHY_REF] = imx8m_clk_composite("csi1_phy_ref", imx8mq_csi1_phy_sels, base + 0xbd80);
|
||||
clks[IMX8MQ_CLK_CSI1_ESC] = imx8m_clk_composite("csi1_esc", imx8mq_csi1_esc_sels, base + 0xbe00);
|
||||
|
|
|
@ -43,7 +43,7 @@ static int clk_pfdv2_wait(struct clk_pfdv2 *pfd)
|
|||
{
|
||||
u32 val;
|
||||
|
||||
return readl_poll_timeout(pfd->reg, val, val & pfd->vld_bit,
|
||||
return readl_poll_timeout(pfd->reg, val, val & (1 << pfd->vld_bit),
|
||||
0, LOCK_TIMEOUT_US);
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ static int clk_pfdv2_enable(struct clk_hw *hw)
|
|||
|
||||
spin_lock_irqsave(&pfd_lock, flags);
|
||||
val = readl_relaxed(pfd->reg);
|
||||
val &= ~pfd->gate_bit;
|
||||
val &= ~(1 << pfd->gate_bit);
|
||||
writel_relaxed(val, pfd->reg);
|
||||
spin_unlock_irqrestore(&pfd_lock, flags);
|
||||
|
||||
|
@ -70,7 +70,7 @@ static void clk_pfdv2_disable(struct clk_hw *hw)
|
|||
|
||||
spin_lock_irqsave(&pfd_lock, flags);
|
||||
val = readl_relaxed(pfd->reg);
|
||||
val |= pfd->gate_bit;
|
||||
val |= (1 << pfd->gate_bit);
|
||||
writel_relaxed(val, pfd->reg);
|
||||
spin_unlock_irqrestore(&pfd_lock, flags);
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ static int clk_pfdv2_is_enabled(struct clk_hw *hw)
|
|||
{
|
||||
struct clk_pfdv2 *pfd = to_clk_pfdv2(hw);
|
||||
|
||||
if (readl_relaxed(pfd->reg) & pfd->gate_bit)
|
||||
if (readl_relaxed(pfd->reg) & (1 << pfd->gate_bit))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
|
@ -180,7 +180,7 @@ struct clk_hw *imx_clk_pfdv2(const char *name, const char *parent_name,
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pfd->reg = reg;
|
||||
pfd->gate_bit = 1 << ((idx + 1) * 8 - 1);
|
||||
pfd->gate_bit = (idx + 1) * 8 - 1;
|
||||
pfd->vld_bit = pfd->gate_bit - 1;
|
||||
pfd->frac_off = idx * 8;
|
||||
|
||||
|
|
|
@ -74,10 +74,9 @@ static unsigned long clk_pll1416x_recalc_rate(struct clk_hw *hw,
|
|||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pll14xx *pll = to_clk_pll14xx(hw);
|
||||
u32 mdiv, pdiv, sdiv, pll_gnrl, pll_div;
|
||||
u32 mdiv, pdiv, sdiv, pll_div;
|
||||
u64 fvco = parent_rate;
|
||||
|
||||
pll_gnrl = readl_relaxed(pll->base);
|
||||
pll_div = readl_relaxed(pll->base + 4);
|
||||
mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT;
|
||||
pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT;
|
||||
|
@ -93,11 +92,10 @@ static unsigned long clk_pll1443x_recalc_rate(struct clk_hw *hw,
|
|||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pll14xx *pll = to_clk_pll14xx(hw);
|
||||
u32 mdiv, pdiv, sdiv, pll_gnrl, pll_div_ctl0, pll_div_ctl1;
|
||||
u32 mdiv, pdiv, sdiv, pll_div_ctl0, pll_div_ctl1;
|
||||
short int kdiv;
|
||||
u64 fvco = parent_rate;
|
||||
|
||||
pll_gnrl = readl_relaxed(pll->base);
|
||||
pll_div_ctl0 = readl_relaxed(pll->base + 4);
|
||||
pll_div_ctl1 = readl_relaxed(pll->base + 8);
|
||||
mdiv = (pll_div_ctl0 & MDIV_MASK) >> MDIV_SHIFT;
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#define PLL_NUM_OFFSET 0x10
|
||||
#define PLL_DENOM_OFFSET 0x20
|
||||
#define PLL_IMX7_NUM_OFFSET 0x20
|
||||
#define PLL_IMX7_DENOM_OFFSET 0x30
|
||||
|
||||
#define PLL_VF610_NUM_OFFSET 0x20
|
||||
#define PLL_VF610_DENOM_OFFSET 0x30
|
||||
|
@ -49,6 +51,8 @@ struct clk_pllv3 {
|
|||
u32 div_mask;
|
||||
u32 div_shift;
|
||||
unsigned long ref_clock;
|
||||
u32 num_offset;
|
||||
u32 denom_offset;
|
||||
};
|
||||
|
||||
#define to_clk_pllv3(_hw) container_of(_hw, struct clk_pllv3, hw)
|
||||
|
@ -219,8 +223,8 @@ static unsigned long clk_pllv3_av_recalc_rate(struct clk_hw *hw,
|
|||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pllv3 *pll = to_clk_pllv3(hw);
|
||||
u32 mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET);
|
||||
u32 mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET);
|
||||
u32 mfn = readl_relaxed(pll->base + pll->num_offset);
|
||||
u32 mfd = readl_relaxed(pll->base + pll->denom_offset);
|
||||
u32 div = readl_relaxed(pll->base) & pll->div_mask;
|
||||
u64 temp64 = (u64)parent_rate;
|
||||
|
||||
|
@ -289,8 +293,8 @@ static int clk_pllv3_av_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
val &= ~pll->div_mask;
|
||||
val |= div;
|
||||
writel_relaxed(val, pll->base);
|
||||
writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
|
||||
writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
|
||||
writel_relaxed(mfn, pll->base + pll->num_offset);
|
||||
writel_relaxed(mfd, pll->base + pll->denom_offset);
|
||||
|
||||
return clk_pllv3_wait_lock(pll);
|
||||
}
|
||||
|
@ -352,8 +356,8 @@ static unsigned long clk_pllv3_vf610_recalc_rate(struct clk_hw *hw,
|
|||
struct clk_pllv3 *pll = to_clk_pllv3(hw);
|
||||
struct clk_pllv3_vf610_mf mf;
|
||||
|
||||
mf.mfn = readl_relaxed(pll->base + PLL_VF610_NUM_OFFSET);
|
||||
mf.mfd = readl_relaxed(pll->base + PLL_VF610_DENOM_OFFSET);
|
||||
mf.mfn = readl_relaxed(pll->base + pll->num_offset);
|
||||
mf.mfd = readl_relaxed(pll->base + pll->denom_offset);
|
||||
mf.mfi = (readl_relaxed(pll->base) & pll->div_mask) ? 22 : 20;
|
||||
|
||||
return clk_pllv3_vf610_mf_to_rate(parent_rate, mf);
|
||||
|
@ -382,8 +386,8 @@ static int clk_pllv3_vf610_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
val |= pll->div_mask; /* set bit for mfi=22 */
|
||||
writel_relaxed(val, pll->base);
|
||||
|
||||
writel_relaxed(mf.mfn, pll->base + PLL_VF610_NUM_OFFSET);
|
||||
writel_relaxed(mf.mfd, pll->base + PLL_VF610_DENOM_OFFSET);
|
||||
writel_relaxed(mf.mfn, pll->base + pll->num_offset);
|
||||
writel_relaxed(mf.mfd, pll->base + pll->denom_offset);
|
||||
|
||||
return clk_pllv3_wait_lock(pll);
|
||||
}
|
||||
|
@ -426,6 +430,8 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pll->power_bit = BM_PLL_POWER;
|
||||
pll->num_offset = PLL_NUM_OFFSET;
|
||||
pll->denom_offset = PLL_DENOM_OFFSET;
|
||||
|
||||
switch (type) {
|
||||
case IMX_PLLV3_SYS:
|
||||
|
@ -433,13 +439,20 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
|
|||
break;
|
||||
case IMX_PLLV3_SYS_VF610:
|
||||
ops = &clk_pllv3_vf610_ops;
|
||||
pll->num_offset = PLL_VF610_NUM_OFFSET;
|
||||
pll->denom_offset = PLL_VF610_DENOM_OFFSET;
|
||||
break;
|
||||
case IMX_PLLV3_USB_VF610:
|
||||
pll->div_shift = 1;
|
||||
/* fall through */
|
||||
case IMX_PLLV3_USB:
|
||||
ops = &clk_pllv3_ops;
|
||||
pll->powerup_set = true;
|
||||
break;
|
||||
case IMX_PLLV3_AV_IMX7:
|
||||
pll->num_offset = PLL_IMX7_NUM_OFFSET;
|
||||
pll->denom_offset = PLL_IMX7_DENOM_OFFSET;
|
||||
/* fall through */
|
||||
case IMX_PLLV3_AV:
|
||||
ops = &clk_pllv3_av_ops;
|
||||
break;
|
||||
|
@ -454,6 +467,8 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
|
|||
break;
|
||||
case IMX_PLLV3_DDR_IMX7:
|
||||
pll->power_bit = IMX7_DDR_PLL_POWER;
|
||||
pll->num_offset = PLL_IMX7_NUM_OFFSET;
|
||||
pll->denom_offset = PLL_IMX7_DENOM_OFFSET;
|
||||
ops = &clk_pllv3_av_ops;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -30,6 +30,9 @@
|
|||
/* PLL Denominator Register (xPLLDENOM) */
|
||||
#define PLL_DENOM_OFFSET 0x14
|
||||
|
||||
#define MAX_MFD 0x3fffffff
|
||||
#define DEFAULT_MFD 1000000
|
||||
|
||||
struct clk_pllv4 {
|
||||
struct clk_hw hw;
|
||||
void __iomem *base;
|
||||
|
@ -64,13 +67,20 @@ static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
|
|||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pllv4 *pll = to_clk_pllv4(hw);
|
||||
u32 div;
|
||||
u32 mult, mfn, mfd;
|
||||
u64 temp64;
|
||||
|
||||
div = readl_relaxed(pll->base + PLL_CFG_OFFSET);
|
||||
div &= BM_PLL_MULT;
|
||||
div >>= BP_PLL_MULT;
|
||||
mult = readl_relaxed(pll->base + PLL_CFG_OFFSET);
|
||||
mult &= BM_PLL_MULT;
|
||||
mult >>= BP_PLL_MULT;
|
||||
|
||||
return parent_rate * div;
|
||||
mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET);
|
||||
mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET);
|
||||
temp64 = parent_rate;
|
||||
temp64 *= mfn;
|
||||
do_div(temp64, mfd);
|
||||
|
||||
return (parent_rate * mult) + (u32)temp64;
|
||||
}
|
||||
|
||||
static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
@ -78,14 +88,46 @@ static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
{
|
||||
unsigned long parent_rate = *prate;
|
||||
unsigned long round_rate, i;
|
||||
u32 mfn, mfd = DEFAULT_MFD;
|
||||
bool found = false;
|
||||
u64 temp64;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
|
||||
round_rate = parent_rate * pllv4_mult_table[i];
|
||||
if (rate >= round_rate)
|
||||
return round_rate;
|
||||
if (rate >= round_rate) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return round_rate;
|
||||
if (!found) {
|
||||
pr_warn("%s: unable to round rate %lu, parent rate %lu\n",
|
||||
clk_hw_get_name(hw), rate, parent_rate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parent_rate <= MAX_MFD)
|
||||
mfd = parent_rate;
|
||||
|
||||
temp64 = (u64)(rate - round_rate);
|
||||
temp64 *= mfd;
|
||||
do_div(temp64, parent_rate);
|
||||
mfn = temp64;
|
||||
|
||||
/*
|
||||
* NOTE: The value of numerator must always be configured to be
|
||||
* less than the value of the denominator. If we can't get a proper
|
||||
* pair of mfn/mfd, we simply return the round_rate without using
|
||||
* the frac part.
|
||||
*/
|
||||
if (mfn >= mfd)
|
||||
return round_rate;
|
||||
|
||||
temp64 = (u64)parent_rate;
|
||||
temp64 *= mfn;
|
||||
do_div(temp64, mfd);
|
||||
|
||||
return round_rate + (u32)temp64;
|
||||
}
|
||||
|
||||
static bool clk_pllv4_is_valid_mult(unsigned int mult)
|
||||
|
@ -105,18 +147,30 @@ static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pllv4 *pll = to_clk_pllv4(hw);
|
||||
u32 val, mult;
|
||||
u32 val, mult, mfn, mfd = DEFAULT_MFD;
|
||||
u64 temp64;
|
||||
|
||||
mult = rate / parent_rate;
|
||||
|
||||
if (!clk_pllv4_is_valid_mult(mult))
|
||||
return -EINVAL;
|
||||
|
||||
if (parent_rate <= MAX_MFD)
|
||||
mfd = parent_rate;
|
||||
|
||||
temp64 = (u64)(rate - mult * parent_rate);
|
||||
temp64 *= mfd;
|
||||
do_div(temp64, parent_rate);
|
||||
mfn = temp64;
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG_OFFSET);
|
||||
val &= ~BM_PLL_MULT;
|
||||
val |= mult << BP_PLL_MULT;
|
||||
writel_relaxed(val, pll->base + PLL_CFG_OFFSET);
|
||||
|
||||
writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
|
||||
writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -348,7 +348,7 @@ static unsigned long clk_sccg_pll_recalc_rate(struct clk_hw *hw,
|
|||
|
||||
temp64 = parent_rate;
|
||||
|
||||
val = clk_readl(pll->base + PLL_CFG0);
|
||||
val = readl(pll->base + PLL_CFG0);
|
||||
if (val & SSCG_PLL_BYPASS2_MASK) {
|
||||
temp64 = parent_rate;
|
||||
} else if (val & SSCG_PLL_BYPASS1_MASK) {
|
||||
|
@ -371,10 +371,10 @@ static int clk_sccg_pll_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
u32 val;
|
||||
|
||||
/* set bypass here too since the parent might be the same */
|
||||
val = clk_readl(pll->base + PLL_CFG0);
|
||||
val = readl(pll->base + PLL_CFG0);
|
||||
val &= ~SSCG_PLL_BYPASS_MASK;
|
||||
val |= FIELD_PREP(SSCG_PLL_BYPASS_MASK, setup->bypass);
|
||||
clk_writel(val, pll->base + PLL_CFG0);
|
||||
writel(val, pll->base + PLL_CFG0);
|
||||
|
||||
val = readl_relaxed(pll->base + PLL_CFG2);
|
||||
val &= ~(PLL_DIVF1_MASK | PLL_DIVF2_MASK);
|
||||
|
@ -395,7 +395,7 @@ static u8 clk_sccg_pll_get_parent(struct clk_hw *hw)
|
|||
u32 val;
|
||||
u8 ret = pll->parent;
|
||||
|
||||
val = clk_readl(pll->base + PLL_CFG0);
|
||||
val = readl(pll->base + PLL_CFG0);
|
||||
if (val & SSCG_PLL_BYPASS2_MASK)
|
||||
ret = pll->bypass2;
|
||||
else if (val & SSCG_PLL_BYPASS1_MASK)
|
||||
|
@ -408,10 +408,10 @@ static int clk_sccg_pll_set_parent(struct clk_hw *hw, u8 index)
|
|||
struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
|
||||
u32 val;
|
||||
|
||||
val = clk_readl(pll->base + PLL_CFG0);
|
||||
val = readl(pll->base + PLL_CFG0);
|
||||
val &= ~SSCG_PLL_BYPASS_MASK;
|
||||
val |= FIELD_PREP(SSCG_PLL_BYPASS_MASK, pll->setup.bypass);
|
||||
clk_writel(val, pll->base + PLL_CFG0);
|
||||
writel(val, pll->base + PLL_CFG0);
|
||||
|
||||
return clk_sccg_pll_wait_lock(pll);
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ enum imx_pllv3_type {
|
|||
IMX_PLLV3_ENET_IMX7,
|
||||
IMX_PLLV3_SYS_VF610,
|
||||
IMX_PLLV3_DDR_IMX7,
|
||||
IMX_PLLV3_AV_IMX7,
|
||||
};
|
||||
|
||||
struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
|
||||
|
@ -138,11 +139,6 @@ static inline struct clk_hw *imx_clk_hw_fixed(const char *name, int rate)
|
|||
return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
|
||||
}
|
||||
|
||||
static inline struct clk_hw *imx_get_clk_hw_fixed(const char *name, int rate)
|
||||
{
|
||||
return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate);
|
||||
}
|
||||
|
||||
static inline struct clk *imx_clk_mux_ldb(const char *name, void __iomem *reg,
|
||||
u8 shift, u8 width, const char * const *parents,
|
||||
int num_parents)
|
||||
|
|
|
@ -205,6 +205,12 @@ static const struct ingenic_cgu_clk_info jz4725b_cgu_clocks[] = {
|
|||
.parents = { JZ4725B_CLK_EXT512, JZ4725B_CLK_OSC32K, -1, -1 },
|
||||
.mux = { CGU_REG_OPCR, 2, 1},
|
||||
},
|
||||
|
||||
[JZ4725B_CLK_UDC_PHY] = {
|
||||
"udc_phy", CGU_CLK_GATE,
|
||||
.parents = { JZ4725B_CLK_EXT, -1, -1, -1 },
|
||||
.gate = { CGU_REG_OPCR, 6, true },
|
||||
},
|
||||
};
|
||||
|
||||
static void __init jz4725b_cgu_init(struct device_node *np)
|
||||
|
|
|
@ -216,4 +216,87 @@ config COMMON_CLK_MT8173
|
|||
default ARCH_MEDIATEK
|
||||
---help---
|
||||
This driver supports MediaTek MT8173 clocks.
|
||||
|
||||
config COMMON_CLK_MT8183
|
||||
bool "Clock driver for MediaTek MT8183"
|
||||
depends on (ARCH_MEDIATEK && ARM64) || COMPILE_TEST
|
||||
select COMMON_CLK_MEDIATEK
|
||||
default ARCH_MEDIATEK && ARM64
|
||||
help
|
||||
This driver supports MediaTek MT8183 basic clocks.
|
||||
|
||||
config COMMON_CLK_MT8183_AUDIOSYS
|
||||
bool "Clock driver for MediaTek MT8183 audiosys"
|
||||
depends on COMMON_CLK_MT8183
|
||||
help
|
||||
This driver supports MediaTek MT8183 audiosys clocks.
|
||||
|
||||
config COMMON_CLK_MT8183_CAMSYS
|
||||
bool "Clock driver for MediaTek MT8183 camsys"
|
||||
depends on COMMON_CLK_MT8183
|
||||
help
|
||||
This driver supports MediaTek MT8183 camsys clocks.
|
||||
|
||||
config COMMON_CLK_MT8183_IMGSYS
|
||||
bool "Clock driver for MediaTek MT8183 imgsys"
|
||||
depends on COMMON_CLK_MT8183
|
||||
help
|
||||
This driver supports MediaTek MT8183 imgsys clocks.
|
||||
|
||||
config COMMON_CLK_MT8183_IPU_CORE0
|
||||
bool "Clock driver for MediaTek MT8183 ipu_core0"
|
||||
depends on COMMON_CLK_MT8183
|
||||
help
|
||||
This driver supports MediaTek MT8183 ipu_core0 clocks.
|
||||
|
||||
config COMMON_CLK_MT8183_IPU_CORE1
|
||||
bool "Clock driver for MediaTek MT8183 ipu_core1"
|
||||
depends on COMMON_CLK_MT8183
|
||||
help
|
||||
This driver supports MediaTek MT8183 ipu_core1 clocks.
|
||||
|
||||
config COMMON_CLK_MT8183_IPU_ADL
|
||||
bool "Clock driver for MediaTek MT8183 ipu_adl"
|
||||
depends on COMMON_CLK_MT8183
|
||||
help
|
||||
This driver supports MediaTek MT8183 ipu_adl clocks.
|
||||
|
||||
config COMMON_CLK_MT8183_IPU_CONN
|
||||
bool "Clock driver for MediaTek MT8183 ipu_conn"
|
||||
depends on COMMON_CLK_MT8183
|
||||
help
|
||||
This driver supports MediaTek MT8183 ipu_conn clocks.
|
||||
|
||||
config COMMON_CLK_MT8183_MFGCFG
|
||||
bool "Clock driver for MediaTek MT8183 mfgcfg"
|
||||
depends on COMMON_CLK_MT8183
|
||||
help
|
||||
This driver supports MediaTek MT8183 mfgcfg clocks.
|
||||
|
||||
config COMMON_CLK_MT8183_MMSYS
|
||||
bool "Clock driver for MediaTek MT8183 mmsys"
|
||||
depends on COMMON_CLK_MT8183
|
||||
help
|
||||
This driver supports MediaTek MT8183 mmsys clocks.
|
||||
|
||||
config COMMON_CLK_MT8183_VDECSYS
|
||||
bool "Clock driver for MediaTek MT8183 vdecsys"
|
||||
depends on COMMON_CLK_MT8183
|
||||
help
|
||||
This driver supports MediaTek MT8183 vdecsys clocks.
|
||||
|
||||
config COMMON_CLK_MT8183_VENCSYS
|
||||
bool "Clock driver for MediaTek MT8183 vencsys"
|
||||
depends on COMMON_CLK_MT8183
|
||||
help
|
||||
This driver supports MediaTek MT8183 vencsys clocks.
|
||||
|
||||
config COMMON_CLK_MT8516
|
||||
bool "Clock driver for MediaTek MT8516"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
select COMMON_CLK_MEDIATEK
|
||||
default ARCH_MEDIATEK
|
||||
help
|
||||
This driver supports MediaTek MT8516 clocks.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o clk-cpumux.o reset.o
|
||||
obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o clk-cpumux.o reset.o clk-mux.o
|
||||
|
||||
obj-$(CONFIG_COMMON_CLK_MT6797) += clk-mt6797.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT6797_IMGSYS) += clk-mt6797-img.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT6797_MMSYS) += clk-mt6797-mm.o
|
||||
|
@ -31,3 +32,16 @@ obj-$(CONFIG_COMMON_CLK_MT7629_ETHSYS) += clk-mt7629-eth.o
|
|||
obj-$(CONFIG_COMMON_CLK_MT7629_HIFSYS) += clk-mt7629-hif.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8183) += clk-mt8183.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8183_AUDIOSYS) += clk-mt8183-audio.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8183_CAMSYS) += clk-mt8183-cam.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8183_IMGSYS) += clk-mt8183-img.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8183_IPU_CORE0) += clk-mt8183-ipu0.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8183_IPU_CORE1) += clk-mt8183-ipu1.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8183_IPU_ADL) += clk-mt8183-ipu_adl.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8183_IPU_CONN) += clk-mt8183-ipu_conn.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8183_MFGCFG) += clk-mt8183-mfgcfg.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8183_MMSYS) += clk-mt8183-mm.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8183_VDECSYS) += clk-mt8183-vdec.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8183_VENCSYS) += clk-mt8183-venc.o
|
||||
obj-$(CONFIG_COMMON_CLK_MT8516) += clk-mt8516.o
|
||||
|
|
|
@ -50,4 +50,18 @@ struct clk *mtk_clk_register_gate(
|
|||
const struct clk_ops *ops,
|
||||
unsigned long flags);
|
||||
|
||||
#define GATE_MTK_FLAGS(_id, _name, _parent, _regs, _shift, \
|
||||
_ops, _flags) { \
|
||||
.id = _id, \
|
||||
.name = _name, \
|
||||
.parent_name = _parent, \
|
||||
.regs = _regs, \
|
||||
.shift = _shift, \
|
||||
.ops = _ops, \
|
||||
.flags = _flags, \
|
||||
}
|
||||
|
||||
#define GATE_MTK(_id, _name, _parent, _regs, _shift, _ops) \
|
||||
GATE_MTK_FLAGS(_id, _name, _parent, _regs, _shift, _ops, 0)
|
||||
|
||||
#endif /* __DRV_CLK_GATE_H */
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright (c) 2018 MediaTek Inc.
|
||||
// Author: Weiyi Lu <weiyi.lu@mediatek.com>
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "clk-mtk.h"
|
||||
#include "clk-gate.h"
|
||||
|
||||
#include <dt-bindings/clock/mt8183-clk.h>
|
||||
|
||||
static const struct mtk_gate_regs audio0_cg_regs = {
|
||||
.set_ofs = 0x0,
|
||||
.clr_ofs = 0x0,
|
||||
.sta_ofs = 0x0,
|
||||
};
|
||||
|
||||
static const struct mtk_gate_regs audio1_cg_regs = {
|
||||
.set_ofs = 0x4,
|
||||
.clr_ofs = 0x4,
|
||||
.sta_ofs = 0x4,
|
||||
};
|
||||
|
||||
#define GATE_AUDIO0(_id, _name, _parent, _shift) \
|
||||
GATE_MTK(_id, _name, _parent, &audio0_cg_regs, _shift, \
|
||||
&mtk_clk_gate_ops_no_setclr)
|
||||
|
||||
#define GATE_AUDIO1(_id, _name, _parent, _shift) \
|
||||
GATE_MTK(_id, _name, _parent, &audio1_cg_regs, _shift, \
|
||||
&mtk_clk_gate_ops_no_setclr)
|
||||
|
||||
static const struct mtk_gate audio_clks[] = {
|
||||
/* AUDIO0 */
|
||||
GATE_AUDIO0(CLK_AUDIO_AFE, "aud_afe", "audio_sel",
|
||||
2),
|
||||
GATE_AUDIO0(CLK_AUDIO_22M, "aud_22m", "aud_eng1_sel",
|
||||
8),
|
||||
GATE_AUDIO0(CLK_AUDIO_24M, "aud_24m", "aud_eng2_sel",
|
||||
9),
|
||||
GATE_AUDIO0(CLK_AUDIO_APLL2_TUNER, "aud_apll2_tuner", "aud_eng2_sel",
|
||||
18),
|
||||
GATE_AUDIO0(CLK_AUDIO_APLL_TUNER, "aud_apll_tuner", "aud_eng1_sel",
|
||||
19),
|
||||
GATE_AUDIO0(CLK_AUDIO_TDM, "aud_tdm", "apll12_divb",
|
||||
20),
|
||||
GATE_AUDIO0(CLK_AUDIO_ADC, "aud_adc", "audio_sel",
|
||||
24),
|
||||
GATE_AUDIO0(CLK_AUDIO_DAC, "aud_dac", "audio_sel",
|
||||
25),
|
||||
GATE_AUDIO0(CLK_AUDIO_DAC_PREDIS, "aud_dac_predis", "audio_sel",
|
||||
26),
|
||||
GATE_AUDIO0(CLK_AUDIO_TML, "aud_tml", "audio_sel",
|
||||
27),
|
||||
/* AUDIO1 */
|
||||
GATE_AUDIO1(CLK_AUDIO_I2S1, "aud_i2s1", "audio_sel",
|
||||
4),
|
||||
GATE_AUDIO1(CLK_AUDIO_I2S2, "aud_i2s2", "audio_sel",
|
||||
5),
|
||||
GATE_AUDIO1(CLK_AUDIO_I2S3, "aud_i2s3", "audio_sel",
|
||||
6),
|
||||
GATE_AUDIO1(CLK_AUDIO_I2S4, "aud_i2s4", "audio_sel",
|
||||
7),
|
||||
GATE_AUDIO1(CLK_AUDIO_PDN_ADDA6_ADC, "aud_pdn_adda6_adc", "audio_sel",
|
||||
20),
|
||||
};
|
||||
|
||||
static int clk_mt8183_audio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
int r;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
|
||||
clk_data = mtk_alloc_clk_data(CLK_AUDIO_NR_CLK);
|
||||
|
||||
mtk_clk_register_gates(node, audio_clks, ARRAY_SIZE(audio_clks),
|
||||
clk_data);
|
||||
|
||||
r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = devm_of_platform_populate(&pdev->dev);
|
||||
if (r)
|
||||
of_clk_del_provider(node);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_match_clk_mt8183_audio[] = {
|
||||
{ .compatible = "mediatek,mt8183-audiosys", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver clk_mt8183_audio_drv = {
|
||||
.probe = clk_mt8183_audio_probe,
|
||||
.driver = {
|
||||
.name = "clk-mt8183-audio",
|
||||
.of_match_table = of_match_clk_mt8183_audio,
|
||||
},
|
||||
};
|
||||
|
||||
builtin_platform_driver(clk_mt8183_audio_drv);
|
|
@ -0,0 +1,63 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright (c) 2018 MediaTek Inc.
|
||||
// Author: Weiyi Lu <weiyi.lu@mediatek.com>
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "clk-mtk.h"
|
||||
#include "clk-gate.h"
|
||||
|
||||
#include <dt-bindings/clock/mt8183-clk.h>
|
||||
|
||||
static const struct mtk_gate_regs cam_cg_regs = {
|
||||
.set_ofs = 0x4,
|
||||
.clr_ofs = 0x8,
|
||||
.sta_ofs = 0x0,
|
||||
};
|
||||
|
||||
#define GATE_CAM(_id, _name, _parent, _shift) \
|
||||
GATE_MTK(_id, _name, _parent, &cam_cg_regs, _shift, \
|
||||
&mtk_clk_gate_ops_setclr)
|
||||
|
||||
static const struct mtk_gate cam_clks[] = {
|
||||
GATE_CAM(CLK_CAM_LARB6, "cam_larb6", "cam_sel", 0),
|
||||
GATE_CAM(CLK_CAM_DFP_VAD, "cam_dfp_vad", "cam_sel", 1),
|
||||
GATE_CAM(CLK_CAM_LARB3, "cam_larb3", "cam_sel", 2),
|
||||
GATE_CAM(CLK_CAM_CAM, "cam_cam", "cam_sel", 6),
|
||||
GATE_CAM(CLK_CAM_CAMTG, "cam_camtg", "cam_sel", 7),
|
||||
GATE_CAM(CLK_CAM_SENINF, "cam_seninf", "cam_sel", 8),
|
||||
GATE_CAM(CLK_CAM_CAMSV0, "cam_camsv0", "cam_sel", 9),
|
||||
GATE_CAM(CLK_CAM_CAMSV1, "cam_camsv1", "cam_sel", 10),
|
||||
GATE_CAM(CLK_CAM_CAMSV2, "cam_camsv2", "cam_sel", 11),
|
||||
GATE_CAM(CLK_CAM_CCU, "cam_ccu", "cam_sel", 12),
|
||||
};
|
||||
|
||||
static int clk_mt8183_cam_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
|
||||
clk_data = mtk_alloc_clk_data(CLK_CAM_NR_CLK);
|
||||
|
||||
mtk_clk_register_gates(node, cam_clks, ARRAY_SIZE(cam_clks),
|
||||
clk_data);
|
||||
|
||||
return of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
}
|
||||
|
||||
static const struct of_device_id of_match_clk_mt8183_cam[] = {
|
||||
{ .compatible = "mediatek,mt8183-camsys", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver clk_mt8183_cam_drv = {
|
||||
.probe = clk_mt8183_cam_probe,
|
||||
.driver = {
|
||||
.name = "clk-mt8183-cam",
|
||||
.of_match_table = of_match_clk_mt8183_cam,
|
||||
},
|
||||
};
|
||||
|
||||
builtin_platform_driver(clk_mt8183_cam_drv);
|
|
@ -0,0 +1,63 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright (c) 2018 MediaTek Inc.
|
||||
// Author: Weiyi Lu <weiyi.lu@mediatek.com>
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "clk-mtk.h"
|
||||
#include "clk-gate.h"
|
||||
|
||||
#include <dt-bindings/clock/mt8183-clk.h>
|
||||
|
||||
static const struct mtk_gate_regs img_cg_regs = {
|
||||
.set_ofs = 0x4,
|
||||
.clr_ofs = 0x8,
|
||||
.sta_ofs = 0x0,
|
||||
};
|
||||
|
||||
#define GATE_IMG(_id, _name, _parent, _shift) \
|
||||
GATE_MTK(_id, _name, _parent, &img_cg_regs, _shift, \
|
||||
&mtk_clk_gate_ops_setclr)
|
||||
|
||||
static const struct mtk_gate img_clks[] = {
|
||||
GATE_IMG(CLK_IMG_LARB5, "img_larb5", "img_sel", 0),
|
||||
GATE_IMG(CLK_IMG_LARB2, "img_larb2", "img_sel", 1),
|
||||
GATE_IMG(CLK_IMG_DIP, "img_dip", "img_sel", 2),
|
||||
GATE_IMG(CLK_IMG_FDVT, "img_fdvt", "img_sel", 3),
|
||||
GATE_IMG(CLK_IMG_DPE, "img_dpe", "img_sel", 4),
|
||||
GATE_IMG(CLK_IMG_RSC, "img_rsc", "img_sel", 5),
|
||||
GATE_IMG(CLK_IMG_MFB, "img_mfb", "img_sel", 6),
|
||||
GATE_IMG(CLK_IMG_WPE_A, "img_wpe_a", "img_sel", 7),
|
||||
GATE_IMG(CLK_IMG_WPE_B, "img_wpe_b", "img_sel", 8),
|
||||
GATE_IMG(CLK_IMG_OWE, "img_owe", "img_sel", 9),
|
||||
};
|
||||
|
||||
static int clk_mt8183_img_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct clk_onecell_data *clk_data;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
|
||||
clk_data = mtk_alloc_clk_data(CLK_IMG_NR_CLK);
|
||||
|
||||
mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks),
|
||||
clk_data);
|
||||
|
||||
return of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
|
||||
}
|
||||
|
||||
static const struct of_device_id of_match_clk_mt8183_img[] = {
|
||||
{ .compatible = "mediatek,mt8183-imgsys", },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver clk_mt8183_img_drv = {
|
||||
.probe = clk_mt8183_img_probe,
|
||||
.driver = {
|
||||
.name = "clk-mt8183-img",
|
||||
.of_match_table = of_match_clk_mt8183_img,
|
||||
},
|
||||
};
|
||||
|
||||
builtin_platform_driver(clk_mt8183_img_drv);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue