- Support for STM32F769 - Rework AT91 sckc DT bindings - Fix slow RC oscillator issue on sama5d3 - AT91 sam9x60 PMC support - SiFive FU540 PRCI and PLL support * clk-stm32f4: clk: stm32mp1: Add ddrperfm clock clk: stm32: Introduce clocks of STM32F769 board * clk-tegra: clk: tegra: divider: Mark Memory Controller clock as read-only clk: tegra: emc: Replace BUG() with WARN_ONCE() clk: tegra: emc: Fix EMC max-rate clamping clk: tegra: emc: Support multiple RAM codes clk: tegra: emc: Don't enable EMC clock manually clk: tegra124: Remove lock-enable bit from PLLM clk: tegra: Fix PLLM programming on Tegra124+ when PMC overrides divider clk: tegra: Don't enable already enabled PLLs * clk-at91: clk: at91: Mark struct clk_range as const clk: at91: add sam9x60 pmc driver dt-bindings: clk: at91: add bindings for SAM9X60 pmc clk: at91: add sam9x60 PLL driver clk: at91: master: Add sam9x60 support clk: at91: usb: Add sam9x60 support clk: at91: allow configuring generated PCR layout clk: at91: allow configuring peripheral PCR layout clk: at91: sckc: handle different RC startup time clk: at91: modernize sckc binding dt-bindings: clock: at91: new sckc bindings * clk-sifive-fu540: clk: sifive: add a driver for the SiFive FU540 PRCI IP block clk: analogbits: add Wide-Range PLL library dt-bindings: clk: add documentation for the SiFive PRCI driver * clk-spdx: 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
ff060016 parents 5816b74 b06df56 e71f4d3 7b4c162 30b8e27 d65530c59 files · +2424 −259Browse filesUnified · Split
| @@ -8,35 +8,30 @@ Slow Clock controller: · 7 unmodified lines | |||
| 8 | 8 | ||
| 9 | Required properties: | 9 | Required properties: |
| 10 | - compatible : shall be one of the following: | 10 | - compatible : shall be one of the following: |
| 11 | "atmel,at91sam9x5-sckc" or | 11 | "atmel,at91sam9x5-sckc", |
| 12 | "atmel,sama5d3-sckc" or | ||
| 12 | "atmel,sama5d4-sckc": | 13 | "atmel,sama5d4-sckc": |
| 13 | at91 SCKC (Slow Clock Controller) | 14 | at91 SCKC (Slow Clock Controller) |
| 14 | This node contains the slow clock definitions. | 15 | - #clock-cells : shall be 0. |
| 15 | 16 | - clocks : shall be the input parent clock phandle for the clock. | |
| 16 | "atmel,at91sam9x5-clk-slow-osc": | ||
| 17 | at91 slow oscillator | ||
| 18 | |||
| 19 | "atmel,at91sam9x5-clk-slow-rc-osc": | ||
| 20 | at91 internal slow RC oscillator | ||
| 21 | - reg : defines the IO memory reserved for the SCKC. | ||
| 22 | - #size-cells : shall be 0 (reg is used to encode clk id). | ||
| 23 | - #address-cells : shall be 1 (reg is used to encode clk id). | ||
| 24 | 17 | ||
| 18 | Optional properties: | ||
| 19 | - atmel,osc-bypass : boolean property. Set this when a clock signal is directly | ||
| 20 | provided on XIN. | ||
| 25 | 21 | ||
| 26 | For example: | 22 | For example: |
| 27 | sckc: sckc@fffffe50 { | 23 | sckc@fffffe50 { |
| 28 | compatible = "atmel,sama5d3-pmc"; | 24 | compatible = "atmel,at91sam9x5-sckc"; |
| 29 | reg = <0xfffffe50 0x4> | 25 | reg = <0xfffffe50 0x4>; |
| 30 | #size-cells = <0>; | 26 | clocks = <&slow_xtal>; |
| 31 | #address-cells = <1>; | 27 | #clock-cells = <0>; |
| 32 | |||
| 33 | /* put at91 slow clocks here */ | ||
| 34 | }; | 28 | }; |
| 35 | 29 | ||
| 36 | Power Management Controller (PMC): | 30 | Power Management Controller (PMC): |
| 37 | 31 | ||
| 38 | Required properties: | 32 | Required properties: |
| 39 | - compatible : shall be "atmel,<chip>-pmc", "syscon": | 33 | - compatible : shall be "atmel,<chip>-pmc", "syscon" or |
| 34 | "microchip,sam9x60-pmc" | ||
| 40 | <chip> can be: at91rm9200, at91sam9260, at91sam9261, | 35 | <chip> can be: at91rm9200, at91sam9260, at91sam9261, |
| 41 | at91sam9263, at91sam9g45, at91sam9n12, at91sam9rl, at91sam9g15, | 36 | at91sam9263, at91sam9g45, at91sam9n12, at91sam9rl, at91sam9g15, |
| 42 | at91sam9g25, at91sam9g35, at91sam9x25, at91sam9x35, at91sam9x5, | 37 | at91sam9g25, at91sam9g35, at91sam9x25, at91sam9x35, at91sam9x5, |
| @@ -0,0 +1,46 @@ | |||
| 1 | SiFive FU540 PRCI bindings | ||
| 2 | |||
| 3 | On the FU540 family of SoCs, most system-wide clock and reset integration | ||
| 4 | is via the PRCI IP block. | ||
| 5 | |||
| 6 | Required properties: | ||
| 7 | - compatible: Should be "sifive,<chip>-prci". Only one value is | ||
| 8 | supported: "sifive,fu540-c000-prci" | ||
| 9 | - reg: Should describe the PRCI's register target physical address region | ||
| 10 | - clocks: Should point to the hfclk device tree node and the rtcclk | ||
| 11 | device tree node. The RTC clock here is not a time-of-day clock, | ||
| 12 | but is instead a high-stability clock source for system timers | ||
| 13 | and cycle counters. | ||
| 14 | - #clock-cells: Should be <1> | ||
| 15 | |||
| 16 | The clock consumer should specify the desired clock via the clock ID | ||
| 17 | macros defined in include/dt-bindings/clock/sifive-fu540-prci.h. | ||
| 18 | These macros begin with PRCI_CLK_. | ||
| 19 | |||
| 20 | The hfclk and rtcclk nodes are required, and represent physical | ||
| 21 | crystals or resonators located on the PCB. These nodes should be present | ||
| 22 | underneath /, rather than /soc. | ||
| 23 | |||
| 24 | Examples: | ||
| 25 | |||
| 26 | /* under /, in PCB-specific DT data */ | ||
| 27 | hfclk: hfclk { | ||
| 28 | #clock-cells = <0>; | ||
| 29 | compatible = "fixed-clock"; | ||
| 30 | clock-frequency = <33333333>; | ||
| 31 | clock-output-names = "hfclk"; | ||
| 32 | }; | ||
| 33 | rtcclk: rtcclk { | ||
| 34 | #clock-cells = <0>; | ||
| 35 | compatible = "fixed-clock"; | ||
| 36 | clock-frequency = <1000000>; | ||
| 37 | clock-output-names = "rtcclk"; | ||
| 38 | }; | ||
| 39 | |||
| 40 | /* under /soc, in SoC-specific DT data */ | ||
| 41 | prci: clock-controller@10000000 { | ||
| 42 | compatible = "sifive,fu540-c000-prci"; | ||
| 43 | reg = <0x0 0x10000000 0x0 0x1000>; | ||
| 44 | clocks = <&hfclk>, <&rtcclk>; | ||
| 45 | #clock-cells = <1>; | ||
| 46 | }; | ||
| @@ -11,6 +11,8 @@ Required properties: · 10 unmodified lines | |||
| 11 | "st,stm32f42xx-rcc" | 11 | "st,stm32f42xx-rcc" |
| 12 | "st,stm32f469-rcc" | 12 | "st,stm32f469-rcc" |
| 13 | "st,stm32f746-rcc" | 13 | "st,stm32f746-rcc" |
| 14 | "st,stm32f769-rcc" | ||
| 15 | |||
| 14 | - reg: should be register base and length as documented in the | 16 | - reg: should be register base and length as documented in the |
| 15 | datasheet | 17 | datasheet |
| 16 | - #reset-cells: 1, see below | 18 | - #reset-cells: 1, see below |
| @@ -102,6 +104,10 @@ The secondary index is bound with the following magic numbers: · 85 unmodified lines | |||
| 102 | 28 CLK_I2C3 | 104 | 28 CLK_I2C3 |
| 103 | 29 CLK_I2C4 | 105 | 29 CLK_I2C4 |
| 104 | 30 CLK_LPTIMER (LPTimer1 clock) | 106 | 30 CLK_LPTIMER (LPTimer1 clock) |
| 107 | 31 CLK_PLL_SRC | ||
| 108 | 32 CLK_DFSDM1 | ||
| 109 | 33 CLK_ADFSDM1 | ||
| 110 | 34 CLK_F769_DSI | ||
| 105 | ) | 111 | ) |
| 106 | 112 | ||
| 107 | Example: | 113 | Example: |
| @@ -960,6 +960,12 @@ F: drivers/iio/adc/ltc2497* · 959 unmodified lines | |||
| 960 | X: drivers/iio/*/adjd* | 960 | X: drivers/iio/*/adjd* |
| 961 | F: drivers/staging/iio/*/ad* | 961 | F: drivers/staging/iio/*/ad* |
| 962 | 962 | ||
| 963 | ANALOGBITS PLL LIBRARIES | ||
| 964 | M: Paul Walmsley <paul.walmsley@sifive.com> | ||
| 965 | S: Supported | ||
| 966 | F: drivers/clk/analogbits/* | ||
| 967 | F: include/linux/clk/analogbits* | ||
| 968 | |||
| 963 | ANDES ARCHITECTURE | 969 | ANDES ARCHITECTURE |
| 964 | M: Greentime Hu <green.hu@gmail.com> | 970 | M: Greentime Hu <green.hu@gmail.com> |
| 965 | M: Vincent Chen <deanbo422@gmail.com> | 971 | M: Vincent Chen <deanbo422@gmail.com> |
| @@ -1,3 +1,4 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 1 | 2 | ||
| 2 | config CLKDEV_LOOKUP | 3 | config CLKDEV_LOOKUP |
| 3 | bool | 4 | bool |
| @@ -304,6 +305,7 @@ config COMMON_CLK_FIXED_MMIO · 300 unmodified lines | |||
| 304 | Support for Memory Mapped IO Fixed clocks | 305 | Support for Memory Mapped IO Fixed clocks |
| 305 | 306 | ||
| 306 | source "drivers/clk/actions/Kconfig" | 307 | source "drivers/clk/actions/Kconfig" |
| 308 | source "drivers/clk/analogbits/Kconfig" | ||
| 307 | source "drivers/clk/bcm/Kconfig" | 309 | source "drivers/clk/bcm/Kconfig" |
| 308 | source "drivers/clk/hisilicon/Kconfig" | 310 | source "drivers/clk/hisilicon/Kconfig" |
| 309 | source "drivers/clk/imgtec/Kconfig" | 311 | source "drivers/clk/imgtec/Kconfig" |
| @@ -316,6 +318,7 @@ source "drivers/clk/mvebu/Kconfig" · 6 unmodified lines | |||
| 316 | source "drivers/clk/qcom/Kconfig" | 318 | source "drivers/clk/qcom/Kconfig" |
| 317 | source "drivers/clk/renesas/Kconfig" | 319 | source "drivers/clk/renesas/Kconfig" |
| 318 | source "drivers/clk/samsung/Kconfig" | 320 | source "drivers/clk/samsung/Kconfig" |
| 321 | source "drivers/clk/sifive/Kconfig" | ||
| 319 | source "drivers/clk/sprd/Kconfig" | 322 | source "drivers/clk/sprd/Kconfig" |
| 320 | source "drivers/clk/sunxi/Kconfig" | 323 | source "drivers/clk/sunxi/Kconfig" |
| 321 | source "drivers/clk/sunxi-ng/Kconfig" | 324 | source "drivers/clk/sunxi-ng/Kconfig" |
| @@ -66,6 +66,7 @@ obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o · 65 unmodified lines | |||
| 66 | 66 | ||
| 67 | # please keep this section sorted lexicographically by directory path name | 67 | # please keep this section sorted lexicographically by directory path name |
| 68 | obj-y += actions/ | 68 | obj-y += actions/ |
| 69 | obj-y += analogbits/ | ||
| 69 | obj-$(CONFIG_COMMON_CLK_AT91) += at91/ | 70 | obj-$(CONFIG_COMMON_CLK_AT91) += at91/ |
| 70 | obj-$(CONFIG_ARCH_ARTPEC) += axis/ | 71 | obj-$(CONFIG_ARCH_ARTPEC) += axis/ |
| 71 | obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/ | 72 | obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/ |
| @@ -95,6 +96,7 @@ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ · 23 unmodified lines | |||
| 95 | obj-y += renesas/ | 96 | obj-y += renesas/ |
| 96 | obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ | 97 | obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ |
| 97 | obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/ | 98 | obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/ |
| 99 | obj-$(CONFIG_CLK_SIFIVE) += sifive/ | ||
| 98 | obj-$(CONFIG_ARCH_SIRF) += sirf/ | 100 | obj-$(CONFIG_ARCH_SIRF) += sirf/ |
| 99 | obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ | 101 | obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ |
| 100 | obj-$(CONFIG_PLAT_SPEAR) += spear/ | 102 | obj-$(CONFIG_PLAT_SPEAR) += spear/ |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
| 2 | // | 2 | // |
| 3 | // OWL common clock driver | 3 | // OWL common clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
| 2 | // | 2 | // |
| 3 | // OWL composite clock driver | 3 | // OWL composite clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
| 2 | // | 2 | // |
| 3 | // OWL divider clock driver | 3 | // OWL divider clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
| 2 | // | 2 | // |
| 3 | // OWL factor clock driver | 3 | // OWL factor clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
| 2 | // | 2 | // |
| 3 | // OWL fixed factor clock driver | 3 | // OWL fixed factor clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
| 2 | // | 2 | // |
| 3 | // OWL gate clock driver | 3 | // OWL gate clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
| 2 | // | 2 | // |
| 3 | // OWL mux clock driver | 3 | // OWL mux clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
| 2 | // | 2 | // |
| 3 | // OWL pll clock driver | 3 | // OWL pll clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | // | 2 | // |
| 3 | // Actions Semi Owl SoCs Reset Management Unit driver | 3 | // Actions Semi Owl SoCs Reset Management Unit driver |
| 4 | // | 4 | // |
| @@ -0,0 +1,2 @@ | |||
| 1 | config CLK_ANALOGBITS_WRPLL_CLN28HPC | ||
| 2 | bool | ||
| @@ -0,0 +1,3 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | obj-$(CONFIG_CLK_ANALOGBITS_WRPLL_CLN28HPC) += wrpll-cln28hpc.o | ||
| @@ -0,0 +1,364 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2018-2019 SiFive, Inc. | ||
| 4 | * Wesley Terpstra | ||
| 5 | * Paul Walmsley | ||
| 6 | * | ||
| 7 | * This library supports configuration parsing and reprogramming of | ||
| 8 | * the CLN28HPC variant of the Analog Bits Wide Range PLL. The | ||
| 9 | * intention is for this library to be reusable for any device that | ||
| 10 | * integrates this PLL; thus the register structure and programming | ||
| 11 | * details are expected to be provided by a separate IP block driver. | ||
| 12 | * | ||
| 13 | * The bulk of this code is primarily useful for clock configurations | ||
| 14 | * that must operate at arbitrary rates, as opposed to clock configurations | ||
| 15 | * that are restricted by software or manufacturer guidance to a small, | ||
| 16 | * pre-determined set of performance points. | ||
| 17 | * | ||
| 18 | * References: | ||
| 19 | * - Analog Bits "Wide Range PLL Datasheet", version 2015.10.01 | ||
| 20 | * - SiFive FU540-C000 Manual v1p0, Chapter 7 "Clocking and Reset" | ||
| 21 | * https://static.dev.sifive.com/FU540-C000-v1.0.pdf | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <linux/bug.h> | ||
| 25 | #include <linux/err.h> | ||
| 26 | #include <linux/log2.h> | ||
| 27 | #include <linux/math64.h> | ||
| 28 | #include <linux/clk/analogbits-wrpll-cln28hpc.h> | ||
| 29 | |||
| 30 | /* MIN_INPUT_FREQ: minimum input clock frequency, in Hz (Fref_min) */ | ||
| 31 | #define MIN_INPUT_FREQ 7000000 | ||
| 32 | |||
| 33 | /* MAX_INPUT_FREQ: maximum input clock frequency, in Hz (Fref_max) */ | ||
| 34 | #define MAX_INPUT_FREQ 600000000 | ||
| 35 | |||
| 36 | /* MIN_POST_DIVIDE_REF_FREQ: minimum post-divider reference frequency, in Hz */ | ||
| 37 | #define MIN_POST_DIVR_FREQ 7000000 | ||
| 38 | |||
| 39 | /* MAX_POST_DIVIDE_REF_FREQ: maximum post-divider reference frequency, in Hz */ | ||
| 40 | #define MAX_POST_DIVR_FREQ 200000000 | ||
| 41 | |||
| 42 | /* MIN_VCO_FREQ: minimum VCO frequency, in Hz (Fvco_min) */ | ||
| 43 | #define MIN_VCO_FREQ 2400000000UL | ||
| 44 | |||
| 45 | /* MAX_VCO_FREQ: maximum VCO frequency, in Hz (Fvco_max) */ | ||
| 46 | #define MAX_VCO_FREQ 4800000000ULL | ||
| 47 | |||
| 48 | /* MAX_DIVQ_DIVISOR: maximum output divisor. Selected by DIVQ = 6 */ | ||
| 49 | #define MAX_DIVQ_DIVISOR 64 | ||
| 50 | |||
| 51 | /* MAX_DIVR_DIVISOR: maximum reference divisor. Selected by DIVR = 63 */ | ||
| 52 | #define MAX_DIVR_DIVISOR 64 | ||
| 53 | |||
| 54 | /* MAX_LOCK_US: maximum PLL lock time, in microseconds (tLOCK_max) */ | ||
| 55 | #define MAX_LOCK_US 70 | ||
| 56 | |||
| 57 | /* | ||
| 58 | * ROUND_SHIFT: number of bits to shift to avoid precision loss in the rounding | ||
| 59 | * algorithm | ||
| 60 | */ | ||
| 61 | #define ROUND_SHIFT 20 | ||
| 62 | |||
| 63 | /* | ||
| 64 | * Private functions | ||
| 65 | */ | ||
| 66 | |||
| 67 | /** | ||
| 68 | * __wrpll_calc_filter_range() - determine PLL loop filter bandwidth | ||
| 69 | * @post_divr_freq: input clock rate after the R divider | ||
| 70 | * | ||
| 71 | * Select the value to be presented to the PLL RANGE input signals, based | ||
| 72 | * on the input clock frequency after the post-R-divider @post_divr_freq. | ||
| 73 | * This code follows the recommendations in the PLL datasheet for filter | ||
| 74 | * range selection. | ||
| 75 | * | ||
| 76 | * Return: The RANGE value to be presented to the PLL configuration inputs, | ||
| 77 | * or a negative return code upon error. | ||
| 78 | */ | ||
| 79 | static int __wrpll_calc_filter_range(unsigned long post_divr_freq) | ||
| 80 | { | ||
| 81 | if (post_divr_freq < MIN_POST_DIVR_FREQ || | ||
| 82 | post_divr_freq > MAX_POST_DIVR_FREQ) { | ||
| 83 | WARN(1, "%s: post-divider reference freq out of range: %lu", | ||
| 84 | __func__, post_divr_freq); | ||
| 85 | return -ERANGE; | ||
| 86 | } | ||
| 87 | |||
| 88 | switch (post_divr_freq) { | ||
| 89 | case 0 ... 10999999: | ||
| 90 | return 1; | ||
| 91 | case 11000000 ... 17999999: | ||
| 92 | return 2; | ||
| 93 | case 18000000 ... 29999999: | ||
| 94 | return 3; | ||
| 95 | case 30000000 ... 49999999: | ||
| 96 | return 4; | ||
| 97 | case 50000000 ... 79999999: | ||
| 98 | return 5; | ||
| 99 | case 80000000 ... 129999999: | ||
| 100 | return 6; | ||
| 101 | } | ||
| 102 | |||
| 103 | return 7; | ||
| 104 | } | ||
| 105 | |||
| 106 | /** | ||
| 107 | * __wrpll_calc_fbdiv() - return feedback fixed divide value | ||
| 108 | * @c: ptr to a struct wrpll_cfg record to read from | ||
| 109 | * | ||
| 110 | * The internal feedback path includes a fixed by-two divider; the | ||
| 111 | * external feedback path does not. Return the appropriate divider | ||
| 112 | * value (2 or 1) depending on whether internal or external feedback | ||
| 113 | * is enabled. This code doesn't test for invalid configurations | ||
| 114 | * (e.g. both or neither of WRPLL_FLAGS_*_FEEDBACK are set); it relies | ||
| 115 | * on the caller to do so. | ||
| 116 | * | ||
| 117 | * Context: Any context. Caller must protect the memory pointed to by | ||
| 118 | * @c from simultaneous modification. | ||
| 119 | * | ||
| 120 | * Return: 2 if internal feedback is enabled or 1 if external feedback | ||
| 121 | * is enabled. | ||
| 122 | */ | ||
| 123 | static u8 __wrpll_calc_fbdiv(const struct wrpll_cfg *c) | ||
| 124 | { | ||
| 125 | return (c->flags & WRPLL_FLAGS_INT_FEEDBACK_MASK) ? 2 : 1; | ||
| 126 | } | ||
| 127 | |||
| 128 | /** | ||
| 129 | * __wrpll_calc_divq() - determine DIVQ based on target PLL output clock rate | ||
| 130 | * @target_rate: target PLL output clock rate | ||
| 131 | * @vco_rate: pointer to a u64 to store the computed VCO rate into | ||
| 132 | * | ||
| 133 | * Determine a reasonable value for the PLL Q post-divider, based on the | ||
| 134 | * target output rate @target_rate for the PLL. Along with returning the | ||
| 135 | * computed Q divider value as the return value, this function stores the | ||
| 136 | * desired target VCO rate into the variable pointed to by @vco_rate. | ||
| 137 | * | ||
| 138 | * Context: Any context. Caller must protect the memory pointed to by | ||
| 139 | * @vco_rate from simultaneous access or modification. | ||
| 140 | * | ||
| 141 | * Return: a positive integer DIVQ value to be programmed into the hardware | ||
| 142 | * upon success, or 0 upon error (since 0 is an invalid DIVQ value) | ||
| 143 | */ | ||
| 144 | static u8 __wrpll_calc_divq(u32 target_rate, u64 *vco_rate) | ||
| 145 | { | ||
| 146 | u64 s; | ||
| 147 | u8 divq = 0; | ||
| 148 | |||
| 149 | if (!vco_rate) { | ||
| 150 | WARN_ON(1); | ||
| 151 | goto wcd_out; | ||
| 152 | } | ||
| 153 | |||
| 154 | s = div_u64(MAX_VCO_FREQ, target_rate); | ||
| 155 | if (s <= 1) { | ||
| 156 | divq = 1; | ||
| 157 | *vco_rate = MAX_VCO_FREQ; | ||
| 158 | } else if (s > MAX_DIVQ_DIVISOR) { | ||
| 159 | divq = ilog2(MAX_DIVQ_DIVISOR); | ||
| 160 | *vco_rate = MIN_VCO_FREQ; | ||
| 161 | } else { | ||
| 162 | divq = ilog2(s); | ||
| 163 | *vco_rate = (u64)target_rate << divq; | ||
| 164 | } | ||
| 165 | |||
| 166 | wcd_out: | ||
| 167 | return divq; | ||
| 168 | } | ||
| 169 | |||
| 170 | /** | ||
| 171 | * __wrpll_update_parent_rate() - update PLL data when parent rate changes | ||
| 172 | * @c: ptr to a struct wrpll_cfg record to write PLL data to | ||
| 173 | * @parent_rate: PLL input refclk rate (pre-R-divider) | ||
| 174 | * | ||
| 175 | * Pre-compute some data used by the PLL configuration algorithm when | ||
| 176 | * the PLL's reference clock rate changes. The intention is to avoid | ||
| 177 | * computation when the parent rate remains constant - expected to be | ||
| 178 | * the common case. | ||
| 179 | * | ||
| 180 | * Returns: 0 upon success or -ERANGE if the reference clock rate is | ||
| 181 | * out of range. | ||
| 182 | */ | ||
| 183 | static int __wrpll_update_parent_rate(struct wrpll_cfg *c, | ||
| 184 | unsigned long parent_rate) | ||
| 185 | { | ||
| 186 | u8 max_r_for_parent; | ||
| 187 | |||
| 188 | if (parent_rate > MAX_INPUT_FREQ || parent_rate < MIN_POST_DIVR_FREQ) | ||
| 189 | return -ERANGE; | ||
| 190 | |||
| 191 | c->parent_rate = parent_rate; | ||
| 192 | max_r_for_parent = div_u64(parent_rate, MIN_POST_DIVR_FREQ); | ||
| 193 | c->max_r = min_t(u8, MAX_DIVR_DIVISOR, max_r_for_parent); | ||
| 194 | |||
| 195 | c->init_r = DIV_ROUND_UP_ULL(parent_rate, MAX_POST_DIVR_FREQ); | ||
| 196 | |||
| 197 | return 0; | ||
| 198 | } | ||
| 199 | |||
| 200 | /** | ||
| 201 | * wrpll_configure() - compute PLL configuration for a target rate | ||
| 202 | * @c: ptr to a struct wrpll_cfg record to write into | ||
| 203 | * @target_rate: target PLL output clock rate (post-Q-divider) | ||
| 204 | * @parent_rate: PLL input refclk rate (pre-R-divider) | ||
| 205 | * | ||
| 206 | * Compute the appropriate PLL signal configuration values and store | ||
| 207 | * in PLL context @c. PLL reprogramming is not glitchless, so the | ||
| 208 | * caller should switch any downstream logic to a different clock | ||
| 209 | * source or clock-gate it before presenting these values to the PLL | ||
| 210 | * configuration signals. | ||
| 211 | * | ||
| 212 | * The caller must pass this function a pre-initialized struct | ||
| 213 | * wrpll_cfg record: either initialized to zero (with the | ||
| 214 | * exception of the .name and .flags fields) or read from the PLL. | ||
| 215 | * | ||
| 216 | * Context: Any context. Caller must protect the memory pointed to by @c | ||
| 217 | * from simultaneous access or modification. | ||
| 218 | * | ||
| 219 | * Return: 0 upon success; anything else upon failure. | ||
| 220 | */ | ||
| 221 | int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate, | ||
| 222 | unsigned long parent_rate) | ||
| 223 | { | ||
| 224 | unsigned long ratio; | ||
| 225 | u64 target_vco_rate, delta, best_delta, f_pre_div, vco, vco_pre; | ||
| 226 | u32 best_f, f, post_divr_freq; | ||
| 227 | u8 fbdiv, divq, best_r, r; | ||
| 228 | int range; | ||
| 229 | |||
| 230 | if (c->flags == 0) { | ||
| 231 | WARN(1, "%s called with uninitialized PLL config", __func__); | ||
| 232 | return -EINVAL; | ||
| 233 | } | ||
| 234 | |||
| 235 | /* Initialize rounding data if it hasn't been initialized already */ | ||
| 236 | if (parent_rate != c->parent_rate) { | ||
| 237 | if (__wrpll_update_parent_rate(c, parent_rate)) { | ||
| 238 | pr_err("%s: PLL input rate is out of range\n", | ||
| 239 | __func__); | ||
| 240 | return -ERANGE; | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | c->flags &= ~WRPLL_FLAGS_RESET_MASK; | ||
| 245 | |||
| 246 | /* Put the PLL into bypass if the user requests the parent clock rate */ | ||
| 247 | if (target_rate == parent_rate) { | ||
| 248 | c->flags |= WRPLL_FLAGS_BYPASS_MASK; | ||
| 249 | return 0; | ||
| 250 | } | ||
| 251 | |||
| 252 | c->flags &= ~WRPLL_FLAGS_BYPASS_MASK; | ||
| 253 | |||
| 254 | /* Calculate the Q shift and target VCO rate */ | ||
| 255 | divq = __wrpll_calc_divq(target_rate, &target_vco_rate); | ||
| 256 | if (!divq) | ||
| 257 | return -1; | ||
| 258 | c->divq = divq; | ||
| 259 | |||
| 260 | /* Precalculate the pre-Q divider target ratio */ | ||
| 261 | ratio = div64_u64((target_vco_rate << ROUND_SHIFT), parent_rate); | ||
| 262 | |||
| 263 | fbdiv = __wrpll_calc_fbdiv(c); | ||
| 264 | best_r = 0; | ||
| 265 | best_f = 0; | ||
| 266 | best_delta = MAX_VCO_FREQ; | ||
| 267 | |||
| 268 | /* | ||
| 269 | * Consider all values for R which land within | ||
| 270 | * [MIN_POST_DIVR_FREQ, MAX_POST_DIVR_FREQ]; prefer smaller R | ||
| 271 | */ | ||
| 272 | for (r = c->init_r; r <= c->max_r; ++r) { | ||
| 273 | f_pre_div = ratio * r; | ||
| 274 | f = (f_pre_div + (1 << ROUND_SHIFT)) >> ROUND_SHIFT; | ||
| 275 | f >>= (fbdiv - 1); | ||
| 276 | |||
| 277 | post_divr_freq = div_u64(parent_rate, r); | ||
| 278 | vco_pre = fbdiv * post_divr_freq; | ||
| 279 | vco = vco_pre * f; | ||
| 280 | |||
| 281 | /* Ensure rounding didn't take us out of range */ | ||
| 282 | if (vco > target_vco_rate) { | ||
| 283 | --f; | ||
| 284 | vco = vco_pre * f; | ||
| 285 | } else if (vco < MIN_VCO_FREQ) { | ||
| 286 | ++f; | ||
| 287 | vco = vco_pre * f; | ||
| 288 | } | ||
| 289 | |||
| 290 | delta = abs(target_rate - vco); | ||
| 291 | if (delta < best_delta) { | ||
| 292 | best_delta = delta; | ||
| 293 | best_r = r; | ||
| 294 | best_f = f; | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | c->divr = best_r - 1; | ||
| 299 | c->divf = best_f - 1; | ||
| 300 | |||
| 301 | post_divr_freq = div_u64(parent_rate, best_r); | ||
| 302 | |||
| 303 | /* Pick the best PLL jitter filter */ | ||
| 304 | range = __wrpll_calc_filter_range(post_divr_freq); | ||
| 305 | if (range < 0) | ||
| 306 | return range; | ||
| 307 | c->range = range; | ||
| 308 | |||
| 309 | return 0; | ||
| 310 | } | ||
| 311 | |||
| 312 | /** | ||
| 313 | * wrpll_calc_output_rate() - calculate the PLL's target output rate | ||
| 314 | * @c: ptr to a struct wrpll_cfg record to read from | ||
| 315 | * @parent_rate: PLL refclk rate | ||
| 316 | * | ||
| 317 | * Given a pointer to the PLL's current input configuration @c and the | ||
| 318 | * PLL's input reference clock rate @parent_rate (before the R | ||
| 319 | * pre-divider), calculate the PLL's output clock rate (after the Q | ||
| 320 | * post-divider). | ||
| 321 | * | ||
| 322 | * Context: Any context. Caller must protect the memory pointed to by @c | ||
| 323 | * from simultaneous modification. | ||
| 324 | * | ||
| 325 | * Return: the PLL's output clock rate, in Hz. The return value from | ||
| 326 | * this function is intended to be convenient to pass directly | ||
| 327 | * to the Linux clock framework; thus there is no explicit | ||
| 328 | * error return value. | ||
| 329 | */ | ||
| 330 | unsigned long wrpll_calc_output_rate(const struct wrpll_cfg *c, | ||
| 331 | unsigned long parent_rate) | ||
| 332 | { | ||
| 333 | u8 fbdiv; | ||
| 334 | u64 n; | ||
| 335 | |||
| 336 | if (c->flags & WRPLL_FLAGS_EXT_FEEDBACK_MASK) { | ||
| 337 | WARN(1, "external feedback mode not yet supported"); | ||
| 338 | return ULONG_MAX; | ||
| 339 | } | ||
| 340 | |||
| 341 | fbdiv = __wrpll_calc_fbdiv(c); | ||
| 342 | n = parent_rate * fbdiv * (c->divf + 1); | ||
| 343 | n = div_u64(n, c->divr + 1); | ||
| 344 | n >>= c->divq; | ||
| 345 | |||
| 346 | return n; | ||
| 347 | } | ||
| 348 | |||
| 349 | /** | ||
| 350 | * wrpll_calc_max_lock_us() - return the time for the PLL to lock | ||
| 351 | * @c: ptr to a struct wrpll_cfg record to read from | ||
| 352 | * | ||
| 353 | * Return the minimum amount of time (in microseconds) that the caller | ||
| 354 | * must wait after reprogramming the PLL to ensure that it is locked | ||
| 355 | * to the input frequency and stable. This is likely to depend on the DIVR | ||
| 356 | * value; this is under discussion with the manufacturer. | ||
| 357 | * | ||
| 358 | * Return: the minimum amount of time the caller must wait for the PLL | ||
| 359 | * to lock (in microseconds) | ||
| 360 | */ | ||
| 361 | unsigned int wrpll_calc_max_lock_us(const struct wrpll_cfg *c) | ||
| 362 | { | ||
| 363 | return MAX_LOCK_US; | ||
| 364 | } | ||
| @@ -14,6 +14,8 @@ obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o · 13 unmodified lines | |||
| 14 | obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o | 14 | obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o |
| 15 | obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o | 15 | obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o |
| 16 | obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o | 16 | obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o |
| 17 | obj-$(CONFIG_HAVE_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o | ||
| 17 | obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o | 18 | obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o |
| 19 | obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o | ||
| 18 | obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o | 20 | obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o |
| 19 | obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o | 21 | obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o |
| @@ -41,7 +41,7 @@ static u8 sam9260_plla_out[] = { 0, 2 }; · 40 unmodified lines | |||
| 41 | 41 | ||
| 42 | static u16 sam9260_plla_icpll[] = { 1, 1 }; | 42 | static u16 sam9260_plla_icpll[] = { 1, 1 }; |
| 43 | 43 | ||
| 44 | static struct clk_range sam9260_plla_outputs[] = { | 44 | static const struct clk_range sam9260_plla_outputs[] = { |
| 45 | { .min = 80000000, .max = 160000000 }, | 45 | { .min = 80000000, .max = 160000000 }, |
| 46 | { .min = 150000000, .max = 240000000 }, | 46 | { .min = 150000000, .max = 240000000 }, |
| 47 | }; | 47 | }; |
| @@ -58,7 +58,7 @@ static u8 sam9260_pllb_out[] = { 1 }; · 10 unmodified lines | |||
| 58 | 58 | ||
| 59 | static u16 sam9260_pllb_icpll[] = { 1 }; | 59 | static u16 sam9260_pllb_icpll[] = { 1 }; |
| 60 | 60 | ||
| 61 | static struct clk_range sam9260_pllb_outputs[] = { | 61 | static const struct clk_range sam9260_pllb_outputs[] = { |
| 62 | { .min = 70000000, .max = 130000000 }, | 62 | { .min = 70000000, .max = 130000000 }, |
| 63 | }; | 63 | }; |
| 64 | 64 | ||
| @@ -128,7 +128,7 @@ static u8 sam9g20_plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 }; · 63 unmodified lines | |||
| 128 | 128 | ||
| 129 | static u16 sam9g20_plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 }; | 129 | static u16 sam9g20_plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 }; |
| 130 | 130 | ||
| 131 | static struct clk_range sam9g20_plla_outputs[] = { | 131 | static const struct clk_range sam9g20_plla_outputs[] = { |
| 132 | { .min = 745000000, .max = 800000000 }, | 132 | { .min = 745000000, .max = 800000000 }, |
| 133 | { .min = 695000000, .max = 750000000 }, | 133 | { .min = 695000000, .max = 750000000 }, |
| 134 | { .min = 645000000, .max = 700000000 }, | 134 | { .min = 645000000, .max = 700000000 }, |
| @@ -151,7 +151,7 @@ static u8 sam9g20_pllb_out[] = { 0 }; · 16 unmodified lines | |||
| 151 | 151 | ||
| 152 | static u16 sam9g20_pllb_icpll[] = { 0 }; | 152 | static u16 sam9g20_pllb_icpll[] = { 0 }; |
| 153 | 153 | ||
| 154 | static struct clk_range sam9g20_pllb_outputs[] = { | 154 | static const struct clk_range sam9g20_pllb_outputs[] = { |
| 155 | { .min = 30000000, .max = 100000000 }, | 155 | { .min = 30000000, .max = 100000000 }, |
| 156 | }; | 156 | }; |
| 157 | 157 | ||
| @@ -182,7 +182,7 @@ static const struct clk_master_characteristics sam9261_mck_characteristics = { · 24 unmodified lines | |||
| 182 | .divisors = { 1, 2, 4, 0 }, | 182 | .divisors = { 1, 2, 4, 0 }, |
| 183 | }; | 183 | }; |
| 184 | 184 | ||
| 185 | static struct clk_range sam9261_plla_outputs[] = { | 185 | static const struct clk_range sam9261_plla_outputs[] = { |
| 186 | { .min = 80000000, .max = 200000000 }, | 186 | { .min = 80000000, .max = 200000000 }, |
| 187 | { .min = 190000000, .max = 240000000 }, | 187 | { .min = 190000000, .max = 240000000 }, |
| 188 | }; | 188 | }; |
| @@ -199,7 +199,7 @@ static u8 sam9261_pllb_out[] = { 1 }; · 10 unmodified lines | |||
| 199 | 199 | ||
| 200 | static u16 sam9261_pllb_icpll[] = { 1 }; | 200 | static u16 sam9261_pllb_icpll[] = { 1 }; |
| 201 | 201 | ||
| 202 | static struct clk_range sam9261_pllb_outputs[] = { | 202 | static const struct clk_range sam9261_pllb_outputs[] = { |
| 203 | { .min = 70000000, .max = 130000000 }, | 203 | { .min = 70000000, .max = 130000000 }, |
| 204 | }; | 204 | }; |
| 205 | 205 | ||
| @@ -262,7 +262,7 @@ static const struct clk_master_characteristics sam9263_mck_characteristics = { · 56 unmodified lines | |||
| 262 | .divisors = { 1, 2, 4, 0 }, | 262 | .divisors = { 1, 2, 4, 0 }, |
| 263 | }; | 263 | }; |
| 264 | 264 | ||
| 265 | static struct clk_range sam9263_pll_outputs[] = { | 265 | static const struct clk_range sam9263_pll_outputs[] = { |
| 266 | { .min = 80000000, .max = 200000000 }, | 266 | { .min = 80000000, .max = 200000000 }, |
| 267 | { .min = 190000000, .max = 240000000 }, | 267 | { .min = 190000000, .max = 240000000 }, |
| 268 | }; | 268 | }; |
| @@ -14,7 +14,7 @@ static const struct clk_master_characteristics sam9rl_mck_characteristics = { · 13 unmodified lines | |||
| 14 | 14 | ||
| 15 | static u8 sam9rl_plla_out[] = { 0, 2 }; | 15 | static u8 sam9rl_plla_out[] = { 0, 2 }; |
| 16 | 16 | ||
| 17 | static struct clk_range sam9rl_plla_outputs[] = { | 17 | static const struct clk_range sam9rl_plla_outputs[] = { |
| 18 | { .min = 80000000, .max = 200000000 }, | 18 | { .min = 80000000, .max = 200000000 }, |
| 19 | { .min = 190000000, .max = 240000000 }, | 19 | { .min = 190000000, .max = 240000000 }, |
| 20 | }; | 20 | }; |
| @@ -17,7 +17,7 @@ static u8 plla_out[] = { 0, 1, 2, 3, 0, 1, 2, 3 }; · 16 unmodified lines | |||
| 17 | 17 | ||
| 18 | static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 }; | 18 | static u16 plla_icpll[] = { 0, 0, 0, 0, 1, 1, 1, 1 }; |
| 19 | 19 | ||
| 20 | static struct clk_range plla_outputs[] = { | 20 | static const struct clk_range plla_outputs[] = { |
| 21 | { .min = 745000000, .max = 800000000 }, | 21 | { .min = 745000000, .max = 800000000 }, |
| 22 | { .min = 695000000, .max = 750000000 }, | 22 | { .min = 695000000, .max = 750000000 }, |
| 23 | { .min = 645000000, .max = 700000000 }, | 23 | { .min = 645000000, .max = 700000000 }, |
| @@ -49,6 +49,13 @@ static const struct { · 25 unmodified lines | |||
| 49 | { .n = "pck1", .p = "prog1", .id = 9 }, | 49 | { .n = "pck1", .p = "prog1", .id = 9 }, |
| 50 | }; | 50 | }; |
| 51 | 51 | ||
| 52 | static const struct clk_pcr_layout at91sam9x5_pcr_layout = { | ||
| 53 | .offset = 0x10c, | ||
| 54 | .cmd = BIT(12), | ||
| 55 | .pid_mask = GENMASK(5, 0), | ||
| 56 | .div_mask = GENMASK(17, 16), | ||
| 57 | }; | ||
| 58 | |||
| 52 | struct pck { | 59 | struct pck { |
| 53 | char *n; | 60 | char *n; |
| 54 | u8 id; | 61 | u8 id; |
| @@ -242,6 +249,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, · 187 unmodified lines | |||
| 242 | 249 | ||
| 243 | for (i = 0; i < ARRAY_SIZE(at91sam9x5_periphck); i++) { | 250 | for (i = 0; i < ARRAY_SIZE(at91sam9x5_periphck); i++) { |
| 244 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, | 251 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, |
| 252 | &at91sam9x5_pcr_layout, | ||
| 245 | at91sam9x5_periphck[i].n, | 253 | at91sam9x5_periphck[i].n, |
| 246 | "masterck", | 254 | "masterck", |
| 247 | at91sam9x5_periphck[i].id, | 255 | at91sam9x5_periphck[i].id, |
| @@ -254,6 +262,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, · 6 unmodified lines | |||
| 254 | 262 | ||
| 255 | for (i = 0; extra_pcks[i].id; i++) { | 263 | for (i = 0; extra_pcks[i].id; i++) { |
| 256 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, | 264 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, |
| 265 | &at91sam9x5_pcr_layout, | ||
| 257 | extra_pcks[i].n, | 266 | extra_pcks[i].n, |
| 258 | "masterck", | 267 | "masterck", |
| 259 | extra_pcks[i].id, | 268 | extra_pcks[i].id, |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | |||
| 11 | * | 11 | * |
| 12 | */ | 12 | */ |
| 13 | 13 | ||
| 14 | #include <linux/bitfield.h> | ||
| 14 | #include <linux/clk-provider.h> | 15 | #include <linux/clk-provider.h> |
| 15 | #include <linux/clkdev.h> | 16 | #include <linux/clkdev.h> |
| 16 | #include <linux/clk/at91_pmc.h> | 17 | #include <linux/clk/at91_pmc.h> |
| @@ -31,6 +32,7 @@ struct clk_generated { · 14 unmodified lines | |||
| 31 | spinlock_t *lock; | 32 | spinlock_t *lock; |
| 32 | u32 id; | 33 | u32 id; |
| 33 | u32 gckdiv; | 34 | u32 gckdiv; |
| 35 | const struct clk_pcr_layout *layout; | ||
| 34 | u8 parent_id; | 36 | u8 parent_id; |
| 35 | bool audio_pll_allowed; | 37 | bool audio_pll_allowed; |
| 36 | }; | 38 | }; |
| @@ -47,14 +49,14 @@ static int clk_generated_enable(struct clk_hw *hw) · 10 unmodified lines | |||
| 47 | __func__, gck->gckdiv, gck->parent_id); | 49 | __func__, gck->gckdiv, gck->parent_id); |
| 48 | 50 | ||
| 49 | spin_lock_irqsave(gck->lock, flags); | 51 | spin_lock_irqsave(gck->lock, flags); |
| 50 | regmap_write(gck->regmap, AT91_PMC_PCR, | 52 | regmap_write(gck->regmap, gck->layout->offset, |
| 51 | (gck->id & AT91_PMC_PCR_PID_MASK)); | 53 | (gck->id & gck->layout->pid_mask)); |
| 52 | regmap_update_bits(gck->regmap, AT91_PMC_PCR, | 54 | regmap_update_bits(gck->regmap, gck->layout->offset, |
| 53 | AT91_PMC_PCR_GCKDIV_MASK | AT91_PMC_PCR_GCKCSS_MASK | | 55 | AT91_PMC_PCR_GCKDIV_MASK | gck->layout->gckcss_mask | |
| 54 | AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN, | 56 | gck->layout->cmd | AT91_PMC_PCR_GCKEN, |
| 55 | AT91_PMC_PCR_GCKCSS(gck->parent_id) | | 57 | field_prep(gck->layout->gckcss_mask, gck->parent_id) | |
| 56 | AT91_PMC_PCR_CMD | | 58 | gck->layout->cmd | |
| 57 | AT91_PMC_PCR_GCKDIV(gck->gckdiv) | | 59 | FIELD_PREP(AT91_PMC_PCR_GCKDIV_MASK, gck->gckdiv) | |
| 58 | AT91_PMC_PCR_GCKEN); | 60 | AT91_PMC_PCR_GCKEN); |
| 59 | spin_unlock_irqrestore(gck->lock, flags); | 61 | spin_unlock_irqrestore(gck->lock, flags); |
| 60 | return 0; | 62 | return 0; |
| @@ -66,11 +68,11 @@ static void clk_generated_disable(struct clk_hw *hw) · 5 unmodified lines | |||
| 66 | unsigned long flags; | 68 | unsigned long flags; |
| 67 | 69 | ||
| 68 | spin_lock_irqsave(gck->lock, flags); | 70 | spin_lock_irqsave(gck->lock, flags); |
| 69 | regmap_write(gck->regmap, AT91_PMC_PCR, | 71 | regmap_write(gck->regmap, gck->layout->offset, |
| 70 | (gck->id & AT91_PMC_PCR_PID_MASK)); | 72 | (gck->id & gck->layout->pid_mask)); |
| 71 | regmap_update_bits(gck->regmap, AT91_PMC_PCR, | 73 | regmap_update_bits(gck->regmap, gck->layout->offset, |
| 72 | AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN, | 74 | gck->layout->cmd | AT91_PMC_PCR_GCKEN, |
| 73 | AT91_PMC_PCR_CMD); | 75 | gck->layout->cmd); |
| 74 | spin_unlock_irqrestore(gck->lock, flags); | 76 | spin_unlock_irqrestore(gck->lock, flags); |
| 75 | } | 77 | } |
| 76 | 78 | ||
| @@ -81,9 +83,9 @@ static int clk_generated_is_enabled(struct clk_hw *hw) · 4 unmodified lines | |||
| 81 | unsigned int status; | 83 | unsigned int status; |
| 82 | 84 | ||
| 83 | spin_lock_irqsave(gck->lock, flags); | 85 | spin_lock_irqsave(gck->lock, flags); |
| 84 | regmap_write(gck->regmap, AT91_PMC_PCR, | 86 | regmap_write(gck->regmap, gck->layout->offset, |
| 85 | (gck->id & AT91_PMC_PCR_PID_MASK)); | 87 | (gck->id & gck->layout->pid_mask)); |
| 86 | regmap_read(gck->regmap, AT91_PMC_PCR, &status); | 88 | regmap_read(gck->regmap, gck->layout->offset, &status); |
| 87 | spin_unlock_irqrestore(gck->lock, flags); | 89 | spin_unlock_irqrestore(gck->lock, flags); |
| 88 | 90 | ||
| 89 | return status & AT91_PMC_PCR_GCKEN ? 1 : 0; | 91 | return status & AT91_PMC_PCR_GCKEN ? 1 : 0; |
| @@ -259,19 +261,18 @@ static void clk_generated_startup(struct clk_generated *gck) · 169 unmodified lines | |||
| 259 | unsigned long flags; | 261 | unsigned long flags; |
| 260 | 262 | ||
| 261 | spin_lock_irqsave(gck->lock, flags); | 263 | spin_lock_irqsave(gck->lock, flags); |
| 262 | regmap_write(gck->regmap, AT91_PMC_PCR, | 264 | regmap_write(gck->regmap, gck->layout->offset, |
| 263 | (gck->id & AT91_PMC_PCR_PID_MASK)); | 265 | (gck->id & gck->layout->pid_mask)); |
| 264 | regmap_read(gck->regmap, AT91_PMC_PCR, &tmp); | 266 | regmap_read(gck->regmap, gck->layout->offset, &tmp); |
| 265 | spin_unlock_irqrestore(gck->lock, flags); | 267 | spin_unlock_irqrestore(gck->lock, flags); |
| 266 | 268 | ||
| 267 | gck->parent_id = (tmp & AT91_PMC_PCR_GCKCSS_MASK) | 269 | gck->parent_id = field_get(gck->layout->gckcss_mask, tmp); |
| 268 | >> AT91_PMC_PCR_GCKCSS_OFFSET; | 270 | gck->gckdiv = FIELD_GET(AT91_PMC_PCR_GCKDIV_MASK, tmp); |
| 269 | gck->gckdiv = (tmp & AT91_PMC_PCR_GCKDIV_MASK) | ||
| 270 | >> AT91_PMC_PCR_GCKDIV_OFFSET; | ||
| 271 | } | 271 | } |
| 272 | 272 | ||
| 273 | struct clk_hw * __init | 273 | struct clk_hw * __init |
| 274 | at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, | 274 | at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, |
| 275 | const struct clk_pcr_layout *layout, | ||
| 275 | const char *name, const char **parent_names, | 276 | const char *name, const char **parent_names, |
| 276 | u8 num_parents, u8 id, bool pll_audio, | 277 | u8 num_parents, u8 id, bool pll_audio, |
| 277 | const struct clk_range *range) | 278 | const struct clk_range *range) |
| @@ -298,6 +299,7 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, · 20 unmodified lines | |||
| 298 | gck->lock = lock; | 299 | gck->lock = lock; |
| 299 | gck->range = *range; | 300 | gck->range = *range; |
| 300 | gck->audio_pll_allowed = pll_audio; | 301 | gck->audio_pll_allowed = pll_audio; |
| 302 | gck->layout = layout; | ||
| 301 | 303 | ||
| 302 | clk_generated_startup(gck); | 304 | clk_generated_startup(gck); |
| 303 | hw = &gck->hw; | 305 | hw = &gck->hw; |
| @@ -29,6 +29,7 @@ struct clk_master { · 28 unmodified lines | |||
| 29 | struct regmap *regmap; | 29 | struct regmap *regmap; |
| 30 | const struct clk_master_layout *layout; | 30 | const struct clk_master_layout *layout; |
| 31 | const struct clk_master_characteristics *characteristics; | 31 | const struct clk_master_characteristics *characteristics; |
| 32 | u32 mckr; | ||
| 32 | }; | 33 | }; |
| 33 | 34 | ||
| 34 | static inline bool clk_master_ready(struct regmap *regmap) | 35 | static inline bool clk_master_ready(struct regmap *regmap) |
| @@ -69,7 +70,7 @@ static unsigned long clk_master_recalc_rate(struct clk_hw *hw, · 34 unmodified lines | |||
| 69 | master->characteristics; | 70 | master->characteristics; |
| 70 | unsigned int mckr; | 71 | unsigned int mckr; |
| 71 | 72 | ||
| 72 | regmap_read(master->regmap, AT91_PMC_MCKR, &mckr); | 73 | regmap_read(master->regmap, master->layout->offset, &mckr); |
| 73 | mckr &= layout->mask; | 74 | mckr &= layout->mask; |
| 74 | 75 | ||
| 75 | pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK; | 76 | pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK; |
| @@ -95,7 +96,7 @@ static u8 clk_master_get_parent(struct clk_hw *hw) · 19 unmodified lines | |||
| 95 | struct clk_master *master = to_clk_master(hw); | 96 | struct clk_master *master = to_clk_master(hw); |
| 96 | unsigned int mckr; | 97 | unsigned int mckr; |
| 97 | 98 | ||
| 98 | regmap_read(master->regmap, AT91_PMC_MCKR, &mckr); | 99 | regmap_read(master->regmap, master->layout->offset, &mckr); |
| 99 | 100 | ||
| 100 | return mckr & AT91_PMC_CSS; | 101 | return mckr & AT91_PMC_CSS; |
| 101 | } | 102 | } |
| @@ -147,13 +148,14 @@ at91_clk_register_master(struct regmap *regmap, · 45 unmodified lines | |||
| 147 | return hw; | 148 | return hw; |
| 148 | } | 149 | } |
| 149 | 150 | ||
| 150 | |||
| 151 | const struct clk_master_layout at91rm9200_master_layout = { | 151 | const struct clk_master_layout at91rm9200_master_layout = { |
| 152 | .mask = 0x31F, | 152 | .mask = 0x31F, |
| 153 | .pres_shift = 2, | 153 | .pres_shift = 2, |
| 154 | .offset = AT91_PMC_MCKR, | ||
| 154 | }; | 155 | }; |
| 155 | 156 | ||
| 156 | const struct clk_master_layout at91sam9x5_master_layout = { | 157 | const struct clk_master_layout at91sam9x5_master_layout = { |
| 157 | .mask = 0x373, | 158 | .mask = 0x373, |
| 158 | .pres_shift = 4, | 159 | .pres_shift = 4, |
| 160 | .offset = AT91_PMC_MCKR, | ||
| 159 | }; | 161 | }; |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | |||
| 8 | * | 8 | * |
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | #include <linux/bitops.h> | ||
| 11 | #include <linux/clk-provider.h> | 12 | #include <linux/clk-provider.h> |
| 12 | #include <linux/clkdev.h> | 13 | #include <linux/clkdev.h> |
| 13 | #include <linux/clk/at91_pmc.h> | 14 | #include <linux/clk/at91_pmc.h> |
| @@ -23,9 +24,6 @@ DEFINE_SPINLOCK(pmc_pcr_lock); · 9 unmodified lines | |||
| 23 | #define PERIPHERAL_ID_MAX 31 | 24 | #define PERIPHERAL_ID_MAX 31 |
| 24 | #define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) | 25 | #define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) |
| 25 | 26 | ||
| 26 | #define PERIPHERAL_RSHIFT_MASK 0x3 | ||
| 27 | #define PERIPHERAL_RSHIFT(val) (((val) >> 16) & PERIPHERAL_RSHIFT_MASK) | ||
| 28 | |||
| 29 | #define PERIPHERAL_MAX_SHIFT 3 | 27 | #define PERIPHERAL_MAX_SHIFT 3 |
| 30 | 28 | ||
| 31 | struct clk_peripheral { | 29 | struct clk_peripheral { |
| @@ -43,6 +41,7 @@ struct clk_sam9x5_peripheral { · 11 unmodified lines | |||
| 43 | spinlock_t *lock; | 41 | spinlock_t *lock; |
| 44 | u32 id; | 42 | u32 id; |
| 45 | u32 div; | 43 | u32 div; |
| 44 | const struct clk_pcr_layout *layout; | ||
| 46 | bool auto_div; | 45 | bool auto_div; |
| 47 | }; | 46 | }; |
| 48 | 47 | ||
| @@ -169,13 +168,13 @@ static int clk_sam9x5_peripheral_enable(struct clk_hw *hw) · 120 unmodified lines | |||
| 169 | return 0; | 168 | return 0; |
| 170 | 169 | ||
| 171 | spin_lock_irqsave(periph->lock, flags); | 170 | spin_lock_irqsave(periph->lock, flags); |
| 172 | regmap_write(periph->regmap, AT91_PMC_PCR, | 171 | regmap_write(periph->regmap, periph->layout->offset, |
| 173 | (periph->id & AT91_PMC_PCR_PID_MASK)); | 172 | (periph->id & periph->layout->pid_mask)); |
| 174 | regmap_update_bits(periph->regmap, AT91_PMC_PCR, | 173 | regmap_update_bits(periph->regmap, periph->layout->offset, |
| 175 | AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD | | 174 | periph->layout->div_mask | periph->layout->cmd | |
| 176 | AT91_PMC_PCR_EN, | 175 | AT91_PMC_PCR_EN, |
| 177 | AT91_PMC_PCR_DIV(periph->div) | | 176 | field_prep(periph->layout->div_mask, periph->div) | |
| 178 | AT91_PMC_PCR_CMD | | 177 | periph->layout->cmd | |
| 179 | AT91_PMC_PCR_EN); | 178 | AT91_PMC_PCR_EN); |
| 180 | spin_unlock_irqrestore(periph->lock, flags); | 179 | spin_unlock_irqrestore(periph->lock, flags); |
| 181 | 180 | ||
| @@ -191,11 +190,11 @@ static void clk_sam9x5_peripheral_disable(struct clk_hw *hw) · 9 unmodified lines | |||
| 191 | return; | 190 | return; |
| 192 | 191 | ||
| 193 | spin_lock_irqsave(periph->lock, flags); | 192 | spin_lock_irqsave(periph->lock, flags); |
| 194 | regmap_write(periph->regmap, AT91_PMC_PCR, | 193 | regmap_write(periph->regmap, periph->layout->offset, |
| 195 | (periph->id & AT91_PMC_PCR_PID_MASK)); | 194 | (periph->id & periph->layout->pid_mask)); |
| 196 | regmap_update_bits(periph->regmap, AT91_PMC_PCR, | 195 | regmap_update_bits(periph->regmap, periph->layout->offset, |
| 197 | AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD, | 196 | AT91_PMC_PCR_EN | periph->layout->cmd, |
| 198 | AT91_PMC_PCR_CMD); | 197 | periph->layout->cmd); |
| 199 | spin_unlock_irqrestore(periph->lock, flags); | 198 | spin_unlock_irqrestore(periph->lock, flags); |
| 200 | } | 199 | } |
| 201 | 200 | ||
| @@ -209,9 +208,9 @@ static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw) · 7 unmodified lines | |||
| 209 | return 1; | 208 | return 1; |
| 210 | 209 | ||
| 211 | spin_lock_irqsave(periph->lock, flags); | 210 | spin_lock_irqsave(periph->lock, flags); |
| 212 | regmap_write(periph->regmap, AT91_PMC_PCR, | 211 | regmap_write(periph->regmap, periph->layout->offset, |
| 213 | (periph->id & AT91_PMC_PCR_PID_MASK)); | 212 | (periph->id & periph->layout->pid_mask)); |
| 214 | regmap_read(periph->regmap, AT91_PMC_PCR, &status); | 213 | regmap_read(periph->regmap, periph->layout->offset, &status); |
| 215 | spin_unlock_irqrestore(periph->lock, flags); | 214 | spin_unlock_irqrestore(periph->lock, flags); |
| 216 | 215 | ||
| 217 | return status & AT91_PMC_PCR_EN ? 1 : 0; | 216 | return status & AT91_PMC_PCR_EN ? 1 : 0; |
| @@ -229,13 +228,13 @@ clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw, · 11 unmodified lines | |||
| 229 | return parent_rate; | 228 | return parent_rate; |
| 230 | 229 | ||
| 231 | spin_lock_irqsave(periph->lock, flags); | 230 | spin_lock_irqsave(periph->lock, flags); |
| 232 | regmap_write(periph->regmap, AT91_PMC_PCR, | 231 | regmap_write(periph->regmap, periph->layout->offset, |
| 233 | (periph->id & AT91_PMC_PCR_PID_MASK)); | 232 | (periph->id & periph->layout->pid_mask)); |
| 234 | regmap_read(periph->regmap, AT91_PMC_PCR, &status); | 233 | regmap_read(periph->regmap, periph->layout->offset, &status); |
| 235 | spin_unlock_irqrestore(periph->lock, flags); | 234 | spin_unlock_irqrestore(periph->lock, flags); |
| 236 | 235 | ||
| 237 | if (status & AT91_PMC_PCR_EN) { | 236 | if (status & AT91_PMC_PCR_EN) { |
| 238 | periph->div = PERIPHERAL_RSHIFT(status); | 237 | periph->div = field_get(periph->layout->div_mask, status); |
| 239 | periph->auto_div = false; | 238 | periph->auto_div = false; |
| 240 | } else { | 239 | } else { |
| 241 | clk_sam9x5_peripheral_autodiv(periph); | 240 | clk_sam9x5_peripheral_autodiv(periph); |
| @@ -328,6 +327,7 @@ static const struct clk_ops sam9x5_peripheral_ops = { · 86 unmodified lines | |||
| 328 | 327 | ||
| 329 | struct clk_hw * __init | 328 | struct clk_hw * __init |
| 330 | at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, | 329 | at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, |
| 330 | const struct clk_pcr_layout *layout, | ||
| 331 | const char *name, const char *parent_name, | 331 | const char *name, const char *parent_name, |
| 332 | u32 id, const struct clk_range *range) | 332 | u32 id, const struct clk_range *range) |
| 333 | { | 333 | { |
| @@ -354,7 +354,9 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, · 20 unmodified lines | |||
| 354 | periph->div = 0; | 354 | periph->div = 0; |
| 355 | periph->regmap = regmap; | 355 | periph->regmap = regmap; |
| 356 | periph->lock = lock; | 356 | periph->lock = lock; |
| 357 | periph->auto_div = true; | 357 | if (layout->div_mask) |
| 358 | periph->auto_div = true; | ||
| 359 | periph->layout = layout; | ||
| 358 | periph->range = *range; | 360 | periph->range = *range; |
| 359 | 361 | ||
| 360 | hw = &periph->hw; | 362 | hw = &periph->hw; |
| @@ -0,0 +1,330 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0+ | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2019 Microchip Technology Inc. | ||
| 4 | * | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/bitfield.h> | ||
| 8 | #include <linux/clk-provider.h> | ||
| 9 | #include <linux/clkdev.h> | ||
| 10 | #include <linux/clk/at91_pmc.h> | ||
| 11 | #include <linux/of.h> | ||
| 12 | #include <linux/mfd/syscon.h> | ||
| 13 | #include <linux/regmap.h> | ||
| 14 | |||
| 15 | #include "pmc.h" | ||
| 16 | |||
| 17 | #define PMC_PLL_CTRL0 0xc | ||
| 18 | #define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0) | ||
| 19 | #define PMC_PLL_CTRL0_ENPLL BIT(28) | ||
| 20 | #define PMC_PLL_CTRL0_ENPLLCK BIT(29) | ||
| 21 | #define PMC_PLL_CTRL0_ENLOCK BIT(31) | ||
| 22 | |||
| 23 | #define PMC_PLL_CTRL1 0x10 | ||
| 24 | #define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0) | ||
| 25 | #define PMC_PLL_CTRL1_MUL_MSK GENMASK(30, 24) | ||
| 26 | |||
| 27 | #define PMC_PLL_ACR 0x18 | ||
| 28 | #define PMC_PLL_ACR_DEFAULT 0x1b040010UL | ||
| 29 | #define PMC_PLL_ACR_UTMIVR BIT(12) | ||
| 30 | #define PMC_PLL_ACR_UTMIBG BIT(13) | ||
| 31 | #define PMC_PLL_ACR_LOOP_FILTER_MSK GENMASK(31, 24) | ||
| 32 | |||
| 33 | #define PMC_PLL_UPDT 0x1c | ||
| 34 | #define PMC_PLL_UPDT_UPDATE BIT(8) | ||
| 35 | |||
| 36 | #define PMC_PLL_ISR0 0xec | ||
| 37 | |||
| 38 | #define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1) | ||
| 39 | #define UPLL_DIV 2 | ||
| 40 | #define PLL_MUL_MAX (FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1) | ||
| 41 | |||
| 42 | #define PLL_MAX_ID 1 | ||
| 43 | |||
| 44 | struct sam9x60_pll { | ||
| 45 | struct clk_hw hw; | ||
| 46 | struct regmap *regmap; | ||
| 47 | spinlock_t *lock; | ||
| 48 | const struct clk_pll_characteristics *characteristics; | ||
| 49 | u32 frac; | ||
| 50 | u8 id; | ||
| 51 | u8 div; | ||
| 52 | u16 mul; | ||
| 53 | }; | ||
| 54 | |||
| 55 | #define to_sam9x60_pll(hw) container_of(hw, struct sam9x60_pll, hw) | ||
| 56 | |||
| 57 | static inline bool sam9x60_pll_ready(struct regmap *regmap, int id) | ||
| 58 | { | ||
| 59 | unsigned int status; | ||
| 60 | |||
| 61 | regmap_read(regmap, PMC_PLL_ISR0, &status); | ||
| 62 | |||
| 63 | return !!(status & BIT(id)); | ||
| 64 | } | ||
| 65 | |||
| 66 | static int sam9x60_pll_prepare(struct clk_hw *hw) | ||
| 67 | { | ||
| 68 | struct sam9x60_pll *pll = to_sam9x60_pll(hw); | ||
| 69 | struct regmap *regmap = pll->regmap; | ||
| 70 | unsigned long flags; | ||
| 71 | u8 div; | ||
| 72 | u16 mul; | ||
| 73 | u32 val; | ||
| 74 | |||
| 75 | spin_lock_irqsave(pll->lock, flags); | ||
| 76 | regmap_write(regmap, PMC_PLL_UPDT, pll->id); | ||
| 77 | |||
| 78 | regmap_read(regmap, PMC_PLL_CTRL0, &val); | ||
| 79 | div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val); | ||
| 80 | |||
| 81 | regmap_read(regmap, PMC_PLL_CTRL1, &val); | ||
| 82 | mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val); | ||
| 83 | |||
| 84 | if (sam9x60_pll_ready(regmap, pll->id) && | ||
| 85 | (div == pll->div && mul == pll->mul)) { | ||
| 86 | spin_unlock_irqrestore(pll->lock, flags); | ||
| 87 | return 0; | ||
| 88 | } | ||
| 89 | |||
| 90 | /* Recommended value for PMC_PLL_ACR */ | ||
| 91 | val = PMC_PLL_ACR_DEFAULT; | ||
| 92 | regmap_write(regmap, PMC_PLL_ACR, val); | ||
| 93 | |||
| 94 | regmap_write(regmap, PMC_PLL_CTRL1, | ||
| 95 | FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul)); | ||
| 96 | |||
| 97 | if (pll->characteristics->upll) { | ||
| 98 | /* Enable the UTMI internal bandgap */ | ||
| 99 | val |= PMC_PLL_ACR_UTMIBG; | ||
| 100 | regmap_write(regmap, PMC_PLL_ACR, val); | ||
| 101 | |||
| 102 | udelay(10); | ||
| 103 | |||
| 104 | /* Enable the UTMI internal regulator */ | ||
| 105 | val |= PMC_PLL_ACR_UTMIVR; | ||
| 106 | regmap_write(regmap, PMC_PLL_ACR, val); | ||
| 107 | |||
| 108 | udelay(10); | ||
| 109 | } | ||
| 110 | |||
| 111 | regmap_update_bits(regmap, PMC_PLL_UPDT, | ||
| 112 | PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE); | ||
| 113 | |||
| 114 | regmap_write(regmap, PMC_PLL_CTRL0, | ||
| 115 | PMC_PLL_CTRL0_ENLOCK | PMC_PLL_CTRL0_ENPLL | | ||
| 116 | PMC_PLL_CTRL0_ENPLLCK | pll->div); | ||
| 117 | |||
| 118 | regmap_update_bits(regmap, PMC_PLL_UPDT, | ||
| 119 | PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE); | ||
| 120 | |||
| 121 | while (!sam9x60_pll_ready(regmap, pll->id)) | ||
| 122 | cpu_relax(); | ||
| 123 | |||
| 124 | spin_unlock_irqrestore(pll->lock, flags); | ||
| 125 | |||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | static int sam9x60_pll_is_prepared(struct clk_hw *hw) | ||
| 130 | { | ||
| 131 | struct sam9x60_pll *pll = to_sam9x60_pll(hw); | ||
| 132 | |||
| 133 | return sam9x60_pll_ready(pll->regmap, pll->id); | ||
| 134 | } | ||
| 135 | |||
| 136 | static void sam9x60_pll_unprepare(struct clk_hw *hw) | ||
| 137 | { | ||
| 138 | struct sam9x60_pll *pll = to_sam9x60_pll(hw); | ||
| 139 | unsigned long flags; | ||
| 140 | |||
| 141 | spin_lock_irqsave(pll->lock, flags); | ||
| 142 | |||
| 143 | regmap_write(pll->regmap, PMC_PLL_UPDT, pll->id); | ||
| 144 | |||
| 145 | regmap_update_bits(pll->regmap, PMC_PLL_CTRL0, | ||
| 146 | PMC_PLL_CTRL0_ENPLLCK, 0); | ||
| 147 | |||
| 148 | regmap_update_bits(pll->regmap, PMC_PLL_UPDT, | ||
| 149 | PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE); | ||
| 150 | |||
| 151 | regmap_update_bits(pll->regmap, PMC_PLL_CTRL0, PMC_PLL_CTRL0_ENPLL, 0); | ||
| 152 | |||
| 153 | if (pll->characteristics->upll) | ||
| 154 | regmap_update_bits(pll->regmap, PMC_PLL_ACR, | ||
| 155 | PMC_PLL_ACR_UTMIBG | PMC_PLL_ACR_UTMIVR, 0); | ||
| 156 | |||
| 157 | regmap_update_bits(pll->regmap, PMC_PLL_UPDT, | ||
| 158 | PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE); | ||
| 159 | |||
| 160 | spin_unlock_irqrestore(pll->lock, flags); | ||
| 161 | } | ||
| 162 | |||
| 163 | static unsigned long sam9x60_pll_recalc_rate(struct clk_hw *hw, | ||
| 164 | unsigned long parent_rate) | ||
| 165 | { | ||
| 166 | struct sam9x60_pll *pll = to_sam9x60_pll(hw); | ||
| 167 | |||
| 168 | return (parent_rate * (pll->mul + 1)) / (pll->div + 1); | ||
| 169 | } | ||
| 170 | |||
| 171 | static long sam9x60_pll_get_best_div_mul(struct sam9x60_pll *pll, | ||
| 172 | unsigned long rate, | ||
| 173 | unsigned long parent_rate, | ||
| 174 | bool update) | ||
| 175 | { | ||
| 176 | const struct clk_pll_characteristics *characteristics = | ||
| 177 | pll->characteristics; | ||
| 178 | unsigned long bestremainder = ULONG_MAX; | ||
| 179 | unsigned long maxdiv, mindiv, tmpdiv; | ||
| 180 | long bestrate = -ERANGE; | ||
| 181 | unsigned long bestdiv = 0; | ||
| 182 | unsigned long bestmul = 0; | ||
| 183 | unsigned long bestfrac = 0; | ||
| 184 | |||
| 185 | if (rate < characteristics->output[0].min || | ||
| 186 | rate > characteristics->output[0].max) | ||
| 187 | return -ERANGE; | ||
| 188 | |||
| 189 | if (!pll->characteristics->upll) { | ||
| 190 | mindiv = parent_rate / rate; | ||
| 191 | if (mindiv < 2) | ||
| 192 | mindiv = 2; | ||
| 193 | |||
| 194 | maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX, rate); | ||
| 195 | if (maxdiv > PLL_DIV_MAX) | ||
| 196 | maxdiv = PLL_DIV_MAX; | ||
| 197 | } else { | ||
| 198 | mindiv = maxdiv = UPLL_DIV; | ||
| 199 | } | ||
| 200 | |||
| 201 | for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) { | ||
| 202 | unsigned long remainder; | ||
| 203 | unsigned long tmprate; | ||
| 204 | unsigned long tmpmul; | ||
| 205 | unsigned long tmpfrac = 0; | ||
| 206 | |||
| 207 | /* | ||
| 208 | * Calculate the multiplier associated with the current | ||
| 209 | * divider that provide the closest rate to the requested one. | ||
| 210 | */ | ||
| 211 | tmpmul = mult_frac(rate, tmpdiv, parent_rate); | ||
| 212 | tmprate = mult_frac(parent_rate, tmpmul, tmpdiv); | ||
| 213 | remainder = rate - tmprate; | ||
| 214 | |||
| 215 | if (remainder) { | ||
| 216 | tmpfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * tmpdiv * (1 << 22), | ||
| 217 | parent_rate); | ||
| 218 | |||
| 219 | tmprate += DIV_ROUND_CLOSEST_ULL((u64)tmpfrac * parent_rate, | ||
| 220 | tmpdiv * (1 << 22)); | ||
| 221 | |||
| 222 | if (tmprate > rate) | ||
| 223 | remainder = tmprate - rate; | ||
| 224 | else | ||
| 225 | remainder = rate - tmprate; | ||
| 226 | } | ||
| 227 | |||
| 228 | /* | ||
| 229 | * Compare the remainder with the best remainder found until | ||
| 230 | * now and elect a new best multiplier/divider pair if the | ||
| 231 | * current remainder is smaller than the best one. | ||
| 232 | */ | ||
| 233 | if (remainder < bestremainder) { | ||
| 234 | bestremainder = remainder; | ||
| 235 | bestdiv = tmpdiv; | ||
| 236 | bestmul = tmpmul; | ||
| 237 | bestrate = tmprate; | ||
| 238 | bestfrac = tmpfrac; | ||
| 239 | } | ||
| 240 | |||
| 241 | /* We've found a perfect match! */ | ||
| 242 | if (!remainder) | ||
| 243 | break; | ||
| 244 | } | ||
| 245 | |||
| 246 | /* Check if bestrate is a valid output rate */ | ||
| 247 | if (bestrate < characteristics->output[0].min && | ||
| 248 | bestrate > characteristics->output[0].max) | ||
| 249 | return -ERANGE; | ||
| 250 | |||
| 251 | if (update) { | ||
| 252 | pll->div = bestdiv - 1; | ||
| 253 | pll->mul = bestmul - 1; | ||
| 254 | pll->frac = bestfrac; | ||
| 255 | } | ||
| 256 | |||
| 257 | return bestrate; | ||
| 258 | } | ||
| 259 | |||
| 260 | static long sam9x60_pll_round_rate(struct clk_hw *hw, unsigned long rate, | ||
| 261 | unsigned long *parent_rate) | ||
| 262 | { | ||
| 263 | struct sam9x60_pll *pll = to_sam9x60_pll(hw); | ||
| 264 | |||
| 265 | return sam9x60_pll_get_best_div_mul(pll, rate, *parent_rate, false); | ||
| 266 | } | ||
| 267 | |||
| 268 | static int sam9x60_pll_set_rate(struct clk_hw *hw, unsigned long rate, | ||
| 269 | unsigned long parent_rate) | ||
| 270 | { | ||
| 271 | struct sam9x60_pll *pll = to_sam9x60_pll(hw); | ||
| 272 | |||
| 273 | return sam9x60_pll_get_best_div_mul(pll, rate, parent_rate, true); | ||
| 274 | } | ||
| 275 | |||
| 276 | static const struct clk_ops pll_ops = { | ||
| 277 | .prepare = sam9x60_pll_prepare, | ||
| 278 | .unprepare = sam9x60_pll_unprepare, | ||
| 279 | .is_prepared = sam9x60_pll_is_prepared, | ||
| 280 | .recalc_rate = sam9x60_pll_recalc_rate, | ||
| 281 | .round_rate = sam9x60_pll_round_rate, | ||
| 282 | .set_rate = sam9x60_pll_set_rate, | ||
| 283 | }; | ||
| 284 | |||
| 285 | struct clk_hw * __init | ||
| 286 | sam9x60_clk_register_pll(struct regmap *regmap, spinlock_t *lock, | ||
| 287 | const char *name, const char *parent_name, u8 id, | ||
| 288 | const struct clk_pll_characteristics *characteristics) | ||
| 289 | { | ||
| 290 | struct sam9x60_pll *pll; | ||
| 291 | struct clk_hw *hw; | ||
| 292 | struct clk_init_data init; | ||
| 293 | unsigned int pllr; | ||
| 294 | int ret; | ||
| 295 | |||
| 296 | if (id > PLL_MAX_ID) | ||
| 297 | return ERR_PTR(-EINVAL); | ||
| 298 | |||
| 299 | pll = kzalloc(sizeof(*pll), GFP_KERNEL); | ||
| 300 | if (!pll) | ||
| 301 | return ERR_PTR(-ENOMEM); | ||
| 302 | |||
| 303 | init.name = name; | ||
| 304 | init.ops = &pll_ops; | ||
| 305 | init.parent_names = &parent_name; | ||
| 306 | init.num_parents = 1; | ||
| 307 | init.flags = CLK_SET_RATE_GATE; | ||
| 308 | |||
| 309 | pll->id = id; | ||
| 310 | pll->hw.init = &init; | ||
| 311 | pll->characteristics = characteristics; | ||
| 312 | pll->regmap = regmap; | ||
| 313 | pll->lock = lock; | ||
| 314 | |||
| 315 | regmap_write(regmap, PMC_PLL_UPDT, id); | ||
| 316 | regmap_read(regmap, PMC_PLL_CTRL0, &pllr); | ||
| 317 | pll->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, pllr); | ||
| 318 | regmap_read(regmap, PMC_PLL_CTRL1, &pllr); | ||
| 319 | pll->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, pllr); | ||
| 320 | |||
| 321 | hw = &pll->hw; | ||
| 322 | ret = clk_hw_register(NULL, hw); | ||
| 323 | if (ret) { | ||
| 324 | kfree(pll); | ||
| 325 | hw = ERR_PTR(ret); | ||
| 326 | } | ||
| 327 | |||
| 328 | return hw; | ||
| 329 | } | ||
| 330 | |||
| @@ -23,9 +23,13 @@ · 22 unmodified lines | |||
| 23 | #define RM9200_USB_DIV_SHIFT 28 | 23 | #define RM9200_USB_DIV_SHIFT 28 |
| 24 | #define RM9200_USB_DIV_TAB_SIZE 4 | 24 | #define RM9200_USB_DIV_TAB_SIZE 4 |
| 25 | 25 | ||
| 26 | #define SAM9X5_USBS_MASK GENMASK(0, 0) | ||
| 27 | #define SAM9X60_USBS_MASK GENMASK(1, 0) | ||
| 28 | |||
| 26 | struct at91sam9x5_clk_usb { | 29 | struct at91sam9x5_clk_usb { |
| 27 | struct clk_hw hw; | 30 | struct clk_hw hw; |
| 28 | struct regmap *regmap; | 31 | struct regmap *regmap; |
| 32 | u32 usbs_mask; | ||
| 29 | }; | 33 | }; |
| 30 | 34 | ||
| 31 | #define to_at91sam9x5_clk_usb(hw) \ | 35 | #define to_at91sam9x5_clk_usb(hw) \ |
| @@ -111,8 +115,7 @@ static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index) · 79 unmodified lines | |||
| 111 | if (index > 1) | 115 | if (index > 1) |
| 112 | return -EINVAL; | 116 | return -EINVAL; |
| 113 | 117 | ||
| 114 | regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, | 118 | regmap_update_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index); |
| 115 | index ? AT91_PMC_USBS : 0); | ||
| 116 | 119 | ||
| 117 | return 0; | 120 | return 0; |
| 118 | } | 121 | } |
| @@ -124,7 +127,7 @@ static u8 at91sam9x5_clk_usb_get_parent(struct clk_hw *hw) · 5 unmodified lines | |||
| 124 | 127 | ||
| 125 | regmap_read(usb->regmap, AT91_PMC_USB, &usbr); | 128 | regmap_read(usb->regmap, AT91_PMC_USB, &usbr); |
| 126 | 129 | ||
| 127 | return usbr & AT91_PMC_USBS; | 130 | return usbr & usb->usbs_mask; |
| 128 | } | 131 | } |
| 129 | 132 | ||
| 130 | static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate, | 133 | 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 = { · 59 unmodified lines | |||
| 190 | .set_rate = at91sam9x5_clk_usb_set_rate, | 193 | .set_rate = at91sam9x5_clk_usb_set_rate, |
| 191 | }; | 194 | }; |
| 192 | 195 | ||
| 193 | struct clk_hw * __init | 196 | static struct clk_hw * __init |
| 194 | at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, | 197 | _at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, |
| 195 | const char **parent_names, u8 num_parents) | 198 | const char **parent_names, u8 num_parents, |
| 199 | u32 usbs_mask) | ||
| 196 | { | 200 | { |
| 197 | struct at91sam9x5_clk_usb *usb; | 201 | struct at91sam9x5_clk_usb *usb; |
| 198 | struct clk_hw *hw; | 202 | struct clk_hw *hw; |
| @@ -212,6 +216,7 @@ at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, · 13 unmodified lines | |||
| 212 | 216 | ||
| 213 | usb->hw.init = &init; | 217 | usb->hw.init = &init; |
| 214 | usb->regmap = regmap; | 218 | usb->regmap = regmap; |
| 219 | usb->usbs_mask = SAM9X5_USBS_MASK; | ||
| 215 | 220 | ||
| 216 | hw = &usb->hw; | 221 | hw = &usb->hw; |
| 217 | ret = clk_hw_register(NULL, &usb->hw); | 222 | ret = clk_hw_register(NULL, &usb->hw); |
| @@ -223,6 +228,22 @@ at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, · 5 unmodified lines | |||
| 223 | return hw; | 228 | return hw; |
| 224 | } | 229 | } |
| 225 | 230 | ||
| 231 | struct clk_hw * __init | ||
| 232 | at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, | ||
| 233 | const char **parent_names, u8 num_parents) | ||
| 234 | { | ||
| 235 | return _at91sam9x5_clk_register_usb(regmap, name, parent_names, | ||
| 236 | num_parents, SAM9X5_USBS_MASK); | ||
| 237 | } | ||
| 238 | |||
| 239 | struct clk_hw * __init | ||
| 240 | sam9x60_clk_register_usb(struct regmap *regmap, const char *name, | ||
| 241 | const char **parent_names, u8 num_parents) | ||
| 242 | { | ||
| 243 | return _at91sam9x5_clk_register_usb(regmap, name, parent_names, | ||
| 244 | num_parents, SAM9X60_USBS_MASK); | ||
| 245 | } | ||
| 246 | |||
| 226 | struct clk_hw * __init | 247 | struct clk_hw * __init |
| 227 | at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, | 248 | at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, |
| 228 | const char *parent_name) | 249 | const char *parent_name) |
| @@ -93,6 +93,14 @@ CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup, · 92 unmodified lines | |||
| 93 | of_sama5d2_clk_audio_pll_pmc_setup); | 93 | of_sama5d2_clk_audio_pll_pmc_setup); |
| 94 | #endif /* CONFIG_HAVE_AT91_AUDIO_PLL */ | 94 | #endif /* CONFIG_HAVE_AT91_AUDIO_PLL */ |
| 95 | 95 | ||
| 96 | static const struct clk_pcr_layout dt_pcr_layout = { | ||
| 97 | .offset = 0x10c, | ||
| 98 | .cmd = BIT(12), | ||
| 99 | .pid_mask = GENMASK(5, 0), | ||
| 100 | .div_mask = GENMASK(17, 16), | ||
| 101 | .gckcss_mask = GENMASK(10, 8), | ||
| 102 | }; | ||
| 103 | |||
| 96 | #ifdef CONFIG_HAVE_AT91_GENERATED_CLK | 104 | #ifdef CONFIG_HAVE_AT91_GENERATED_CLK |
| 97 | #define GENERATED_SOURCE_MAX 6 | 105 | #define GENERATED_SOURCE_MAX 6 |
| 98 | 106 | ||
| @@ -146,7 +154,8 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np) · 47 unmodified lines | |||
| 146 | id == GCK_ID_CLASSD)) | 154 | id == GCK_ID_CLASSD)) |
| 147 | pll_audio = true; | 155 | pll_audio = true; |
| 148 | 156 | ||
| 149 | hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name, | 157 | hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, |
| 158 | &dt_pcr_layout, name, | ||
| 150 | parent_names, num_parents, | 159 | parent_names, num_parents, |
| 151 | id, pll_audio, &range); | 160 | id, pll_audio, &range); |
| 152 | if (IS_ERR(hw)) | 161 | if (IS_ERR(hw)) |
| @@ -448,6 +457,7 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type) · 295 unmodified lines | |||
| 448 | 457 | ||
| 449 | hw = at91_clk_register_sam9x5_peripheral(regmap, | 458 | hw = at91_clk_register_sam9x5_peripheral(regmap, |
| 450 | &pmc_pcr_lock, | 459 | &pmc_pcr_lock, |
| 460 | &dt_pcr_layout, | ||
| 451 | name, | 461 | name, |
| 452 | parent_name, | 462 | parent_name, |
| 453 | id, &range); | 463 | id, &range); |
| @@ -38,6 +38,7 @@ struct clk_range { · 37 unmodified lines | |||
| 38 | #define CLK_RANGE(MIN, MAX) {.min = MIN, .max = MAX,} | 38 | #define CLK_RANGE(MIN, MAX) {.min = MIN, .max = MAX,} |
| 39 | 39 | ||
| 40 | struct clk_master_layout { | 40 | struct clk_master_layout { |
| 41 | u32 offset; | ||
| 41 | u32 mask; | 42 | u32 mask; |
| 42 | u8 pres_shift; | 43 | u8 pres_shift; |
| 43 | }; | 44 | }; |
| @@ -65,9 +66,10 @@ extern const struct clk_pll_layout sama5d3_pll_layout; · 21 unmodified lines | |||
| 65 | struct clk_pll_characteristics { | 66 | struct clk_pll_characteristics { |
| 66 | struct clk_range input; | 67 | struct clk_range input; |
| 67 | int num_output; | 68 | int num_output; |
| 68 | struct clk_range *output; | 69 | const struct clk_range *output; |
| 69 | u16 *icpll; | 70 | u16 *icpll; |
| 70 | u8 *out; | 71 | u8 *out; |
| 72 | u8 upll : 1; | ||
| 71 | }; | 73 | }; |
| 72 | 74 | ||
| 73 | struct clk_programmable_layout { | 75 | struct clk_programmable_layout { |
| @@ -82,6 +84,17 @@ extern const struct clk_programmable_layout at91rm9200_programmable_layout; · 8 unmodified lines | |||
| 82 | extern const struct clk_programmable_layout at91sam9g45_programmable_layout; | 84 | extern const struct clk_programmable_layout at91sam9g45_programmable_layout; |
| 83 | extern const struct clk_programmable_layout at91sam9x5_programmable_layout; | 85 | extern const struct clk_programmable_layout at91sam9x5_programmable_layout; |
| 84 | 86 | ||
| 87 | struct clk_pcr_layout { | ||
| 88 | u32 offset; | ||
| 89 | u32 cmd; | ||
| 90 | u32 div_mask; | ||
| 91 | u32 gckcss_mask; | ||
| 92 | u32 pid_mask; | ||
| 93 | }; | ||
| 94 | |||
| 95 | #define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) | ||
| 96 | #define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) | ||
| 97 | |||
| 85 | #define ndck(a, s) (a[s - 1].id + 1) | 98 | #define ndck(a, s) (a[s - 1].id + 1) |
| 86 | #define nck(a) (a[ARRAY_SIZE(a) - 1].id + 1) | 99 | #define nck(a) (a[ARRAY_SIZE(a) - 1].id + 1) |
| 87 | struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem, | 100 | 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, · 19 unmodified lines | |||
| 107 | 120 | ||
| 108 | struct clk_hw * __init | 121 | struct clk_hw * __init |
| 109 | at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, | 122 | at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, |
| 123 | const struct clk_pcr_layout *layout, | ||
| 110 | const char *name, const char **parent_names, | 124 | const char *name, const char **parent_names, |
| 111 | u8 num_parents, u8 id, bool pll_audio, | 125 | u8 num_parents, u8 id, bool pll_audio, |
| 112 | const struct clk_range *range); | 126 | const struct clk_range *range); |
| @@ -145,6 +159,7 @@ at91_clk_register_peripheral(struct regmap *regmap, const char *name, · 32 unmodified lines | |||
| 145 | const char *parent_name, u32 id); | 159 | const char *parent_name, u32 id); |
| 146 | struct clk_hw * __init | 160 | struct clk_hw * __init |
| 147 | at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, | 161 | at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, |
| 162 | const struct clk_pcr_layout *layout, | ||
| 148 | const char *name, const char *parent_name, | 163 | const char *name, const char *parent_name, |
| 149 | u32 id, const struct clk_range *range); | 164 | u32 id, const struct clk_range *range); |
| 150 | 165 | ||
| @@ -157,6 +172,11 @@ struct clk_hw * __init · 6 unmodified lines | |||
| 157 | at91_clk_register_plldiv(struct regmap *regmap, const char *name, | 172 | at91_clk_register_plldiv(struct regmap *regmap, const char *name, |
| 158 | const char *parent_name); | 173 | const char *parent_name); |
| 159 | 174 | ||
| 175 | struct clk_hw * __init | ||
| 176 | sam9x60_clk_register_pll(struct regmap *regmap, spinlock_t *lock, | ||
| 177 | const char *name, const char *parent_name, u8 id, | ||
| 178 | const struct clk_pll_characteristics *characteristics); | ||
| 179 | |||
| 160 | struct clk_hw * __init | 180 | struct clk_hw * __init |
| 161 | at91_clk_register_programmable(struct regmap *regmap, const char *name, | 181 | at91_clk_register_programmable(struct regmap *regmap, const char *name, |
| 162 | const char **parent_names, u8 num_parents, u8 id, | 182 | const char **parent_names, u8 num_parents, u8 id, |
| @@ -183,6 +203,9 @@ struct clk_hw * __init · 20 unmodified lines | |||
| 183 | at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, | 203 | at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, |
| 184 | const char *parent_name); | 204 | const char *parent_name); |
| 185 | struct clk_hw * __init | 205 | struct clk_hw * __init |
| 206 | sam9x60_clk_register_usb(struct regmap *regmap, const char *name, | ||
| 207 | const char **parent_names, u8 num_parents); | ||
| 208 | struct clk_hw * __init | ||
| 186 | at91rm9200_clk_register_usb(struct regmap *regmap, const char *name, | 209 | at91rm9200_clk_register_usb(struct regmap *regmap, const char *name, |
| 187 | const char *parent_name, const u32 *divisors); | 210 | const char *parent_name, const u32 *divisors); |
| 188 | 211 | ||
| @@ -0,0 +1,307 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | #include <linux/clk-provider.h> | ||
| 3 | #include <linux/mfd/syscon.h> | ||
| 4 | #include <linux/slab.h> | ||
| 5 | |||
| 6 | #include <dt-bindings/clock/at91.h> | ||
| 7 | |||
| 8 | #include "pmc.h" | ||
| 9 | |||
| 10 | static DEFINE_SPINLOCK(pmc_pll_lock); | ||
| 11 | |||
| 12 | static const struct clk_master_characteristics mck_characteristics = { | ||
| 13 | .output = { .min = 140000000, .max = 200000000 }, | ||
| 14 | .divisors = { 1, 2, 4, 3 }, | ||
| 15 | .have_div3_pres = 1, | ||
| 16 | }; | ||
| 17 | |||
| 18 | static const struct clk_master_layout sam9x60_master_layout = { | ||
| 19 | .mask = 0x373, | ||
| 20 | .pres_shift = 4, | ||
| 21 | .offset = 0x28, | ||
| 22 | }; | ||
| 23 | |||
| 24 | static const struct clk_range plla_outputs[] = { | ||
| 25 | { .min = 300000000, .max = 600000000 }, | ||
| 26 | }; | ||
| 27 | |||
| 28 | static const struct clk_pll_characteristics plla_characteristics = { | ||
| 29 | .input = { .min = 12000000, .max = 48000000 }, | ||
| 30 | .num_output = ARRAY_SIZE(plla_outputs), | ||
| 31 | .output = plla_outputs, | ||
| 32 | }; | ||
| 33 | |||
| 34 | static const struct clk_range upll_outputs[] = { | ||
| 35 | { .min = 300000000, .max = 500000000 }, | ||
| 36 | }; | ||
| 37 | |||
| 38 | static const struct clk_pll_characteristics upll_characteristics = { | ||
| 39 | .input = { .min = 12000000, .max = 48000000 }, | ||
| 40 | .num_output = ARRAY_SIZE(upll_outputs), | ||
| 41 | .output = upll_outputs, | ||
| 42 | .upll = true, | ||
| 43 | }; | ||
| 44 | |||
| 45 | static const struct clk_programmable_layout sam9x60_programmable_layout = { | ||
| 46 | .pres_shift = 8, | ||
| 47 | .css_mask = 0x1f, | ||
| 48 | .have_slck_mck = 0, | ||
| 49 | }; | ||
| 50 | |||
| 51 | static const struct clk_pcr_layout sam9x60_pcr_layout = { | ||
| 52 | .offset = 0x88, | ||
| 53 | .cmd = BIT(31), | ||
| 54 | .gckcss_mask = GENMASK(12, 8), | ||
| 55 | .pid_mask = GENMASK(6, 0), | ||
| 56 | }; | ||
| 57 | |||
| 58 | static const struct { | ||
| 59 | char *n; | ||
| 60 | char *p; | ||
| 61 | u8 id; | ||
| 62 | } sam9x60_systemck[] = { | ||
| 63 | { .n = "ddrck", .p = "masterck", .id = 2 }, | ||
| 64 | { .n = "uhpck", .p = "usbck", .id = 6 }, | ||
| 65 | { .n = "pck0", .p = "prog0", .id = 8 }, | ||
| 66 | { .n = "pck1", .p = "prog1", .id = 9 }, | ||
| 67 | { .n = "qspick", .p = "masterck", .id = 19 }, | ||
| 68 | }; | ||
| 69 | |||
| 70 | static const struct { | ||
| 71 | char *n; | ||
| 72 | u8 id; | ||
| 73 | } sam9x60_periphck[] = { | ||
| 74 | { .n = "pioA_clk", .id = 2, }, | ||
| 75 | { .n = "pioB_clk", .id = 3, }, | ||
| 76 | { .n = "pioC_clk", .id = 4, }, | ||
| 77 | { .n = "flex0_clk", .id = 5, }, | ||
| 78 | { .n = "flex1_clk", .id = 6, }, | ||
| 79 | { .n = "flex2_clk", .id = 7, }, | ||
| 80 | { .n = "flex3_clk", .id = 8, }, | ||
| 81 | { .n = "flex6_clk", .id = 9, }, | ||
| 82 | { .n = "flex7_clk", .id = 10, }, | ||
| 83 | { .n = "flex8_clk", .id = 11, }, | ||
| 84 | { .n = "sdmmc0_clk", .id = 12, }, | ||
| 85 | { .n = "flex4_clk", .id = 13, }, | ||
| 86 | { .n = "flex5_clk", .id = 14, }, | ||
| 87 | { .n = "flex9_clk", .id = 15, }, | ||
| 88 | { .n = "flex10_clk", .id = 16, }, | ||
| 89 | { .n = "tcb0_clk", .id = 17, }, | ||
| 90 | { .n = "pwm_clk", .id = 18, }, | ||
| 91 | { .n = "adc_clk", .id = 19, }, | ||
| 92 | { .n = "dma0_clk", .id = 20, }, | ||
| 93 | { .n = "matrix_clk", .id = 21, }, | ||
| 94 | { .n = "uhphs_clk", .id = 22, }, | ||
| 95 | { .n = "udphs_clk", .id = 23, }, | ||
| 96 | { .n = "macb0_clk", .id = 24, }, | ||
| 97 | { .n = "lcd_clk", .id = 25, }, | ||
| 98 | { .n = "sdmmc1_clk", .id = 26, }, | ||
| 99 | { .n = "macb1_clk", .id = 27, }, | ||
| 100 | { .n = "ssc_clk", .id = 28, }, | ||
| 101 | { .n = "can0_clk", .id = 29, }, | ||
| 102 | { .n = "can1_clk", .id = 30, }, | ||
| 103 | { .n = "flex11_clk", .id = 32, }, | ||
| 104 | { .n = "flex12_clk", .id = 33, }, | ||
| 105 | { .n = "i2s_clk", .id = 34, }, | ||
| 106 | { .n = "qspi_clk", .id = 35, }, | ||
| 107 | { .n = "gfx2d_clk", .id = 36, }, | ||
| 108 | { .n = "pit64b_clk", .id = 37, }, | ||
| 109 | { .n = "trng_clk", .id = 38, }, | ||
| 110 | { .n = "aes_clk", .id = 39, }, | ||
| 111 | { .n = "tdes_clk", .id = 40, }, | ||
| 112 | { .n = "sha_clk", .id = 41, }, | ||
| 113 | { .n = "classd_clk", .id = 42, }, | ||
| 114 | { .n = "isi_clk", .id = 43, }, | ||
| 115 | { .n = "pioD_clk", .id = 44, }, | ||
| 116 | { .n = "tcb1_clk", .id = 45, }, | ||
| 117 | { .n = "dbgu_clk", .id = 47, }, | ||
| 118 | { .n = "mpddr_clk", .id = 49, }, | ||
| 119 | }; | ||
| 120 | |||
| 121 | static const struct { | ||
| 122 | char *n; | ||
| 123 | u8 id; | ||
| 124 | struct clk_range r; | ||
| 125 | bool pll; | ||
| 126 | } sam9x60_gck[] = { | ||
| 127 | { .n = "flex0_gclk", .id = 5, }, | ||
| 128 | { .n = "flex1_gclk", .id = 6, }, | ||
| 129 | { .n = "flex2_gclk", .id = 7, }, | ||
| 130 | { .n = "flex3_gclk", .id = 8, }, | ||
| 131 | { .n = "flex6_gclk", .id = 9, }, | ||
| 132 | { .n = "flex7_gclk", .id = 10, }, | ||
| 133 | { .n = "flex8_gclk", .id = 11, }, | ||
| 134 | { .n = "sdmmc0_gclk", .id = 12, .r = { .min = 0, .max = 105000000 }, }, | ||
| 135 | { .n = "flex4_gclk", .id = 13, }, | ||
| 136 | { .n = "flex5_gclk", .id = 14, }, | ||
| 137 | { .n = "flex9_gclk", .id = 15, }, | ||
| 138 | { .n = "flex10_gclk", .id = 16, }, | ||
| 139 | { .n = "tcb0_gclk", .id = 17, }, | ||
| 140 | { .n = "adc_gclk", .id = 19, }, | ||
| 141 | { .n = "lcd_gclk", .id = 25, .r = { .min = 0, .max = 140000000 }, }, | ||
| 142 | { .n = "sdmmc1_gclk", .id = 26, .r = { .min = 0, .max = 105000000 }, }, | ||
| 143 | { .n = "flex11_gclk", .id = 32, }, | ||
| 144 | { .n = "flex12_gclk", .id = 33, }, | ||
| 145 | { .n = "i2s_gclk", .id = 34, .r = { .min = 0, .max = 105000000 }, | ||
| 146 | .pll = true, }, | ||
| 147 | { .n = "pit64b_gclk", .id = 37, }, | ||
| 148 | { .n = "classd_gclk", .id = 42, .r = { .min = 0, .max = 100000000 }, | ||
| 149 | .pll = true, }, | ||
| 150 | { .n = "tcb1_gclk", .id = 45, }, | ||
| 151 | { .n = "dbgu_gclk", .id = 47, }, | ||
| 152 | }; | ||
| 153 | |||
| 154 | static void __init sam9x60_pmc_setup(struct device_node *np) | ||
| 155 | { | ||
| 156 | struct clk_range range = CLK_RANGE(0, 0); | ||
| 157 | const char *td_slck_name, *md_slck_name, *mainxtal_name; | ||
| 158 | struct pmc_data *sam9x60_pmc; | ||
| 159 | const char *parent_names[6]; | ||
| 160 | struct regmap *regmap; | ||
| 161 | struct clk_hw *hw; | ||
| 162 | int i; | ||
| 163 | bool bypass; | ||
| 164 | |||
| 165 | i = of_property_match_string(np, "clock-names", "td_slck"); | ||
| 166 | if (i < 0) | ||
| 167 | return; | ||
| 168 | |||
| 169 | td_slck_name = of_clk_get_parent_name(np, i); | ||
| 170 | |||
| 171 | i = of_property_match_string(np, "clock-names", "md_slck"); | ||
| 172 | if (i < 0) | ||
| 173 | return; | ||
| 174 | |||
| 175 | md_slck_name = of_clk_get_parent_name(np, i); | ||
| 176 | |||
| 177 | i = of_property_match_string(np, "clock-names", "main_xtal"); | ||
| 178 | if (i < 0) | ||
| 179 | return; | ||
| 180 | mainxtal_name = of_clk_get_parent_name(np, i); | ||
| 181 | |||
| 182 | regmap = syscon_node_to_regmap(np); | ||
| 183 | if (IS_ERR(regmap)) | ||
| 184 | return; | ||
| 185 | |||
| 186 | sam9x60_pmc = pmc_data_allocate(PMC_MAIN + 1, | ||
| 187 | nck(sam9x60_systemck), | ||
| 188 | nck(sam9x60_periphck), | ||
| 189 | nck(sam9x60_gck)); | ||
| 190 | if (!sam9x60_pmc) | ||
| 191 | return; | ||
| 192 | |||
| 193 | hw = at91_clk_register_main_rc_osc(regmap, "main_rc_osc", 24000000, | ||
| 194 | 50000000); | ||
| 195 | if (IS_ERR(hw)) | ||
| 196 | goto err_free; | ||
| 197 | |||
| 198 | bypass = of_property_read_bool(np, "atmel,osc-bypass"); | ||
| 199 | |||
| 200 | hw = at91_clk_register_main_osc(regmap, "main_osc", mainxtal_name, | ||
| 201 | bypass); | ||
| 202 | if (IS_ERR(hw)) | ||
| 203 | goto err_free; | ||
| 204 | |||
| 205 | parent_names[0] = "main_rc_osc"; | ||
| 206 | parent_names[1] = "main_osc"; | ||
| 207 | hw = at91_clk_register_sam9x5_main(regmap, "mainck", parent_names, 2); | ||
| 208 | if (IS_ERR(hw)) | ||
| 209 | goto err_free; | ||
| 210 | |||
| 211 | sam9x60_pmc->chws[PMC_MAIN] = hw; | ||
| 212 | |||
| 213 | hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "pllack", | ||
| 214 | "mainck", 0, &plla_characteristics); | ||
| 215 | if (IS_ERR(hw)) | ||
| 216 | goto err_free; | ||
| 217 | |||
| 218 | hw = sam9x60_clk_register_pll(regmap, &pmc_pll_lock, "upllck", | ||
| 219 | "main_osc", 1, &upll_characteristics); | ||
| 220 | if (IS_ERR(hw)) | ||
| 221 | goto err_free; | ||
| 222 | |||
| 223 | sam9x60_pmc->chws[PMC_UTMI] = hw; | ||
| 224 | |||
| 225 | parent_names[0] = md_slck_name; | ||
| 226 | parent_names[1] = "mainck"; | ||
| 227 | parent_names[2] = "pllack"; | ||
| 228 | hw = at91_clk_register_master(regmap, "masterck", 3, parent_names, | ||
| 229 | &sam9x60_master_layout, | ||
| 230 | &mck_characteristics); | ||
| 231 | if (IS_ERR(hw)) | ||
| 232 | goto err_free; | ||
| 233 | |||
| 234 | sam9x60_pmc->chws[PMC_MCK] = hw; | ||
| 235 | |||
| 236 | parent_names[0] = "pllack"; | ||
| 237 | parent_names[1] = "upllck"; | ||
| 238 | parent_names[2] = "mainck"; | ||
| 239 | parent_names[3] = "mainck"; | ||
| 240 | hw = sam9x60_clk_register_usb(regmap, "usbck", parent_names, 4); | ||
| 241 | if (IS_ERR(hw)) | ||
| 242 | goto err_free; | ||
| 243 | |||
| 244 | parent_names[0] = md_slck_name; | ||
| 245 | parent_names[1] = td_slck_name; | ||
| 246 | parent_names[2] = "mainck"; | ||
| 247 | parent_names[3] = "masterck"; | ||
| 248 | parent_names[4] = "pllack"; | ||
| 249 | parent_names[5] = "upllck"; | ||
| 250 | for (i = 0; i < 8; i++) { | ||
| 251 | char name[6]; | ||
| 252 | |||
| 253 | snprintf(name, sizeof(name), "prog%d", i); | ||
| 254 | |||
| 255 | hw = at91_clk_register_programmable(regmap, name, | ||
| 256 | parent_names, 6, i, | ||
| 257 | &sam9x60_programmable_layout); | ||
| 258 | if (IS_ERR(hw)) | ||
| 259 | goto err_free; | ||
| 260 | } | ||
| 261 | |||
| 262 | for (i = 0; i < ARRAY_SIZE(sam9x60_systemck); i++) { | ||
| 263 | hw = at91_clk_register_system(regmap, sam9x60_systemck[i].n, | ||
| 264 | sam9x60_systemck[i].p, | ||
| 265 | sam9x60_systemck[i].id); | ||
| 266 | if (IS_ERR(hw)) | ||
| 267 | goto err_free; | ||
| 268 | |||
| 269 | sam9x60_pmc->shws[sam9x60_systemck[i].id] = hw; | ||
| 270 | } | ||
| 271 | |||
| 272 | for (i = 0; i < ARRAY_SIZE(sam9x60_periphck); i++) { | ||
| 273 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, | ||
| 274 | &sam9x60_pcr_layout, | ||
| 275 | sam9x60_periphck[i].n, | ||
| 276 | "masterck", | ||
| 277 | sam9x60_periphck[i].id, | ||
| 278 | &range); | ||
| 279 | if (IS_ERR(hw)) | ||
| 280 | goto err_free; | ||
| 281 | |||
| 282 | sam9x60_pmc->phws[sam9x60_periphck[i].id] = hw; | ||
| 283 | } | ||
| 284 | |||
| 285 | for (i = 0; i < ARRAY_SIZE(sam9x60_gck); i++) { | ||
| 286 | hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, | ||
| 287 | &sam9x60_pcr_layout, | ||
| 288 | sam9x60_gck[i].n, | ||
| 289 | parent_names, 6, | ||
| 290 | sam9x60_gck[i].id, | ||
| 291 | sam9x60_gck[i].pll, | ||
| 292 | &sam9x60_gck[i].r); | ||
| 293 | if (IS_ERR(hw)) | ||
| 294 | goto err_free; | ||
| 295 | |||
| 296 | sam9x60_pmc->ghws[sam9x60_gck[i].id] = hw; | ||
| 297 | } | ||
| 298 | |||
| 299 | of_clk_add_hw_provider(np, of_clk_hw_pmc_get, sam9x60_pmc); | ||
| 300 | |||
| 301 | return; | ||
| 302 | |||
| 303 | err_free: | ||
| 304 | pmc_data_free(sam9x60_pmc); | ||
| 305 | } | ||
| 306 | /* Some clks are used for a clocksource */ | ||
| 307 | CLK_OF_DECLARE(sam9x60_pmc, "microchip,sam9x60-pmc", sam9x60_pmc_setup); | ||
| @@ -16,7 +16,7 @@ static u8 plla_out[] = { 0 }; · 15 unmodified lines | |||
| 16 | 16 | ||
| 17 | static u16 plla_icpll[] = { 0 }; | 17 | static u16 plla_icpll[] = { 0 }; |
| 18 | 18 | ||
| 19 | static struct clk_range plla_outputs[] = { | 19 | static const struct clk_range plla_outputs[] = { |
| 20 | { .min = 600000000, .max = 1200000000 }, | 20 | { .min = 600000000, .max = 1200000000 }, |
| 21 | }; | 21 | }; |
| 22 | 22 | ||
| @@ -28,6 +28,13 @@ static const struct clk_pll_characteristics plla_characteristics = { · 5 unmodified lines | |||
| 28 | .out = plla_out, | 28 | .out = plla_out, |
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | static const struct clk_pcr_layout sama5d2_pcr_layout = { | ||
| 32 | .offset = 0x10c, | ||
| 33 | .cmd = BIT(12), | ||
| 34 | .gckcss_mask = GENMASK(10, 8), | ||
| 35 | .pid_mask = GENMASK(6, 0), | ||
| 36 | }; | ||
| 37 | |||
| 31 | static const struct { | 38 | static const struct { |
| 32 | char *n; | 39 | char *n; |
| 33 | char *p; | 40 | char *p; |
| @@ -274,6 +281,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np) · 240 unmodified lines | |||
| 274 | 281 | ||
| 275 | for (i = 0; i < ARRAY_SIZE(sama5d2_periphck); i++) { | 282 | for (i = 0; i < ARRAY_SIZE(sama5d2_periphck); i++) { |
| 276 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, | 283 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, |
| 284 | &sama5d2_pcr_layout, | ||
| 277 | sama5d2_periphck[i].n, | 285 | sama5d2_periphck[i].n, |
| 278 | "masterck", | 286 | "masterck", |
| 279 | sama5d2_periphck[i].id, | 287 | sama5d2_periphck[i].id, |
| @@ -286,6 +294,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np) · 6 unmodified lines | |||
| 286 | 294 | ||
| 287 | for (i = 0; i < ARRAY_SIZE(sama5d2_periph32ck); i++) { | 295 | for (i = 0; i < ARRAY_SIZE(sama5d2_periph32ck); i++) { |
| 288 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, | 296 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, |
| 297 | &sama5d2_pcr_layout, | ||
| 289 | sama5d2_periph32ck[i].n, | 298 | sama5d2_periph32ck[i].n, |
| 290 | "h32mxck", | 299 | "h32mxck", |
| 291 | sama5d2_periph32ck[i].id, | 300 | sama5d2_periph32ck[i].id, |
| @@ -304,6 +313,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np) · 12 unmodified lines | |||
| 304 | parent_names[5] = "audiopll_pmcck"; | 313 | parent_names[5] = "audiopll_pmcck"; |
| 305 | for (i = 0; i < ARRAY_SIZE(sama5d2_gck); i++) { | 314 | for (i = 0; i < ARRAY_SIZE(sama5d2_gck); i++) { |
| 306 | hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, | 315 | hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, |
| 316 | &sama5d2_pcr_layout, | ||
| 307 | sama5d2_gck[i].n, | 317 | sama5d2_gck[i].n, |
| 308 | parent_names, 6, | 318 | parent_names, 6, |
| 309 | sama5d2_gck[i].id, | 319 | sama5d2_gck[i].id, |
| @@ -16,7 +16,7 @@ static u8 plla_out[] = { 0 }; · 15 unmodified lines | |||
| 16 | 16 | ||
| 17 | static u16 plla_icpll[] = { 0 }; | 17 | static u16 plla_icpll[] = { 0 }; |
| 18 | 18 | ||
| 19 | static struct clk_range plla_outputs[] = { | 19 | static const struct clk_range plla_outputs[] = { |
| 20 | { .min = 600000000, .max = 1200000000 }, | 20 | { .min = 600000000, .max = 1200000000 }, |
| 21 | }; | 21 | }; |
| 22 | 22 | ||
| @@ -28,6 +28,12 @@ static const struct clk_pll_characteristics plla_characteristics = { · 5 unmodified lines | |||
| 28 | .out = plla_out, | 28 | .out = plla_out, |
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | static const struct clk_pcr_layout sama5d4_pcr_layout = { | ||
| 32 | .offset = 0x10c, | ||
| 33 | .cmd = BIT(12), | ||
| 34 | .pid_mask = GENMASK(6, 0), | ||
| 35 | }; | ||
| 36 | |||
| 31 | static const struct { | 37 | static const struct { |
| 32 | char *n; | 38 | char *n; |
| 33 | char *p; | 39 | char *p; |
| @@ -232,6 +238,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np) · 198 unmodified lines | |||
| 232 | 238 | ||
| 233 | for (i = 0; i < ARRAY_SIZE(sama5d4_periphck); i++) { | 239 | for (i = 0; i < ARRAY_SIZE(sama5d4_periphck); i++) { |
| 234 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, | 240 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, |
| 241 | &sama5d4_pcr_layout, | ||
| 235 | sama5d4_periphck[i].n, | 242 | sama5d4_periphck[i].n, |
| 236 | "masterck", | 243 | "masterck", |
| 237 | sama5d4_periphck[i].id, | 244 | sama5d4_periphck[i].id, |
| @@ -244,6 +251,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np) · 6 unmodified lines | |||
| 244 | 251 | ||
| 245 | for (i = 0; i < ARRAY_SIZE(sama5d4_periph32ck); i++) { | 252 | for (i = 0; i < ARRAY_SIZE(sama5d4_periph32ck); i++) { |
| 246 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, | 253 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, |
| 254 | &sama5d4_pcr_layout, | ||
| 247 | sama5d4_periph32ck[i].n, | 255 | sama5d4_periph32ck[i].n, |
| 248 | "h32mxck", | 256 | "h32mxck", |
| 249 | sama5d4_periph32ck[i].id, | 257 | sama5d4_periph32ck[i].id, |
| @@ -152,28 +152,6 @@ at91_clk_register_slow_osc(void __iomem *sckcr, · 151 unmodified lines | |||
| 152 | return hw; | 152 | return hw; |
| 153 | } | 153 | } |
| 154 | 154 | ||
| 155 | static void __init | ||
| 156 | of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, void __iomem *sckcr) | ||
| 157 | { | ||
| 158 | struct clk_hw *hw; | ||
| 159 | const char *parent_name; | ||
| 160 | const char *name = np->name; | ||
| 161 | u32 startup; | ||
| 162 | bool bypass; | ||
| 163 | |||
| 164 | parent_name = of_clk_get_parent_name(np, 0); | ||
| 165 | of_property_read_string(np, "clock-output-names", &name); | ||
| 166 | of_property_read_u32(np, "atmel,startup-time-usec", &startup); | ||
| 167 | bypass = of_property_read_bool(np, "atmel,osc-bypass"); | ||
| 168 | |||
| 169 | hw = at91_clk_register_slow_osc(sckcr, name, parent_name, startup, | ||
| 170 | bypass); | ||
| 171 | if (IS_ERR(hw)) | ||
| 172 | return; | ||
| 173 | |||
| 174 | of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); | ||
| 175 | } | ||
| 176 | |||
| 177 | static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, | 155 | static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, |
| 178 | unsigned long parent_rate) | 156 | unsigned long parent_rate) |
| 179 | { | 157 | { |
| @@ -266,28 +244,6 @@ at91_clk_register_slow_rc_osc(void __iomem *sckcr, · 86 unmodified lines | |||
| 266 | return hw; | 244 | return hw; |
| 267 | } | 245 | } |
| 268 | 246 | ||
| 269 | static void __init | ||
| 270 | of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, void __iomem *sckcr) | ||
| 271 | { | ||
| 272 | struct clk_hw *hw; | ||
| 273 | u32 frequency = 0; | ||
| 274 | u32 accuracy = 0; | ||
| 275 | u32 startup = 0; | ||
| 276 | const char *name = np->name; | ||
| 277 | |||
| 278 | of_property_read_string(np, "clock-output-names", &name); | ||
| 279 | of_property_read_u32(np, "clock-frequency", &frequency); | ||
| 280 | of_property_read_u32(np, "clock-accuracy", &accuracy); | ||
| 281 | of_property_read_u32(np, "atmel,startup-time-usec", &startup); | ||
| 282 | |||
| 283 | hw = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy, | ||
| 284 | startup); | ||
| 285 | if (IS_ERR(hw)) | ||
| 286 | return; | ||
| 287 | |||
| 288 | of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); | ||
| 289 | } | ||
| 290 | |||
| 291 | static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) | 247 | static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) |
| 292 | { | 248 | { |
| 293 | struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); | 249 | struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); |
| @@ -365,68 +321,72 @@ at91_clk_register_sam9x5_slow(void __iomem *sckcr, · 71 unmodified lines | |||
| 365 | return hw; | 321 | return hw; |
| 366 | } | 322 | } |
| 367 | 323 | ||
| 368 | static void __init | 324 | static void __init at91sam9x5_sckc_register(struct device_node *np, |
| 369 | of_at91sam9x5_clk_slow_setup(struct device_node *np, void __iomem *sckcr) | 325 | unsigned int rc_osc_startup_us) |
| 370 | { | 326 | { |
| 327 | const char *parent_names[2] = { "slow_rc_osc", "slow_osc" }; | ||
| 328 | void __iomem *regbase = of_iomap(np, 0); | ||
| 329 | struct device_node *child = NULL; | ||
| 330 | const char *xtal_name; | ||
| 371 | struct clk_hw *hw; | 331 | struct clk_hw *hw; |
| 372 | const char *parent_names[2]; | 332 | bool bypass; |
| 373 | unsigned int num_parents; | ||
| 374 | const char *name = np->name; | ||
| 375 | 333 | ||
| 376 | num_parents = of_clk_get_parent_count(np); | 334 | if (!regbase) |
| 377 | if (num_parents == 0 || num_parents > 2) | 335 | return; |
| 336 | |||
| 337 | hw = at91_clk_register_slow_rc_osc(regbase, parent_names[0], 32768, | ||
| 338 | 50000000, rc_osc_startup_us); | ||
| 339 | if (IS_ERR(hw)) | ||
| 378 | return; | 340 | return; |
| 379 | 341 | ||
| 380 | of_clk_parent_fill(np, parent_names, num_parents); | 342 | xtal_name = of_clk_get_parent_name(np, 0); |
| 343 | if (!xtal_name) { | ||
| 344 | /* DT backward compatibility */ | ||
| 345 | child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc"); | ||
| 346 | if (!child) | ||
| 347 | return; | ||
| 348 | |||
| 349 | xtal_name = of_clk_get_parent_name(child, 0); | ||
| 350 | bypass = of_property_read_bool(child, "atmel,osc-bypass"); | ||
| 351 | |||
| 352 | child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow"); | ||
| 353 | } else { | ||
| 354 | bypass = of_property_read_bool(np, "atmel,osc-bypass"); | ||
| 355 | } | ||
| 356 | |||
| 357 | if (!xtal_name) | ||
| 358 | return; | ||
| 381 | 359 | ||
| 382 | of_property_read_string(np, "clock-output-names", &name); | 360 | hw = at91_clk_register_slow_osc(regbase, parent_names[1], xtal_name, |
| 361 | 1200000, bypass); | ||
| 362 | if (IS_ERR(hw)) | ||
| 363 | return; | ||
| 383 | 364 | ||
| 384 | hw = at91_clk_register_sam9x5_slow(sckcr, name, parent_names, | 365 | hw = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2); |
| 385 | num_parents); | ||
| 386 | if (IS_ERR(hw)) | 366 | if (IS_ERR(hw)) |
| 387 | return; | 367 | return; |
| 388 | 368 | ||
| 389 | of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); | 369 | of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); |
| 390 | } | ||
| 391 | 370 | ||
| 392 | static const struct of_device_id sckc_clk_ids[] __initconst = { | 371 | /* DT backward compatibility */ |
| 393 | /* Slow clock */ | 372 | if (child) |
| 394 | { | 373 | of_clk_add_hw_provider(child, of_clk_hw_simple_get, hw); |
| 395 | .compatible = "atmel,at91sam9x5-clk-slow-osc", | 374 | } |
| 396 | .data = of_at91sam9x5_clk_slow_osc_setup, | ||
| 397 | }, | ||
| 398 | { | ||
| 399 | .compatible = "atmel,at91sam9x5-clk-slow-rc-osc", | ||
| 400 | .data = of_at91sam9x5_clk_slow_rc_osc_setup, | ||
| 401 | }, | ||
| 402 | { | ||
| 403 | .compatible = "atmel,at91sam9x5-clk-slow", | ||
| 404 | .data = of_at91sam9x5_clk_slow_setup, | ||
| 405 | }, | ||
| 406 | { /*sentinel*/ } | ||
| 407 | }; | ||
| 408 | 375 | ||
| 409 | static void __init of_at91sam9x5_sckc_setup(struct device_node *np) | 376 | static void __init of_at91sam9x5_sckc_setup(struct device_node *np) |
| 410 | { | 377 | { |
| 411 | struct device_node *childnp; | 378 | at91sam9x5_sckc_register(np, 75); |
| 412 | void (*clk_setup)(struct device_node *, void __iomem *); | ||
| 413 | const struct of_device_id *clk_id; | ||
| 414 | void __iomem *regbase = of_iomap(np, 0); | ||
| 415 | |||
| 416 | if (!regbase) | ||
| 417 | return; | ||
| 418 | |||
| 419 | for_each_child_of_node(np, childnp) { | ||
| 420 | clk_id = of_match_node(sckc_clk_ids, childnp); | ||
| 421 | if (!clk_id) | ||
| 422 | continue; | ||
| 423 | clk_setup = clk_id->data; | ||
| 424 | clk_setup(childnp, regbase); | ||
| 425 | } | ||
| 426 | } | 379 | } |
| 427 | CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc", | 380 | CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc", |
| 428 | of_at91sam9x5_sckc_setup); | 381 | of_at91sam9x5_sckc_setup); |
| 429 | 382 | ||
| 383 | static void __init of_sama5d3_sckc_setup(struct device_node *np) | ||
| 384 | { | ||
| 385 | at91sam9x5_sckc_register(np, 500); | ||
| 386 | } | ||
| 387 | CLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc", | ||
| 388 | of_sama5d3_sckc_setup); | ||
| 389 | |||
| 430 | static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw) | 390 | static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw) |
| 431 | { | 391 | { |
| 432 | struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); | 392 | struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw); |
| @@ -300,6 +300,85 @@ static const struct stm32f4_gate_data stm32f746_gates[] __initconst = { · 299 unmodified lines | |||
| 300 | { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, | 300 | { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, |
| 301 | }; | 301 | }; |
| 302 | 302 | ||
| 303 | static const struct stm32f4_gate_data stm32f769_gates[] __initconst = { | ||
| 304 | { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, | ||
| 305 | { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, | ||
| 306 | { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, | ||
| 307 | { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" }, | ||
| 308 | { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" }, | ||
| 309 | { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" }, | ||
| 310 | { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" }, | ||
| 311 | { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" }, | ||
| 312 | { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" }, | ||
| 313 | { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" }, | ||
| 314 | { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" }, | ||
| 315 | { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" }, | ||
| 316 | { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" }, | ||
| 317 | { STM32F4_RCC_AHB1ENR, 20, "dtcmram", "ahb_div" }, | ||
| 318 | { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" }, | ||
| 319 | { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" }, | ||
| 320 | { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" }, | ||
| 321 | { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" }, | ||
| 322 | { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" }, | ||
| 323 | { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" }, | ||
| 324 | { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" }, | ||
| 325 | { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" }, | ||
| 326 | { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" }, | ||
| 327 | |||
| 328 | { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" }, | ||
| 329 | { STM32F4_RCC_AHB2ENR, 1, "jpeg", "ahb_div" }, | ||
| 330 | { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" }, | ||
| 331 | { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" }, | ||
| 332 | { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" }, | ||
| 333 | { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" }, | ||
| 334 | |||
| 335 | { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div", | ||
| 336 | CLK_IGNORE_UNUSED }, | ||
| 337 | { STM32F4_RCC_AHB3ENR, 1, "qspi", "ahb_div", | ||
| 338 | CLK_IGNORE_UNUSED }, | ||
| 339 | |||
| 340 | { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" }, | ||
| 341 | { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" }, | ||
| 342 | { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" }, | ||
| 343 | { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" }, | ||
| 344 | { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" }, | ||
| 345 | { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" }, | ||
| 346 | { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" }, | ||
| 347 | { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" }, | ||
| 348 | { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" }, | ||
| 349 | { STM32F4_RCC_APB1ENR, 10, "rtcapb", "apb1_mul" }, | ||
| 350 | { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" }, | ||
| 351 | { STM32F4_RCC_APB1ENR, 13, "can3", "apb1_div" }, | ||
| 352 | { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" }, | ||
| 353 | { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" }, | ||
| 354 | { STM32F4_RCC_APB1ENR, 16, "spdifrx", "apb1_div" }, | ||
| 355 | { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" }, | ||
| 356 | { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" }, | ||
| 357 | { STM32F4_RCC_APB1ENR, 27, "cec", "apb1_div" }, | ||
| 358 | { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" }, | ||
| 359 | { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" }, | ||
| 360 | |||
| 361 | { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" }, | ||
| 362 | { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" }, | ||
| 363 | { STM32F4_RCC_APB2ENR, 7, "sdmmc2", "sdmux2" }, | ||
| 364 | { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, | ||
| 365 | { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, | ||
| 366 | { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, | ||
| 367 | { STM32F4_RCC_APB2ENR, 11, "sdmmc1", "sdmux1" }, | ||
| 368 | { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, | ||
| 369 | { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, | ||
| 370 | { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, | ||
| 371 | { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" }, | ||
| 372 | { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" }, | ||
| 373 | { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" }, | ||
| 374 | { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, | ||
| 375 | { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, | ||
| 376 | { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, | ||
| 377 | { STM32F4_RCC_APB2ENR, 23, "sai2", "apb2_div" }, | ||
| 378 | { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, | ||
| 379 | { STM32F4_RCC_APB2ENR, 30, "mdio", "apb2_div" }, | ||
| 380 | }; | ||
| 381 | |||
| 303 | /* | 382 | /* |
| 304 | * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx | 383 | * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx |
| 305 | * have gate bits associated with them. Its combined hweight is 71. | 384 | * 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, · 12 unmodified lines | |||
| 318 | 0x0000000000000003ull, | 397 | 0x0000000000000003ull, |
| 319 | 0x04f77f833e01c9ffull }; | 398 | 0x04f77f833e01c9ffull }; |
| 320 | 399 | ||
| 400 | static const u64 stm32f769_gate_map[MAX_GATE_MAP] = { 0x000000f37ef417ffull, | ||
| 401 | 0x0000000000000003ull, | ||
| 402 | 0x44F77F833E01EDFFull }; | ||
| 403 | |||
| 321 | static const u64 *stm32f4_gate_map; | 404 | static const u64 *stm32f4_gate_map; |
| 322 | 405 | ||
| 323 | static struct clk_hw **clks; | 406 | static struct clk_hw **clks; |
| @@ -1048,6 +1131,10 @@ static const char *rtc_parents[4] = { · 724 unmodified lines | |||
| 1048 | "no-clock", "lse", "lsi", "hse-rtc" | 1131 | "no-clock", "lse", "lsi", "hse-rtc" |
| 1049 | }; | 1132 | }; |
| 1050 | 1133 | ||
| 1134 | static const char *pll_src = "pll-src"; | ||
| 1135 | |||
| 1136 | static const char *pllsrc_parent[2] = { "hsi", NULL }; | ||
| 1137 | |||
| 1051 | static const char *dsi_parent[2] = { NULL, "pll-r" }; | 1138 | static const char *dsi_parent[2] = { NULL, "pll-r" }; |
| 1052 | 1139 | ||
| 1053 | static const char *lcd_parent[1] = { "pllsai-r-div" }; | 1140 | static const char *lcd_parent[1] = { "pllsai-r-div" }; |
| @@ -1072,6 +1159,9 @@ static const char *uart_parents2[4] = { "apb1_div", "sys", "hsi", "lse" }; · 18 unmodified lines | |||
| 1072 | 1159 | ||
| 1073 | static const char *i2c_parents[4] = { "apb1_div", "sys", "hsi", "no-clock" }; | 1160 | static const char *i2c_parents[4] = { "apb1_div", "sys", "hsi", "no-clock" }; |
| 1074 | 1161 | ||
| 1162 | static const char * const dfsdm1_src[] = { "apb2_div", "sys" }; | ||
| 1163 | static const char * const adsfdm1_parent[] = { "sai1_clk", "sai2_clk" }; | ||
| 1164 | |||
| 1075 | struct stm32_aux_clk { | 1165 | struct stm32_aux_clk { |
| 1076 | int idx; | 1166 | int idx; |
| 1077 | const char *name; | 1167 | const char *name; |
| @@ -1313,6 +1403,177 @@ static const struct stm32_aux_clk stm32f746_aux_clk[] = { · 235 unmodified lines | |||
| 1313 | }, | 1403 | }, |
| 1314 | }; | 1404 | }; |
| 1315 | 1405 | ||
| 1406 | static const struct stm32_aux_clk stm32f769_aux_clk[] = { | ||
| 1407 | { | ||
| 1408 | CLK_LCD, "lcd-tft", lcd_parent, ARRAY_SIZE(lcd_parent), | ||
| 1409 | NO_MUX, 0, 0, | ||
| 1410 | STM32F4_RCC_APB2ENR, 26, | ||
| 1411 | CLK_SET_RATE_PARENT | ||
| 1412 | }, | ||
| 1413 | { | ||
| 1414 | CLK_I2S, "i2s", i2s_parents, ARRAY_SIZE(i2s_parents), | ||
| 1415 | STM32F4_RCC_CFGR, 23, 1, | ||
| 1416 | NO_GATE, 0, | ||
| 1417 | CLK_SET_RATE_PARENT | ||
| 1418 | }, | ||
| 1419 | { | ||
| 1420 | CLK_SAI1, "sai1_clk", sai_parents, ARRAY_SIZE(sai_parents), | ||
| 1421 | STM32F4_RCC_DCKCFGR, 20, 3, | ||
| 1422 | STM32F4_RCC_APB2ENR, 22, | ||
| 1423 | CLK_SET_RATE_PARENT | ||
| 1424 | }, | ||
| 1425 | { | ||
| 1426 | CLK_SAI2, "sai2_clk", sai_parents, ARRAY_SIZE(sai_parents), | ||
| 1427 | STM32F4_RCC_DCKCFGR, 22, 3, | ||
| 1428 | STM32F4_RCC_APB2ENR, 23, | ||
| 1429 | CLK_SET_RATE_PARENT | ||
| 1430 | }, | ||
| 1431 | { | ||
| 1432 | NO_IDX, "pll48", pll48_parents, ARRAY_SIZE(pll48_parents), | ||
| 1433 | STM32F7_RCC_DCKCFGR2, 27, 1, | ||
| 1434 | NO_GATE, 0, | ||
| 1435 | 0 | ||
| 1436 | }, | ||
| 1437 | { | ||
| 1438 | NO_IDX, "sdmux1", sdmux_parents, ARRAY_SIZE(sdmux_parents), | ||
| 1439 | STM32F7_RCC_DCKCFGR2, 28, 1, | ||
| 1440 | NO_GATE, 0, | ||
| 1441 | 0 | ||
| 1442 | }, | ||
| 1443 | { | ||
| 1444 | NO_IDX, "sdmux2", sdmux_parents, ARRAY_SIZE(sdmux_parents), | ||
| 1445 | STM32F7_RCC_DCKCFGR2, 29, 1, | ||
| 1446 | NO_GATE, 0, | ||
| 1447 | 0 | ||
| 1448 | }, | ||
| 1449 | { | ||
| 1450 | CLK_HDMI_CEC, "hdmi-cec", | ||
| 1451 | hdmi_parents, ARRAY_SIZE(hdmi_parents), | ||
| 1452 | STM32F7_RCC_DCKCFGR2, 26, 1, | ||
| 1453 | NO_GATE, 0, | ||
| 1454 | 0 | ||
| 1455 | }, | ||
| 1456 | { | ||
| 1457 | CLK_SPDIF, "spdif-rx", | ||
| 1458 | spdif_parent, ARRAY_SIZE(spdif_parent), | ||
| 1459 | STM32F7_RCC_DCKCFGR2, 22, 3, | ||
| 1460 | STM32F4_RCC_APB2ENR, 23, | ||
| 1461 | CLK_SET_RATE_PARENT | ||
| 1462 | }, | ||
| 1463 | { | ||
| 1464 | CLK_USART1, "usart1", | ||
| 1465 | uart_parents1, ARRAY_SIZE(uart_parents1), | ||
| 1466 | STM32F7_RCC_DCKCFGR2, 0, 3, | ||
| 1467 | STM32F4_RCC_APB2ENR, 4, | ||
| 1468 | CLK_SET_RATE_PARENT, | ||
| 1469 | }, | ||
| 1470 | { | ||
| 1471 | CLK_USART2, "usart2", | ||
| 1472 | uart_parents2, ARRAY_SIZE(uart_parents1), | ||
| 1473 | STM32F7_RCC_DCKCFGR2, 2, 3, | ||
| 1474 | STM32F4_RCC_APB1ENR, 17, | ||
| 1475 | CLK_SET_RATE_PARENT, | ||
| 1476 | }, | ||
| 1477 | { | ||
| 1478 | CLK_USART3, "usart3", | ||
| 1479 | uart_parents2, ARRAY_SIZE(uart_parents1), | ||
| 1480 | STM32F7_RCC_DCKCFGR2, 4, 3, | ||
| 1481 | STM32F4_RCC_APB1ENR, 18, | ||
| 1482 | CLK_SET_RATE_PARENT, | ||
| 1483 | }, | ||
| 1484 | { | ||
| 1485 | CLK_UART4, "uart4", | ||
| 1486 | uart_parents2, ARRAY_SIZE(uart_parents1), | ||
| 1487 | STM32F7_RCC_DCKCFGR2, 6, 3, | ||
| 1488 | STM32F4_RCC_APB1ENR, 19, | ||
| 1489 | CLK_SET_RATE_PARENT, | ||
| 1490 | }, | ||
| 1491 | { | ||
| 1492 | CLK_UART5, "uart5", | ||
| 1493 | uart_parents2, ARRAY_SIZE(uart_parents1), | ||
| 1494 | STM32F7_RCC_DCKCFGR2, 8, 3, | ||
| 1495 | STM32F4_RCC_APB1ENR, 20, | ||
| 1496 | CLK_SET_RATE_PARENT, | ||
| 1497 | }, | ||
| 1498 | { | ||
| 1499 | CLK_USART6, "usart6", | ||
| 1500 | uart_parents1, ARRAY_SIZE(uart_parents1), | ||
| 1501 | STM32F7_RCC_DCKCFGR2, 10, 3, | ||
| 1502 | STM32F4_RCC_APB2ENR, 5, | ||
| 1503 | CLK_SET_RATE_PARENT, | ||
| 1504 | }, | ||
| 1505 | { | ||
| 1506 | CLK_UART7, "uart7", | ||
| 1507 | uart_parents2, ARRAY_SIZE(uart_parents1), | ||
| 1508 | STM32F7_RCC_DCKCFGR2, 12, 3, | ||
| 1509 | STM32F4_RCC_APB1ENR, 30, | ||
| 1510 | CLK_SET_RATE_PARENT, | ||
| 1511 | }, | ||
| 1512 | { | ||
| 1513 | CLK_UART8, "uart8", | ||
| 1514 | uart_parents2, ARRAY_SIZE(uart_parents1), | ||
| 1515 | STM32F7_RCC_DCKCFGR2, 14, 3, | ||
| 1516 | STM32F4_RCC_APB1ENR, 31, | ||
| 1517 | CLK_SET_RATE_PARENT, | ||
| 1518 | }, | ||
| 1519 | { | ||
| 1520 | CLK_I2C1, "i2c1", | ||
| 1521 | i2c_parents, ARRAY_SIZE(i2c_parents), | ||
| 1522 | STM32F7_RCC_DCKCFGR2, 16, 3, | ||
| 1523 | STM32F4_RCC_APB1ENR, 21, | ||
| 1524 | CLK_SET_RATE_PARENT, | ||
| 1525 | }, | ||
| 1526 | { | ||
| 1527 | CLK_I2C2, "i2c2", | ||
| 1528 | i2c_parents, ARRAY_SIZE(i2c_parents), | ||
| 1529 | STM32F7_RCC_DCKCFGR2, 18, 3, | ||
| 1530 | STM32F4_RCC_APB1ENR, 22, | ||
| 1531 | CLK_SET_RATE_PARENT, | ||
| 1532 | }, | ||
| 1533 | { | ||
| 1534 | CLK_I2C3, "i2c3", | ||
| 1535 | i2c_parents, ARRAY_SIZE(i2c_parents), | ||
| 1536 | STM32F7_RCC_DCKCFGR2, 20, 3, | ||
| 1537 | STM32F4_RCC_APB1ENR, 23, | ||
| 1538 | CLK_SET_RATE_PARENT, | ||
| 1539 | }, | ||
| 1540 | { | ||
| 1541 | CLK_I2C4, "i2c4", | ||
| 1542 | i2c_parents, ARRAY_SIZE(i2c_parents), | ||
| 1543 | STM32F7_RCC_DCKCFGR2, 22, 3, | ||
| 1544 | STM32F4_RCC_APB1ENR, 24, | ||
| 1545 | CLK_SET_RATE_PARENT, | ||
| 1546 | }, | ||
| 1547 | { | ||
| 1548 | CLK_LPTIMER, "lptim1", | ||
| 1549 | lptim_parent, ARRAY_SIZE(lptim_parent), | ||
| 1550 | STM32F7_RCC_DCKCFGR2, 24, 3, | ||
| 1551 | STM32F4_RCC_APB1ENR, 9, | ||
| 1552 | CLK_SET_RATE_PARENT | ||
| 1553 | }, | ||
| 1554 | { | ||
| 1555 | CLK_F769_DSI, "dsi", | ||
| 1556 | dsi_parent, ARRAY_SIZE(dsi_parent), | ||
| 1557 | STM32F7_RCC_DCKCFGR2, 0, 1, | ||
| 1558 | STM32F4_RCC_APB2ENR, 27, | ||
| 1559 | CLK_SET_RATE_PARENT | ||
| 1560 | }, | ||
| 1561 | { | ||
| 1562 | CLK_DFSDM1, "dfsdm1", | ||
| 1563 | dfsdm1_src, ARRAY_SIZE(dfsdm1_src), | ||
| 1564 | STM32F4_RCC_DCKCFGR, 25, 1, | ||
| 1565 | STM32F4_RCC_APB2ENR, 29, | ||
| 1566 | CLK_SET_RATE_PARENT | ||
| 1567 | }, | ||
| 1568 | { | ||
| 1569 | CLK_ADFSDM1, "adfsdm1", | ||
| 1570 | adsfdm1_parent, ARRAY_SIZE(adsfdm1_parent), | ||
| 1571 | STM32F4_RCC_DCKCFGR, 26, 1, | ||
| 1572 | STM32F4_RCC_APB2ENR, 29, | ||
| 1573 | CLK_SET_RATE_PARENT | ||
| 1574 | }, | ||
| 1575 | }; | ||
| 1576 | |||
| 1316 | static const struct stm32f4_clk_data stm32f429_clk_data = { | 1577 | static const struct stm32f4_clk_data stm32f429_clk_data = { |
| 1317 | .end_primary = END_PRIMARY_CLK, | 1578 | .end_primary = END_PRIMARY_CLK, |
| 1318 | .gates_data = stm32f429_gates, | 1579 | .gates_data = stm32f429_gates, |
| @@ -1343,6 +1604,16 @@ static const struct stm32f4_clk_data stm32f746_clk_data = { · 24 unmodified lines | |||
| 1343 | .aux_clk_num = ARRAY_SIZE(stm32f746_aux_clk), | 1604 | .aux_clk_num = ARRAY_SIZE(stm32f746_aux_clk), |
| 1344 | }; | 1605 | }; |
| 1345 | 1606 | ||
| 1607 | static const struct stm32f4_clk_data stm32f769_clk_data = { | ||
| 1608 | .end_primary = END_PRIMARY_CLK_F7, | ||
| 1609 | .gates_data = stm32f769_gates, | ||
| 1610 | .gates_map = stm32f769_gate_map, | ||
| 1611 | .gates_num = ARRAY_SIZE(stm32f769_gates), | ||
| 1612 | .pll_data = stm32f469_pll, | ||
| 1613 | .aux_clk = stm32f769_aux_clk, | ||
| 1614 | .aux_clk_num = ARRAY_SIZE(stm32f769_aux_clk), | ||
| 1615 | }; | ||
| 1616 | |||
| 1346 | static const struct of_device_id stm32f4_of_match[] = { | 1617 | static const struct of_device_id stm32f4_of_match[] = { |
| 1347 | { | 1618 | { |
| 1348 | .compatible = "st,stm32f42xx-rcc", | 1619 | .compatible = "st,stm32f42xx-rcc", |
| @@ -1356,6 +1627,10 @@ static const struct of_device_id stm32f4_of_match[] = { · 7 unmodified lines | |||
| 1356 | .compatible = "st,stm32f746-rcc", | 1627 | .compatible = "st,stm32f746-rcc", |
| 1357 | .data = &stm32f746_clk_data | 1628 | .data = &stm32f746_clk_data |
| 1358 | }, | 1629 | }, |
| 1630 | { | ||
| 1631 | .compatible = "st,stm32f769-rcc", | ||
| 1632 | .data = &stm32f769_clk_data | ||
| 1633 | }, | ||
| 1359 | {} | 1634 | {} |
| 1360 | }; | 1635 | }; |
| 1361 | 1636 | ||
| @@ -1427,9 +1702,8 @@ static void __init stm32f4_rcc_init(struct device_node *np) · 65 unmodified lines | |||
| 1427 | int n; | 1702 | int n; |
| 1428 | const struct of_device_id *match; | 1703 | const struct of_device_id *match; |
| 1429 | const struct stm32f4_clk_data *data; | 1704 | const struct stm32f4_clk_data *data; |
| 1430 | unsigned long pllcfgr; | ||
| 1431 | const char *pllsrc; | ||
| 1432 | unsigned long pllm; | 1705 | unsigned long pllm; |
| 1706 | struct clk_hw *pll_src_hw; | ||
| 1433 | 1707 | ||
| 1434 | base = of_iomap(np, 0); | 1708 | base = of_iomap(np, 0); |
| 1435 | if (!base) { | 1709 | if (!base) { |
| @@ -1460,21 +1734,33 @@ static void __init stm32f4_rcc_init(struct device_node *np) · 24 unmodified lines | |||
| 1460 | 1734 | ||
| 1461 | hse_clk = of_clk_get_parent_name(np, 0); | 1735 | hse_clk = of_clk_get_parent_name(np, 0); |
| 1462 | dsi_parent[0] = hse_clk; | 1736 | dsi_parent[0] = hse_clk; |
| 1737 | pllsrc_parent[1] = hse_clk; | ||
| 1463 | 1738 | ||
| 1464 | i2s_in_clk = of_clk_get_parent_name(np, 1); | 1739 | i2s_in_clk = of_clk_get_parent_name(np, 1); |
| 1465 | 1740 | ||
| 1466 | i2s_parents[1] = i2s_in_clk; | 1741 | i2s_parents[1] = i2s_in_clk; |
| 1467 | sai_parents[2] = i2s_in_clk; | 1742 | sai_parents[2] = i2s_in_clk; |
| 1468 | 1743 | ||
| 1744 | if (of_device_is_compatible(np, "st,stm32f769-rcc")) { | ||
| 1745 | clk_hw_register_gate(NULL, "dfsdm1_apb", "apb2_div", 0, | ||
| 1746 | base + STM32F4_RCC_APB2ENR, 29, | ||
| 1747 | CLK_IGNORE_UNUSED, &stm32f4_clk_lock); | ||
| 1748 | dsi_parent[0] = pll_src; | ||
| 1749 | sai_parents[3] = pll_src; | ||
| 1750 | } | ||
| 1751 | |||
| 1469 | clks[CLK_HSI] = clk_hw_register_fixed_rate_with_accuracy(NULL, "hsi", | 1752 | clks[CLK_HSI] = clk_hw_register_fixed_rate_with_accuracy(NULL, "hsi", |
| 1470 | NULL, 0, 16000000, 160000); | 1753 | NULL, 0, 16000000, 160000); |
| 1471 | 1754 | ||
| 1472 | pllcfgr = readl(base + STM32F4_RCC_PLLCFGR); | 1755 | pll_src_hw = clk_hw_register_mux(NULL, pll_src, pllsrc_parent, |
| 1473 | pllsrc = pllcfgr & BIT(22) ? hse_clk : "hsi"; | 1756 | ARRAY_SIZE(pllsrc_parent), 0, |
| 1474 | pllm = pllcfgr & 0x3f; | 1757 | base + STM32F4_RCC_PLLCFGR, 22, 1, 0, |
| 1758 | &stm32f4_clk_lock); | ||
| 1759 | |||
| 1760 | pllm = readl(base + STM32F4_RCC_PLLCFGR) & 0x3f; | ||
| 1475 | 1761 | ||
| 1476 | clk_hw_register_fixed_factor(NULL, "vco_in", pllsrc, | 1762 | clk_hw_register_fixed_factor(NULL, "vco_in", pll_src, |
| 1477 | 0, 1, pllm); | 1763 | 0, 1, pllm); |
| 1478 | 1764 | ||
| 1479 | stm32f4_rcc_register_pll("vco_in", &data->pll_data[0], | 1765 | stm32f4_rcc_register_pll("vco_in", &data->pll_data[0], |
| 1480 | &stm32f4_clk_lock); | 1766 | &stm32f4_clk_lock); |
| @@ -1612,12 +1898,16 @@ static void __init stm32f4_rcc_init(struct device_node *np) · 131 unmodified lines | |||
| 1612 | clks[aux_clk->idx] = hw; | 1898 | clks[aux_clk->idx] = hw; |
| 1613 | } | 1899 | } |
| 1614 | 1900 | ||
| 1615 | if (of_device_is_compatible(np, "st,stm32f746-rcc")) | 1901 | if (of_device_is_compatible(np, "st,stm32f746-rcc")) { |
| 1616 | 1902 | ||
| 1617 | clk_hw_register_fixed_factor(NULL, "hsi_div488", "hsi", 0, | 1903 | clk_hw_register_fixed_factor(NULL, "hsi_div488", "hsi", 0, |
| 1618 | 1, 488); | 1904 | 1, 488); |
| 1619 | 1905 | ||
| 1906 | clks[CLK_PLL_SRC] = pll_src_hw; | ||
| 1907 | } | ||
| 1908 | |||
| 1620 | of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL); | 1909 | of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL); |
| 1910 | |||
| 1621 | return; | 1911 | return; |
| 1622 | fail: | 1912 | fail: |
| 1623 | kfree(clks); | 1913 | kfree(clks); |
| @@ -1626,3 +1916,4 @@ fail: · 2 unmodified lines | |||
| 1626 | CLK_OF_DECLARE_DRIVER(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init); | 1916 | CLK_OF_DECLARE_DRIVER(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init); |
| 1627 | CLK_OF_DECLARE_DRIVER(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init); | 1917 | CLK_OF_DECLARE_DRIVER(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init); |
| 1628 | CLK_OF_DECLARE_DRIVER(stm32f746_rcc, "st,stm32f746-rcc", stm32f4_rcc_init); | 1918 | CLK_OF_DECLARE_DRIVER(stm32f746_rcc, "st,stm32f746-rcc", stm32f4_rcc_init); |
| 1919 | CLK_OF_DECLARE_DRIVER(stm32f769_rcc, "st,stm32f769-rcc", stm32f4_rcc_init); | ||
| @@ -1402,6 +1402,7 @@ enum { · 1401 unmodified lines | |||
| 1402 | G_CRYP1, | 1402 | G_CRYP1, |
| 1403 | G_HASH1, | 1403 | G_HASH1, |
| 1404 | G_BKPSRAM, | 1404 | G_BKPSRAM, |
| 1405 | G_DDRPERFM, | ||
| 1405 | 1406 | ||
| 1406 | G_LAST | 1407 | G_LAST |
| 1407 | }; | 1408 | }; |
| @@ -1488,6 +1489,7 @@ static struct stm32_gate_cfg per_gate_cfg[G_LAST] = { · 80 unmodified lines | |||
| 1488 | K_GATE(G_STGENRO, RCC_APB4ENSETR, 20, 0), | 1489 | K_GATE(G_STGENRO, RCC_APB4ENSETR, 20, 0), |
| 1489 | K_MGATE(G_USBPHY, RCC_APB4ENSETR, 16, 0), | 1490 | K_MGATE(G_USBPHY, RCC_APB4ENSETR, 16, 0), |
| 1490 | K_GATE(G_IWDG2, RCC_APB4ENSETR, 15, 0), | 1491 | K_GATE(G_IWDG2, RCC_APB4ENSETR, 15, 0), |
| 1492 | K_GATE(G_DDRPERFM, RCC_APB4ENSETR, 8, 0), | ||
| 1491 | K_MGATE(G_DSI, RCC_APB4ENSETR, 4, 0), | 1493 | K_MGATE(G_DSI, RCC_APB4ENSETR, 4, 0), |
| 1492 | K_MGATE(G_LTDC, RCC_APB4ENSETR, 0, 0), | 1494 | K_MGATE(G_LTDC, RCC_APB4ENSETR, 0, 0), |
| 1493 | 1495 | ||
| @@ -1899,6 +1901,7 @@ static const struct clock_config stm32mp1_clock_cfg[] = { · 405 unmodified lines | |||
| 1899 | PCLK(CRC1, "crc1", "ck_axi", 0, G_CRC1), | 1901 | PCLK(CRC1, "crc1", "ck_axi", 0, G_CRC1), |
| 1900 | PCLK(USBH, "usbh", "ck_axi", 0, G_USBH), | 1902 | PCLK(USBH, "usbh", "ck_axi", 0, G_USBH), |
| 1901 | PCLK(ETHSTP, "ethstp", "ck_axi", 0, G_ETHSTP), | 1903 | PCLK(ETHSTP, "ethstp", "ck_axi", 0, G_ETHSTP), |
| 1904 | PCLK(DDRPERFM, "ddrperfm", "pclk4", 0, G_DDRPERFM), | ||
| 1902 | 1905 | ||
| 1903 | /* Kernel clocks */ | 1906 | /* Kernel clocks */ |
| 1904 | KCLK(SDMMC1_K, "sdmmc1_k", sdmmc12_src, 0, G_SDMMC1, M_SDMMC12), | 1907 | KCLK(SDMMC1_K, "sdmmc1_k", sdmmc12_src, 0, G_SDMMC1, M_SDMMC12), |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | /* | 2 | /* |
| 3 | * Clock driver for TI Davinci PSC controllers | 3 | * Clock driver for TI Davinci PSC controllers |
| 4 | * | 4 | * |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | /* | 2 | /* |
| 3 | * Clock driver for TI Davinci PSC controllers | 3 | * Clock driver for TI Davinci PSC controllers |
| 4 | * | 4 | * |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright (c) 2017, Linaro Limited | 3 | * Copyright (c) 2017, Linaro Limited |
| 4 | * Author: Georgi Djakov <georgi.djakov@linaro.org> | 4 | * Author: Georgi Djakov <georgi.djakov@linaro.org> |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * | 2 | /* |
| 3 | * R-Car Gen2 Clock Pulse Generator | 3 | * R-Car Gen2 Clock Pulse Generator |
| 4 | * | 4 | * |
| 5 | * Copyright (C) 2016 Cogent Embedded Inc. | 5 | * Copyright (C) 2016 Cogent Embedded Inc. |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * | 2 | /* |
| 3 | * R-Car Gen3 Clock Pulse Generator | 3 | * R-Car Gen3 Clock Pulse Generator |
| 4 | * | 4 | * |
| 5 | * Copyright (C) 2015-2018 Glider bvba | 5 | * Copyright (C) 2015-2018 Glider bvba |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | * | 2 | /* |
| 3 | * Renesas Clock Pulse Generator / Module Standby and Software Reset | 3 | * Renesas Clock Pulse Generator / Module Standby and Software Reset |
| 4 | * | 4 | * |
| 5 | * Copyright (C) 2015 Glider bvba | 5 | * Copyright (C) 2015 Glider bvba |
| @@ -0,0 +1,18 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | menuconfig CLK_SIFIVE | ||
| 4 | bool "SiFive SoC driver support" | ||
| 5 | help | ||
| 6 | SoC drivers for SiFive Linux-capable SoCs. | ||
| 7 | |||
| 8 | if CLK_SIFIVE | ||
| 9 | |||
| 10 | config CLK_SIFIVE_FU540_PRCI | ||
| 11 | bool "PRCI driver for SiFive FU540 SoCs" | ||
| 12 | select CLK_ANALOGBITS_WRPLL_CLN28HPC | ||
| 13 | help | ||
| 14 | Supports the Power Reset Clock interface (PRCI) IP block found in | ||
| 15 | FU540 SoCs. If this kernel is meant to run on a SiFive FU540 SoC, | ||
| 16 | enable this driver. | ||
| 17 | |||
| 18 | endif | ||
| @@ -0,0 +1 @@ | |||
| 1 | obj-$(CONFIG_CLK_SIFIVE_FU540_PRCI) += fu540-prci.o | ||
| @@ -0,0 +1,626 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2018-2019 SiFive, Inc. | ||
| 4 | * Wesley Terpstra | ||
| 5 | * Paul Walmsley | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * The FU540 PRCI implements clock and reset control for the SiFive | ||
| 17 | * FU540-C000 chip. This driver assumes that it has sole control | ||
| 18 | * over all PRCI resources. | ||
| 19 | * | ||
| 20 | * This driver is based on the PRCI driver written by Wesley Terpstra: | ||
| 21 | * https://github.com/riscv/riscv-linux/commit/999529edf517ed75b56659d456d221b2ee56bb60 | ||
| 22 | * | ||
| 23 | * References: | ||
| 24 | * - SiFive FU540-C000 manual v1p0, Chapter 7 "Clocking and Reset" | ||
| 25 | */ | ||
| 26 | |||
| 27 | #include <dt-bindings/clock/sifive-fu540-prci.h> | ||
| 28 | #include <linux/clkdev.h> | ||
| 29 | #include <linux/clk-provider.h> | ||
| 30 | #include <linux/clk/analogbits-wrpll-cln28hpc.h> | ||
| 31 | #include <linux/delay.h> | ||
| 32 | #include <linux/err.h> | ||
| 33 | #include <linux/module.h> | ||
| 34 | #include <linux/of.h> | ||
| 35 | #include <linux/of_clk.h> | ||
| 36 | #include <linux/platform_device.h> | ||
| 37 | #include <linux/slab.h> | ||
| 38 | |||
| 39 | /* | ||
| 40 | * EXPECTED_CLK_PARENT_COUNT: how many parent clocks this driver expects: | ||
| 41 | * hfclk and rtcclk | ||
| 42 | */ | ||
| 43 | #define EXPECTED_CLK_PARENT_COUNT 2 | ||
| 44 | |||
| 45 | /* | ||
| 46 | * Register offsets and bitmasks | ||
| 47 | */ | ||
| 48 | |||
| 49 | /* COREPLLCFG0 */ | ||
| 50 | #define PRCI_COREPLLCFG0_OFFSET 0x4 | ||
| 51 | # define PRCI_COREPLLCFG0_DIVR_SHIFT 0 | ||
| 52 | # define PRCI_COREPLLCFG0_DIVR_MASK (0x3f << PRCI_COREPLLCFG0_DIVR_SHIFT) | ||
| 53 | # define PRCI_COREPLLCFG0_DIVF_SHIFT 6 | ||
| 54 | # define PRCI_COREPLLCFG0_DIVF_MASK (0x1ff << PRCI_COREPLLCFG0_DIVF_SHIFT) | ||
| 55 | # define PRCI_COREPLLCFG0_DIVQ_SHIFT 15 | ||
| 56 | # define PRCI_COREPLLCFG0_DIVQ_MASK (0x7 << PRCI_COREPLLCFG0_DIVQ_SHIFT) | ||
| 57 | # define PRCI_COREPLLCFG0_RANGE_SHIFT 18 | ||
| 58 | # define PRCI_COREPLLCFG0_RANGE_MASK (0x7 << PRCI_COREPLLCFG0_RANGE_SHIFT) | ||
| 59 | # define PRCI_COREPLLCFG0_BYPASS_SHIFT 24 | ||
| 60 | # define PRCI_COREPLLCFG0_BYPASS_MASK (0x1 << PRCI_COREPLLCFG0_BYPASS_SHIFT) | ||
| 61 | # define PRCI_COREPLLCFG0_FSE_SHIFT 25 | ||
| 62 | # define PRCI_COREPLLCFG0_FSE_MASK (0x1 << PRCI_COREPLLCFG0_FSE_SHIFT) | ||
| 63 | # define PRCI_COREPLLCFG0_LOCK_SHIFT 31 | ||
| 64 | # define PRCI_COREPLLCFG0_LOCK_MASK (0x1 << PRCI_COREPLLCFG0_LOCK_SHIFT) | ||
| 65 | |||
| 66 | /* DDRPLLCFG0 */ | ||
| 67 | #define PRCI_DDRPLLCFG0_OFFSET 0xc | ||
| 68 | # define PRCI_DDRPLLCFG0_DIVR_SHIFT 0 | ||
| 69 | # define PRCI_DDRPLLCFG0_DIVR_MASK (0x3f << PRCI_DDRPLLCFG0_DIVR_SHIFT) | ||
| 70 | # define PRCI_DDRPLLCFG0_DIVF_SHIFT 6 | ||
| 71 | # define PRCI_DDRPLLCFG0_DIVF_MASK (0x1ff << PRCI_DDRPLLCFG0_DIVF_SHIFT) | ||
| 72 | # define PRCI_DDRPLLCFG0_DIVQ_SHIFT 15 | ||
| 73 | # define PRCI_DDRPLLCFG0_DIVQ_MASK (0x7 << PRCI_DDRPLLCFG0_DIVQ_SHIFT) | ||
| 74 | # define PRCI_DDRPLLCFG0_RANGE_SHIFT 18 | ||
| 75 | # define PRCI_DDRPLLCFG0_RANGE_MASK (0x7 << PRCI_DDRPLLCFG0_RANGE_SHIFT) | ||
| 76 | # define PRCI_DDRPLLCFG0_BYPASS_SHIFT 24 | ||
| 77 | # define PRCI_DDRPLLCFG0_BYPASS_MASK (0x1 << PRCI_DDRPLLCFG0_BYPASS_SHIFT) | ||
| 78 | # define PRCI_DDRPLLCFG0_FSE_SHIFT 25 | ||
| 79 | # define PRCI_DDRPLLCFG0_FSE_MASK (0x1 << PRCI_DDRPLLCFG0_FSE_SHIFT) | ||
| 80 | # define PRCI_DDRPLLCFG0_LOCK_SHIFT 31 | ||
| 81 | # define PRCI_DDRPLLCFG0_LOCK_MASK (0x1 << PRCI_DDRPLLCFG0_LOCK_SHIFT) | ||
| 82 | |||
| 83 | /* DDRPLLCFG1 */ | ||
| 84 | #define PRCI_DDRPLLCFG1_OFFSET 0x10 | ||
| 85 | # define PRCI_DDRPLLCFG1_CKE_SHIFT 24 | ||
| 86 | # define PRCI_DDRPLLCFG1_CKE_MASK (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT) | ||
| 87 | |||
| 88 | /* GEMGXLPLLCFG0 */ | ||
| 89 | #define PRCI_GEMGXLPLLCFG0_OFFSET 0x1c | ||
| 90 | # define PRCI_GEMGXLPLLCFG0_DIVR_SHIFT 0 | ||
| 91 | # define PRCI_GEMGXLPLLCFG0_DIVR_MASK (0x3f << PRCI_GEMGXLPLLCFG0_DIVR_SHIFT) | ||
| 92 | # define PRCI_GEMGXLPLLCFG0_DIVF_SHIFT 6 | ||
| 93 | # define PRCI_GEMGXLPLLCFG0_DIVF_MASK (0x1ff << PRCI_GEMGXLPLLCFG0_DIVF_SHIFT) | ||
| 94 | # define PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT 15 | ||
| 95 | # define PRCI_GEMGXLPLLCFG0_DIVQ_MASK (0x7 << PRCI_GEMGXLPLLCFG0_DIVQ_SHIFT) | ||
| 96 | # define PRCI_GEMGXLPLLCFG0_RANGE_SHIFT 18 | ||
| 97 | # define PRCI_GEMGXLPLLCFG0_RANGE_MASK (0x7 << PRCI_GEMGXLPLLCFG0_RANGE_SHIFT) | ||
| 98 | # define PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT 24 | ||
| 99 | # define PRCI_GEMGXLPLLCFG0_BYPASS_MASK (0x1 << PRCI_GEMGXLPLLCFG0_BYPASS_SHIFT) | ||
| 100 | # define PRCI_GEMGXLPLLCFG0_FSE_SHIFT 25 | ||
| 101 | # define PRCI_GEMGXLPLLCFG0_FSE_MASK (0x1 << PRCI_GEMGXLPLLCFG0_FSE_SHIFT) | ||
| 102 | # define PRCI_GEMGXLPLLCFG0_LOCK_SHIFT 31 | ||
| 103 | # define PRCI_GEMGXLPLLCFG0_LOCK_MASK (0x1 << PRCI_GEMGXLPLLCFG0_LOCK_SHIFT) | ||
| 104 | |||
| 105 | /* GEMGXLPLLCFG1 */ | ||
| 106 | #define PRCI_GEMGXLPLLCFG1_OFFSET 0x20 | ||
| 107 | # define PRCI_GEMGXLPLLCFG1_CKE_SHIFT 24 | ||
| 108 | # define PRCI_GEMGXLPLLCFG1_CKE_MASK (0x1 << PRCI_GEMGXLPLLCFG1_CKE_SHIFT) | ||
| 109 | |||
| 110 | /* CORECLKSEL */ | ||
| 111 | #define PRCI_CORECLKSEL_OFFSET 0x24 | ||
| 112 | # define PRCI_CORECLKSEL_CORECLKSEL_SHIFT 0 | ||
| 113 | # define PRCI_CORECLKSEL_CORECLKSEL_MASK (0x1 << PRCI_CORECLKSEL_CORECLKSEL_SHIFT) | ||
| 114 | |||
| 115 | /* DEVICESRESETREG */ | ||
| 116 | #define PRCI_DEVICESRESETREG_OFFSET 0x28 | ||
| 117 | # define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT 0 | ||
| 118 | # define PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_CTRL_RST_N_SHIFT) | ||
| 119 | # define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT 1 | ||
| 120 | # define PRCI_DEVICESRESETREG_DDR_AXI_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_AXI_RST_N_SHIFT) | ||
| 121 | # define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT 2 | ||
| 122 | # define PRCI_DEVICESRESETREG_DDR_AHB_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_AHB_RST_N_SHIFT) | ||
| 123 | # define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT 3 | ||
| 124 | # define PRCI_DEVICESRESETREG_DDR_PHY_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_DDR_PHY_RST_N_SHIFT) | ||
| 125 | # define PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT 5 | ||
| 126 | # define PRCI_DEVICESRESETREG_GEMGXL_RST_N_MASK (0x1 << PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT) | ||
| 127 | |||
| 128 | /* CLKMUXSTATUSREG */ | ||
| 129 | #define PRCI_CLKMUXSTATUSREG_OFFSET 0x2c | ||
| 130 | # define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT 1 | ||
| 131 | # define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK (0x1 << PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT) | ||
| 132 | |||
| 133 | /* | ||
| 134 | * Private structures | ||
| 135 | */ | ||
| 136 | |||
| 137 | /** | ||
| 138 | * struct __prci_data - per-device-instance data | ||
| 139 | * @va: base virtual address of the PRCI IP block | ||
| 140 | * @hw_clks: encapsulates struct clk_hw records | ||
| 141 | * | ||
| 142 | * PRCI per-device instance data | ||
| 143 | */ | ||
| 144 | struct __prci_data { | ||
| 145 | void __iomem *va; | ||
| 146 | struct clk_hw_onecell_data hw_clks; | ||
| 147 | }; | ||
| 148 | |||
| 149 | /** | ||
| 150 | * struct __prci_wrpll_data - WRPLL configuration and integration data | ||
| 151 | * @c: WRPLL current configuration record | ||
| 152 | * @enable_bypass: fn ptr to code to bypass the WRPLL (if applicable; else NULL) | ||
| 153 | * @disable_bypass: fn ptr to code to not bypass the WRPLL (or NULL) | ||
| 154 | * @cfg0_offs: WRPLL CFG0 register offset (in bytes) from the PRCI base address | ||
| 155 | * | ||
| 156 | * @enable_bypass and @disable_bypass are used for WRPLL instances | ||
| 157 | * that contain a separate external glitchless clock mux downstream | ||
| 158 | * from the PLL. The WRPLL internal bypass mux is not glitchless. | ||
| 159 | */ | ||
| 160 | struct __prci_wrpll_data { | ||
| 161 | struct wrpll_cfg c; | ||
| 162 | void (*enable_bypass)(struct __prci_data *pd); | ||
| 163 | void (*disable_bypass)(struct __prci_data *pd); | ||
| 164 | u8 cfg0_offs; | ||
| 165 | }; | ||
| 166 | |||
| 167 | /** | ||
| 168 | * struct __prci_clock - describes a clock device managed by PRCI | ||
| 169 | * @name: user-readable clock name string - should match the manual | ||
| 170 | * @parent_name: parent name for this clock | ||
| 171 | * @ops: struct clk_ops for the Linux clock framework to use for control | ||
| 172 | * @hw: Linux-private clock data | ||
| 173 | * @pwd: WRPLL-specific data, associated with this clock (if not NULL) | ||
| 174 | * @pd: PRCI-specific data associated with this clock (if not NULL) | ||
| 175 | * | ||
| 176 | * PRCI clock data. Used by the PRCI driver to register PRCI-provided | ||
| 177 | * clocks to the Linux clock infrastructure. | ||
| 178 | */ | ||
| 179 | struct __prci_clock { | ||
| 180 | const char *name; | ||
| 181 | const char *parent_name; | ||
| 182 | const struct clk_ops *ops; | ||
| 183 | struct clk_hw hw; | ||
| 184 | struct __prci_wrpll_data *pwd; | ||
| 185 | struct __prci_data *pd; | ||
| 186 | }; | ||
| 187 | |||
| 188 | #define clk_hw_to_prci_clock(pwd) container_of(pwd, struct __prci_clock, hw) | ||
| 189 | |||
| 190 | /* | ||
| 191 | * Private functions | ||
| 192 | */ | ||
| 193 | |||
| 194 | /** | ||
| 195 | * __prci_readl() - read from a PRCI register | ||
| 196 | * @pd: PRCI context | ||
| 197 | * @offs: register offset to read from (in bytes, from PRCI base address) | ||
| 198 | * | ||
| 199 | * Read the register located at offset @offs from the base virtual | ||
| 200 | * address of the PRCI register target described by @pd, and return | ||
| 201 | * the value to the caller. | ||
| 202 | * | ||
| 203 | * Context: Any context. | ||
| 204 | * | ||
| 205 | * Return: the contents of the register described by @pd and @offs. | ||
| 206 | */ | ||
| 207 | static u32 __prci_readl(struct __prci_data *pd, u32 offs) | ||
| 208 | { | ||
| 209 | return readl_relaxed(pd->va + offs); | ||
| 210 | } | ||
| 211 | |||
| 212 | static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd) | ||
| 213 | { | ||
| 214 | writel_relaxed(v, pd->va + offs); | ||
| 215 | } | ||
| 216 | |||
| 217 | /* WRPLL-related private functions */ | ||
| 218 | |||
| 219 | /** | ||
| 220 | * __prci_wrpll_unpack() - unpack WRPLL configuration registers into parameters | ||
| 221 | * @c: ptr to a struct wrpll_cfg record to write config into | ||
| 222 | * @r: value read from the PRCI PLL configuration register | ||
| 223 | * | ||
| 224 | * Given a value @r read from an FU540 PRCI PLL configuration register, | ||
| 225 | * split it into fields and populate it into the WRPLL configuration record | ||
| 226 | * pointed to by @c. | ||
| 227 | * | ||
| 228 | * The COREPLLCFG0 macros are used below, but the other *PLLCFG0 macros | ||
| 229 | * have the same register layout. | ||
| 230 | * | ||
| 231 | * Context: Any context. | ||
| 232 | */ | ||
| 233 | static void __prci_wrpll_unpack(struct wrpll_cfg *c, u32 r) | ||
| 234 | { | ||
| 235 | u32 v; | ||
| 236 | |||
| 237 | v = r & PRCI_COREPLLCFG0_DIVR_MASK; | ||
| 238 | v >>= PRCI_COREPLLCFG0_DIVR_SHIFT; | ||
| 239 | c->divr = v; | ||
| 240 | |||
| 241 | v = r & PRCI_COREPLLCFG0_DIVF_MASK; | ||
| 242 | v >>= PRCI_COREPLLCFG0_DIVF_SHIFT; | ||
| 243 | c->divf = v; | ||
| 244 | |||
| 245 | v = r & PRCI_COREPLLCFG0_DIVQ_MASK; | ||
| 246 | v >>= PRCI_COREPLLCFG0_DIVQ_SHIFT; | ||
| 247 | c->divq = v; | ||
| 248 | |||
| 249 | v = r & PRCI_COREPLLCFG0_RANGE_MASK; | ||
| 250 | v >>= PRCI_COREPLLCFG0_RANGE_SHIFT; | ||
| 251 | c->range = v; | ||
| 252 | |||
| 253 | c->flags &= (WRPLL_FLAGS_INT_FEEDBACK_MASK | | ||
| 254 | WRPLL_FLAGS_EXT_FEEDBACK_MASK); | ||
| 255 | |||
| 256 | /* external feedback mode not supported */ | ||
| 257 | c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK; | ||
| 258 | } | ||
| 259 | |||
| 260 | /** | ||
| 261 | * __prci_wrpll_pack() - pack PLL configuration parameters into a register value | ||
| 262 | * @c: pointer to a struct wrpll_cfg record containing the PLL's cfg | ||
| 263 | * | ||
| 264 | * Using a set of WRPLL configuration values pointed to by @c, | ||
| 265 | * assemble a PRCI PLL configuration register value, and return it to | ||
| 266 | * the caller. | ||
| 267 | * | ||
| 268 | * Context: Any context. Caller must ensure that the contents of the | ||
| 269 | * record pointed to by @c do not change during the execution | ||
| 270 | * of this function. | ||
| 271 | * | ||
| 272 | * Returns: a value suitable for writing into a PRCI PLL configuration | ||
| 273 | * register | ||
| 274 | */ | ||
| 275 | static u32 __prci_wrpll_pack(const struct wrpll_cfg *c) | ||
| 276 | { | ||
| 277 | u32 r = 0; | ||
| 278 | |||
| 279 | r |= c->divr << PRCI_COREPLLCFG0_DIVR_SHIFT; | ||
| 280 | r |= c->divf << PRCI_COREPLLCFG0_DIVF_SHIFT; | ||
| 281 | r |= c->divq << PRCI_COREPLLCFG0_DIVQ_SHIFT; | ||
| 282 | r |= c->range << PRCI_COREPLLCFG0_RANGE_SHIFT; | ||
| 283 | |||
| 284 | /* external feedback mode not supported */ | ||
| 285 | r |= PRCI_COREPLLCFG0_FSE_MASK; | ||
| 286 | |||
| 287 | return r; | ||
| 288 | } | ||
| 289 | |||
| 290 | /** | ||
| 291 | * __prci_wrpll_read_cfg() - read the WRPLL configuration from the PRCI | ||
| 292 | * @pd: PRCI context | ||
| 293 | * @pwd: PRCI WRPLL metadata | ||
| 294 | * | ||
| 295 | * Read the current configuration of the PLL identified by @pwd from | ||
| 296 | * the PRCI identified by @pd, and store it into the local configuration | ||
| 297 | * cache in @pwd. | ||
| 298 | * | ||
| 299 | * Context: Any context. Caller must prevent the records pointed to by | ||
| 300 | * @pd and @pwd from changing during execution. | ||
| 301 | */ | ||
| 302 | static void __prci_wrpll_read_cfg(struct __prci_data *pd, | ||
| 303 | struct __prci_wrpll_data *pwd) | ||
| 304 | { | ||
| 305 | __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs)); | ||
| 306 | } | ||
| 307 | |||
| 308 | /** | ||
| 309 | * __prci_wrpll_write_cfg() - write WRPLL configuration into the PRCI | ||
| 310 | * @pd: PRCI context | ||
| 311 | * @pwd: PRCI WRPLL metadata | ||
| 312 | * @c: WRPLL configuration record to write | ||
| 313 | * | ||
| 314 | * Write the WRPLL configuration described by @c into the WRPLL | ||
| 315 | * configuration register identified by @pwd in the PRCI instance | ||
| 316 | * described by @c. Make a cached copy of the WRPLL's current | ||
| 317 | * configuration so it can be used by other code. | ||
| 318 | * | ||
| 319 | * Context: Any context. Caller must prevent the records pointed to by | ||
| 320 | * @pd and @pwd from changing during execution. | ||
| 321 | */ | ||
| 322 | static void __prci_wrpll_write_cfg(struct __prci_data *pd, | ||
| 323 | struct __prci_wrpll_data *pwd, | ||
| 324 | struct wrpll_cfg *c) | ||
| 325 | { | ||
| 326 | __prci_writel(__prci_wrpll_pack(c), pwd->cfg0_offs, pd); | ||
| 327 | |||
| 328 | memcpy(&pwd->c, c, sizeof(*c)); | ||
| 329 | } | ||
| 330 | |||
| 331 | /* Core clock mux control */ | ||
| 332 | |||
| 333 | /** | ||
| 334 | * __prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK | ||
| 335 | * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg | ||
| 336 | * | ||
| 337 | * Switch the CORECLK mux to the HFCLK input source; return once complete. | ||
| 338 | * | ||
| 339 | * Context: Any context. Caller must prevent concurrent changes to the | ||
| 340 | * PRCI_CORECLKSEL_OFFSET register. | ||
| 341 | */ | ||
| 342 | static void __prci_coreclksel_use_hfclk(struct __prci_data *pd) | ||
| 343 | { | ||
| 344 | u32 r; | ||
| 345 | |||
| 346 | r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); | ||
| 347 | r |= PRCI_CORECLKSEL_CORECLKSEL_MASK; | ||
| 348 | __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); | ||
| 349 | |||
| 350 | r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ | ||
| 351 | } | ||
| 352 | |||
| 353 | /** | ||
| 354 | * __prci_coreclksel_use_corepll() - switch the CORECLK mux to output COREPLL | ||
| 355 | * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg | ||
| 356 | * | ||
| 357 | * Switch the CORECLK mux to the PLL output clock; return once complete. | ||
| 358 | * | ||
| 359 | * Context: Any context. Caller must prevent concurrent changes to the | ||
| 360 | * PRCI_CORECLKSEL_OFFSET register. | ||
| 361 | */ | ||
| 362 | static void __prci_coreclksel_use_corepll(struct __prci_data *pd) | ||
| 363 | { | ||
| 364 | u32 r; | ||
| 365 | |||
| 366 | r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); | ||
| 367 | r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK; | ||
| 368 | __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); | ||
| 369 | |||
| 370 | r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ | ||
| 371 | } | ||
| 372 | |||
| 373 | /* | ||
| 374 | * Linux clock framework integration | ||
| 375 | * | ||
| 376 | * See the Linux clock framework documentation for more information on | ||
| 377 | * these functions. | ||
| 378 | */ | ||
| 379 | |||
| 380 | static unsigned long sifive_fu540_prci_wrpll_recalc_rate(struct clk_hw *hw, | ||
| 381 | unsigned long parent_rate) | ||
| 382 | { | ||
| 383 | struct __prci_clock *pc = clk_hw_to_prci_clock(hw); | ||
| 384 | struct __prci_wrpll_data *pwd = pc->pwd; | ||
| 385 | |||
| 386 | return wrpll_calc_output_rate(&pwd->c, parent_rate); | ||
| 387 | } | ||
| 388 | |||
| 389 | static long sifive_fu540_prci_wrpll_round_rate(struct clk_hw *hw, | ||
| 390 | unsigned long rate, | ||
| 391 | unsigned long *parent_rate) | ||
| 392 | { | ||
| 393 | struct __prci_clock *pc = clk_hw_to_prci_clock(hw); | ||
| 394 | struct __prci_wrpll_data *pwd = pc->pwd; | ||
| 395 | struct wrpll_cfg c; | ||
| 396 | |||
| 397 | memcpy(&c, &pwd->c, sizeof(c)); | ||
| 398 | |||
| 399 | wrpll_configure_for_rate(&c, rate, *parent_rate); | ||
| 400 | |||
| 401 | return wrpll_calc_output_rate(&c, *parent_rate); | ||
| 402 | } | ||
| 403 | |||
| 404 | static int sifive_fu540_prci_wrpll_set_rate(struct clk_hw *hw, | ||
| 405 | unsigned long rate, | ||
| 406 | unsigned long parent_rate) | ||
| 407 | { | ||
| 408 | struct __prci_clock *pc = clk_hw_to_prci_clock(hw); | ||
| 409 | struct __prci_wrpll_data *pwd = pc->pwd; | ||
| 410 | struct __prci_data *pd = pc->pd; | ||
| 411 | int r; | ||
| 412 | |||
| 413 | r = wrpll_configure_for_rate(&pwd->c, rate, parent_rate); | ||
| 414 | if (r) | ||
| 415 | return r; | ||
| 416 | |||
| 417 | if (pwd->enable_bypass) | ||
| 418 | pwd->enable_bypass(pd); | ||
| 419 | |||
| 420 | __prci_wrpll_write_cfg(pd, pwd, &pwd->c); | ||
| 421 | |||
| 422 | udelay(wrpll_calc_max_lock_us(&pwd->c)); | ||
| 423 | |||
| 424 | if (pwd->disable_bypass) | ||
| 425 | pwd->disable_bypass(pd); | ||
| 426 | |||
| 427 | return 0; | ||
| 428 | } | ||
| 429 | |||
| 430 | static const struct clk_ops sifive_fu540_prci_wrpll_clk_ops = { | ||
| 431 | .set_rate = sifive_fu540_prci_wrpll_set_rate, | ||
| 432 | .round_rate = sifive_fu540_prci_wrpll_round_rate, | ||
| 433 | .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate, | ||
| 434 | }; | ||
| 435 | |||
| 436 | static const struct clk_ops sifive_fu540_prci_wrpll_ro_clk_ops = { | ||
| 437 | .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate, | ||
| 438 | }; | ||
| 439 | |||
| 440 | /* TLCLKSEL clock integration */ | ||
| 441 | |||
| 442 | static unsigned long sifive_fu540_prci_tlclksel_recalc_rate(struct clk_hw *hw, | ||
| 443 | unsigned long parent_rate) | ||
| 444 | { | ||
| 445 | struct __prci_clock *pc = clk_hw_to_prci_clock(hw); | ||
| 446 | struct __prci_data *pd = pc->pd; | ||
| 447 | u32 v; | ||
| 448 | u8 div; | ||
| 449 | |||
| 450 | v = __prci_readl(pd, PRCI_CLKMUXSTATUSREG_OFFSET); | ||
| 451 | v &= PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK; | ||
| 452 | div = v ? 1 : 2; | ||
| 453 | |||
| 454 | return div_u64(parent_rate, div); | ||
| 455 | } | ||
| 456 | |||
| 457 | static const struct clk_ops sifive_fu540_prci_tlclksel_clk_ops = { | ||
| 458 | .recalc_rate = sifive_fu540_prci_tlclksel_recalc_rate, | ||
| 459 | }; | ||
| 460 | |||
| 461 | /* | ||
| 462 | * PRCI integration data for each WRPLL instance | ||
| 463 | */ | ||
| 464 | |||
| 465 | static struct __prci_wrpll_data __prci_corepll_data = { | ||
| 466 | .cfg0_offs = PRCI_COREPLLCFG0_OFFSET, | ||
| 467 | .enable_bypass = __prci_coreclksel_use_hfclk, | ||
| 468 | .disable_bypass = __prci_coreclksel_use_corepll, | ||
| 469 | }; | ||
| 470 | |||
| 471 | static struct __prci_wrpll_data __prci_ddrpll_data = { | ||
| 472 | .cfg0_offs = PRCI_DDRPLLCFG0_OFFSET, | ||
| 473 | }; | ||
| 474 | |||
| 475 | static struct __prci_wrpll_data __prci_gemgxlpll_data = { | ||
| 476 | .cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET, | ||
| 477 | }; | ||
| 478 | |||
| 479 | /* | ||
| 480 | * List of clock controls provided by the PRCI | ||
| 481 | */ | ||
| 482 | |||
| 483 | static struct __prci_clock __prci_init_clocks[] = { | ||
| 484 | [PRCI_CLK_COREPLL] = { | ||
| 485 | .name = "corepll", | ||
| 486 | .parent_name = "hfclk", | ||
| 487 | .ops = &sifive_fu540_prci_wrpll_clk_ops, | ||
| 488 | .pwd = &__prci_corepll_data, | ||
| 489 | }, | ||
| 490 | [PRCI_CLK_DDRPLL] = { | ||
| 491 | .name = "ddrpll", | ||
| 492 | .parent_name = "hfclk", | ||
| 493 | .ops = &sifive_fu540_prci_wrpll_ro_clk_ops, | ||
| 494 | .pwd = &__prci_ddrpll_data, | ||
| 495 | }, | ||
| 496 | [PRCI_CLK_GEMGXLPLL] = { | ||
| 497 | .name = "gemgxlpll", | ||
| 498 | .parent_name = "hfclk", | ||
| 499 | .ops = &sifive_fu540_prci_wrpll_clk_ops, | ||
| 500 | .pwd = &__prci_gemgxlpll_data, | ||
| 501 | }, | ||
| 502 | [PRCI_CLK_TLCLK] = { | ||
| 503 | .name = "tlclk", | ||
| 504 | .parent_name = "corepll", | ||
| 505 | .ops = &sifive_fu540_prci_tlclksel_clk_ops, | ||
| 506 | }, | ||
| 507 | }; | ||
| 508 | |||
| 509 | /** | ||
| 510 | * __prci_register_clocks() - register clock controls in the PRCI with Linux | ||
| 511 | * @dev: Linux struct device * | ||
| 512 | * | ||
| 513 | * Register the list of clock controls described in __prci_init_plls[] with | ||
| 514 | * the Linux clock framework. | ||
| 515 | * | ||
| 516 | * Return: 0 upon success or a negative error code upon failure. | ||
| 517 | */ | ||
| 518 | static int __prci_register_clocks(struct device *dev, struct __prci_data *pd) | ||
| 519 | { | ||
| 520 | struct clk_init_data init = { }; | ||
| 521 | struct __prci_clock *pic; | ||
| 522 | int parent_count, i, r; | ||
| 523 | |||
| 524 | parent_count = of_clk_get_parent_count(dev->of_node); | ||
| 525 | if (parent_count != EXPECTED_CLK_PARENT_COUNT) { | ||
| 526 | dev_err(dev, "expected only two parent clocks, found %d\n", | ||
| 527 | parent_count); | ||
| 528 | return -EINVAL; | ||
| 529 | } | ||
| 530 | |||
| 531 | /* Register PLLs */ | ||
| 532 | for (i = 0; i < ARRAY_SIZE(__prci_init_clocks); ++i) { | ||
| 533 | pic = &__prci_init_clocks[i]; | ||
| 534 | |||
| 535 | init.name = pic->name; | ||
| 536 | init.parent_names = &pic->parent_name; | ||
| 537 | init.num_parents = 1; | ||
| 538 | init.ops = pic->ops; | ||
| 539 | pic->hw.init = &init; | ||
| 540 | |||
| 541 | pic->pd = pd; | ||
| 542 | |||
| 543 | if (pic->pwd) | ||
| 544 | __prci_wrpll_read_cfg(pd, pic->pwd); | ||
| 545 | |||
| 546 | r = devm_clk_hw_register(dev, &pic->hw); | ||
| 547 | if (r) { | ||
| 548 | dev_warn(dev, "Failed to register clock %s: %d\n", | ||
| 549 | init.name, r); | ||
| 550 | return r; | ||
| 551 | } | ||
| 552 | |||
| 553 | r = clk_hw_register_clkdev(&pic->hw, pic->name, dev_name(dev)); | ||
| 554 | if (r) { | ||
| 555 | dev_warn(dev, "Failed to register clkdev for %s: %d\n", | ||
| 556 | init.name, r); | ||
| 557 | return r; | ||
| 558 | } | ||
| 559 | |||
| 560 | pd->hw_clks.hws[i] = &pic->hw; | ||
| 561 | } | ||
| 562 | |||
| 563 | pd->hw_clks.num = i; | ||
| 564 | |||
| 565 | r = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, | ||
| 566 | &pd->hw_clks); | ||
| 567 | if (r) { | ||
| 568 | dev_err(dev, "could not add hw_provider: %d\n", r); | ||
| 569 | return r; | ||
| 570 | } | ||
| 571 | |||
| 572 | return 0; | ||
| 573 | } | ||
| 574 | |||
| 575 | /* | ||
| 576 | * Linux device model integration | ||
| 577 | * | ||
| 578 | * See the Linux device model documentation for more information about | ||
| 579 | * these functions. | ||
| 580 | */ | ||
| 581 | static int sifive_fu540_prci_probe(struct platform_device *pdev) | ||
| 582 | { | ||
| 583 | struct device *dev = &pdev->dev; | ||
| 584 | struct resource *res; | ||
| 585 | struct __prci_data *pd; | ||
| 586 | int r; | ||
| 587 | |||
| 588 | pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); | ||
| 589 | if (!pd) | ||
| 590 | return -ENOMEM; | ||
| 591 | |||
| 592 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 593 | pd->va = devm_ioremap_resource(dev, res); | ||
| 594 | if (IS_ERR(pd->va)) | ||
| 595 | return PTR_ERR(pd->va); | ||
| 596 | |||
| 597 | r = __prci_register_clocks(dev, pd); | ||
| 598 | if (r) { | ||
| 599 | dev_err(dev, "could not register clocks: %d\n", r); | ||
| 600 | return r; | ||
| 601 | } | ||
| 602 | |||
| 603 | dev_dbg(dev, "SiFive FU540 PRCI probed\n"); | ||
| 604 | |||
| 605 | return 0; | ||
| 606 | } | ||
| 607 | |||
| 608 | static const struct of_device_id sifive_fu540_prci_of_match[] = { | ||
| 609 | { .compatible = "sifive,fu540-c000-prci", }, | ||
| 610 | {} | ||
| 611 | }; | ||
| 612 | MODULE_DEVICE_TABLE(of, sifive_fu540_prci_of_match); | ||
| 613 | |||
| 614 | static struct platform_driver sifive_fu540_prci_driver = { | ||
| 615 | .driver = { | ||
| 616 | .name = "sifive-fu540-prci", | ||
| 617 | .of_match_table = sifive_fu540_prci_of_match, | ||
| 618 | }, | ||
| 619 | .probe = sifive_fu540_prci_probe, | ||
| 620 | }; | ||
| 621 | |||
| 622 | static int __init sifive_fu540_prci_init(void) | ||
| 623 | { | ||
| 624 | return platform_driver_register(&sifive_fu540_prci_driver); | ||
| 625 | } | ||
| 626 | core_initcall(sifive_fu540_prci_init); | ||
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | // | 2 | // |
| 3 | // Spreadtrum clock infrastructure | 3 | // Spreadtrum clock infrastructure |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | // | 2 | // |
| 3 | // Spreadtrum composite clock driver | 3 | // Spreadtrum composite clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | // | 2 | // |
| 3 | // Spreadtrum divider clock driver | 3 | // Spreadtrum divider clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | // | 2 | // |
| 3 | // Spreadtrum gate clock driver | 3 | // Spreadtrum gate clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | // | 2 | // |
| 3 | // Spreadtrum multiplexer clock driver | 3 | // Spreadtrum multiplexer clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | // | 2 | // |
| 3 | // Spreadtrum pll clock driver | 3 | // Spreadtrum pll clock driver |
| 4 | // | 4 | // |
| @@ -1,4 +1,4 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
| 2 | /* | 2 | /* |
| 3 | * Copyright 2016 Icenowy Zheng <icenowy@aosc.io> | 3 | * Copyright 2016 Icenowy Zheng <icenowy@aosc.io> |
| 4 | */ | 4 | */ |
| @@ -1,5 +1,5 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0+ | 1 | /* SPDX-License-Identifier: GPL-2.0+ */ |
| 2 | * | 2 | /* |
| 3 | * Copyright 2017 Icenowy Zheng <icenowy@aosc.io> | 3 | * Copyright 2017 Icenowy Zheng <icenowy@aosc.io> |
| 4 | * | 4 | * |
| 5 | */ | 5 | */ |
| @@ -175,6 +175,7 @@ struct clk *tegra_clk_register_mc(const char *name, const char *parent_name, · 174 unmodified lines | |||
| 175 | void __iomem *reg, spinlock_t *lock) | 175 | void __iomem *reg, spinlock_t *lock) |
| 176 | { | 176 | { |
| 177 | return clk_register_divider_table(NULL, name, parent_name, | 177 | return clk_register_divider_table(NULL, name, parent_name, |
| 178 | CLK_IS_CRITICAL, reg, 16, 1, 0, | 178 | CLK_IS_CRITICAL, |
| 179 | reg, 16, 1, CLK_DIVIDER_READ_ONLY, | ||
| 179 | mc_div_table, lock); | 180 | mc_div_table, lock); |
| 180 | } | 181 | } |
| @@ -121,18 +121,28 @@ static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) · 120 unmodified lines | |||
| 121 | struct tegra_clk_emc *tegra; | 121 | struct tegra_clk_emc *tegra; |
| 122 | u8 ram_code = tegra_read_ram_code(); | 122 | u8 ram_code = tegra_read_ram_code(); |
| 123 | struct emc_timing *timing = NULL; | 123 | struct emc_timing *timing = NULL; |
| 124 | int i; | 124 | int i, k, t; |
| 125 | 125 | ||
| 126 | tegra = container_of(hw, struct tegra_clk_emc, hw); | 126 | tegra = container_of(hw, struct tegra_clk_emc, hw); |
| 127 | 127 | ||
| 128 | for (i = 0; i < tegra->num_timings; i++) { | 128 | for (k = 0; k < tegra->num_timings; k++) { |
| 129 | if (tegra->timings[i].ram_code != ram_code) | 129 | if (tegra->timings[k].ram_code == ram_code) |
| 130 | continue; | 130 | break; |
| 131 | } | ||
| 132 | |||
| 133 | for (t = k; t < tegra->num_timings; t++) { | ||
| 134 | if (tegra->timings[t].ram_code != ram_code) | ||
| 135 | break; | ||
| 136 | } | ||
| 131 | 137 | ||
| 138 | for (i = k; i < t; i++) { | ||
| 132 | timing = tegra->timings + i; | 139 | timing = tegra->timings + i; |
| 133 | 140 | ||
| 141 | if (timing->rate < req->rate && i != t - 1) | ||
| 142 | continue; | ||
| 143 | |||
| 134 | if (timing->rate > req->max_rate) { | 144 | if (timing->rate > req->max_rate) { |
| 135 | i = max(i, 1); | 145 | i = max(i, k + 1); |
| 136 | req->rate = tegra->timings[i - 1].rate; | 146 | req->rate = tegra->timings[i - 1].rate; |
| 137 | return 0; | 147 | return 0; |
| 138 | } | 148 | } |
| @@ -140,10 +150,8 @@ static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) · 1 unmodified line | |||
| 140 | if (timing->rate < req->min_rate) | 150 | if (timing->rate < req->min_rate) |
| 141 | continue; | 151 | continue; |
| 142 | 152 | ||
| 143 | if (timing->rate >= req->rate) { | 153 | req->rate = timing->rate; |
| 144 | req->rate = timing->rate; | 154 | return 0; |
| 145 | return 0; | ||
| 146 | } | ||
| 147 | } | 155 | } |
| 148 | 156 | ||
| 149 | if (timing) { | 157 | if (timing) { |
| @@ -214,7 +222,10 @@ static int emc_set_timing(struct tegra_clk_emc *tegra, · 64 unmodified lines | |||
| 214 | 222 | ||
| 215 | if (emc_get_parent(&tegra->hw) == timing->parent_index && | 223 | if (emc_get_parent(&tegra->hw) == timing->parent_index && |
| 216 | clk_get_rate(timing->parent) != timing->parent_rate) { | 224 | clk_get_rate(timing->parent) != timing->parent_rate) { |
| 217 | BUG(); | 225 | WARN_ONCE(1, "parent %s rate mismatch %lu %lu\n", |
| 226 | __clk_get_name(timing->parent), | ||
| 227 | clk_get_rate(timing->parent), | ||
| 228 | timing->parent_rate); | ||
| 218 | return -EINVAL; | 229 | return -EINVAL; |
| 219 | } | 230 | } |
| 220 | 231 | ||
| @@ -282,7 +293,7 @@ static struct emc_timing *get_backup_timing(struct tegra_clk_emc *tegra, · 61 unmodified lines | |||
| 282 | for (i = timing_index+1; i < tegra->num_timings; i++) { | 293 | for (i = timing_index+1; i < tegra->num_timings; i++) { |
| 283 | timing = tegra->timings + i; | 294 | timing = tegra->timings + i; |
| 284 | if (timing->ram_code != ram_code) | 295 | if (timing->ram_code != ram_code) |
| 285 | continue; | 296 | break; |
| 286 | 297 | ||
| 287 | if (emc_parent_clk_sources[timing->parent_index] != | 298 | if (emc_parent_clk_sources[timing->parent_index] != |
| 288 | emc_parent_clk_sources[ | 299 | emc_parent_clk_sources[ |
| @@ -293,7 +304,7 @@ static struct emc_timing *get_backup_timing(struct tegra_clk_emc *tegra, · 4 unmodified lines | |||
| 293 | for (i = timing_index-1; i >= 0; --i) { | 304 | for (i = timing_index-1; i >= 0; --i) { |
| 294 | timing = tegra->timings + i; | 305 | timing = tegra->timings + i; |
| 295 | if (timing->ram_code != ram_code) | 306 | if (timing->ram_code != ram_code) |
| 296 | continue; | 307 | break; |
| 297 | 308 | ||
| 298 | if (emc_parent_clk_sources[timing->parent_index] != | 309 | if (emc_parent_clk_sources[timing->parent_index] != |
| 299 | emc_parent_clk_sources[ | 310 | emc_parent_clk_sources[ |
| @@ -433,19 +444,23 @@ static int load_timings_from_dt(struct tegra_clk_emc *tegra, · 133 unmodified lines | |||
| 433 | struct device_node *node, | 444 | struct device_node *node, |
| 434 | u32 ram_code) | 445 | u32 ram_code) |
| 435 | { | 446 | { |
| 447 | struct emc_timing *timings_ptr; | ||
| 436 | struct device_node *child; | 448 | struct device_node *child; |
| 437 | int child_count = of_get_child_count(node); | 449 | int child_count = of_get_child_count(node); |
| 438 | int i = 0, err; | 450 | int i = 0, err; |
| 451 | size_t size; | ||
| 439 | 452 | ||
| 440 | tegra->timings = kcalloc(child_count, sizeof(struct emc_timing), | 453 | size = (tegra->num_timings + child_count) * sizeof(struct emc_timing); |
| 441 | GFP_KERNEL); | 454 | |
| 455 | tegra->timings = krealloc(tegra->timings, size, GFP_KERNEL); | ||
| 442 | if (!tegra->timings) | 456 | if (!tegra->timings) |
| 443 | return -ENOMEM; | 457 | return -ENOMEM; |
| 444 | 458 | ||
| 445 | tegra->num_timings = child_count; | 459 | timings_ptr = tegra->timings + tegra->num_timings; |
| 460 | tegra->num_timings += child_count; | ||
| 446 | 461 | ||
| 447 | for_each_child_of_node(node, child) { | 462 | for_each_child_of_node(node, child) { |
| 448 | struct emc_timing *timing = tegra->timings + (i++); | 463 | struct emc_timing *timing = timings_ptr + (i++); |
| 449 | 464 | ||
| 450 | err = load_one_timing_from_dt(tegra, timing, child); | 465 | err = load_one_timing_from_dt(tegra, timing, child); |
| 451 | if (err) { | 466 | if (err) { |
| @@ -456,7 +471,7 @@ static int load_timings_from_dt(struct tegra_clk_emc *tegra, · 4 unmodified lines | |||
| 456 | timing->ram_code = ram_code; | 471 | timing->ram_code = ram_code; |
| 457 | } | 472 | } |
| 458 | 473 | ||
| 459 | sort(tegra->timings, tegra->num_timings, sizeof(struct emc_timing), | 474 | sort(timings_ptr, child_count, sizeof(struct emc_timing), |
| 460 | cmp_timings, NULL); | 475 | cmp_timings, NULL); |
| 461 | 476 | ||
| 462 | return 0; | 477 | return 0; |
| @@ -499,10 +514,10 @@ struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, · 36 unmodified lines | |||
| 499 | * fuses until the apbmisc driver is loaded. | 514 | * fuses until the apbmisc driver is loaded. |
| 500 | */ | 515 | */ |
| 501 | err = load_timings_from_dt(tegra, node, node_ram_code); | 516 | err = load_timings_from_dt(tegra, node, node_ram_code); |
| 502 | of_node_put(node); | 517 | if (err) { |
| 503 | if (err) | 518 | of_node_put(node); |
| 504 | return ERR_PTR(err); | 519 | return ERR_PTR(err); |
| 505 | break; | 520 | } |
| 506 | } | 521 | } |
| 507 | 522 | ||
| 508 | if (tegra->num_timings == 0) | 523 | if (tegra->num_timings == 0) |
| @@ -532,7 +547,5 @@ struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, · 23 unmodified lines | |||
| 532 | /* Allow debugging tools to see the EMC clock */ | 547 | /* Allow debugging tools to see the EMC clock */ |
| 533 | clk_register_clkdev(clk, "emc", "tegra-clk-debug"); | 548 | clk_register_clkdev(clk, "emc", "tegra-clk-debug"); |
| 534 | 549 | ||
| 535 | clk_prepare_enable(clk); | ||
| 536 | |||
| 537 | return clk; | 550 | return clk; |
| 538 | }; | 551 | }; |
| @@ -444,6 +444,9 @@ static int clk_pll_enable(struct clk_hw *hw) · 443 unmodified lines | |||
| 444 | unsigned long flags = 0; | 444 | unsigned long flags = 0; |
| 445 | int ret; | 445 | int ret; |
| 446 | 446 | ||
| 447 | if (clk_pll_is_enabled(hw)) | ||
| 448 | return 0; | ||
| 449 | |||
| 447 | if (pll->lock) | 450 | if (pll->lock) |
| 448 | spin_lock_irqsave(pll->lock, flags); | 451 | spin_lock_irqsave(pll->lock, flags); |
| 449 | 452 | ||
| @@ -663,8 +666,8 @@ static void _update_pll_mnp(struct tegra_clk_pll *pll, · 213 unmodified lines | |||
| 663 | pll_override_writel(val, params->pmc_divp_reg, pll); | 666 | pll_override_writel(val, params->pmc_divp_reg, pll); |
| 664 | 667 | ||
| 665 | val = pll_override_readl(params->pmc_divnm_reg, pll); | 668 | val = pll_override_readl(params->pmc_divnm_reg, pll); |
| 666 | val &= ~(divm_mask(pll) << div_nmp->override_divm_shift) | | 669 | val &= ~((divm_mask(pll) << div_nmp->override_divm_shift) | |
| 667 | ~(divn_mask(pll) << div_nmp->override_divn_shift); | 670 | (divn_mask(pll) << div_nmp->override_divn_shift)); |
| 668 | val |= (cfg->m << div_nmp->override_divm_shift) | | 671 | val |= (cfg->m << div_nmp->override_divm_shift) | |
| 669 | (cfg->n << div_nmp->override_divn_shift); | 672 | (cfg->n << div_nmp->override_divn_shift); |
| 670 | pll_override_writel(val, params->pmc_divnm_reg, pll); | 673 | pll_override_writel(val, params->pmc_divnm_reg, pll); |
| @@ -940,11 +943,16 @@ static int clk_plle_training(struct tegra_clk_pll *pll) · 269 unmodified lines | |||
| 940 | static int clk_plle_enable(struct clk_hw *hw) | 943 | static int clk_plle_enable(struct clk_hw *hw) |
| 941 | { | 944 | { |
| 942 | struct tegra_clk_pll *pll = to_clk_pll(hw); | 945 | struct tegra_clk_pll *pll = to_clk_pll(hw); |
| 943 | unsigned long input_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); | ||
| 944 | struct tegra_clk_pll_freq_table sel; | 946 | struct tegra_clk_pll_freq_table sel; |
| 947 | unsigned long input_rate; | ||
| 945 | u32 val; | 948 | u32 val; |
| 946 | int err; | 949 | int err; |
| 947 | 950 | ||
| 951 | if (clk_pll_is_enabled(hw)) | ||
| 952 | return 0; | ||
| 953 | |||
| 954 | input_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); | ||
| 955 | |||
| 948 | if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate)) | 956 | if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate)) |
| 949 | return -EINVAL; | 957 | return -EINVAL; |
| 950 | 958 | ||
| @@ -1355,6 +1363,9 @@ static int clk_pllc_enable(struct clk_hw *hw) · 404 unmodified lines | |||
| 1355 | int ret; | 1363 | int ret; |
| 1356 | unsigned long flags = 0; | 1364 | unsigned long flags = 0; |
| 1357 | 1365 | ||
| 1366 | if (clk_pll_is_enabled(hw)) | ||
| 1367 | return 0; | ||
| 1368 | |||
| 1358 | if (pll->lock) | 1369 | if (pll->lock) |
| 1359 | spin_lock_irqsave(pll->lock, flags); | 1370 | spin_lock_irqsave(pll->lock, flags); |
| 1360 | 1371 | ||
| @@ -1567,7 +1578,12 @@ static int clk_plle_tegra114_enable(struct clk_hw *hw) · 206 unmodified lines | |||
| 1567 | u32 val; | 1578 | u32 val; |
| 1568 | int ret; | 1579 | int ret; |
| 1569 | unsigned long flags = 0; | 1580 | unsigned long flags = 0; |
| 1570 | unsigned long input_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); | 1581 | unsigned long input_rate; |
| 1582 | |||
| 1583 | if (clk_pll_is_enabled(hw)) | ||
| 1584 | return 0; | ||
| 1585 | |||
| 1586 | input_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); | ||
| 1571 | 1587 | ||
| 1572 | if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate)) | 1588 | if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate)) |
| 1573 | return -EINVAL; | 1589 | return -EINVAL; |
| @@ -1704,6 +1720,9 @@ static int clk_pllu_tegra114_enable(struct clk_hw *hw) · 130 unmodified lines | |||
| 1704 | return -EINVAL; | 1720 | return -EINVAL; |
| 1705 | } | 1721 | } |
| 1706 | 1722 | ||
| 1723 | if (clk_pll_is_enabled(hw)) | ||
| 1724 | return 0; | ||
| 1725 | |||
| 1707 | input_rate = clk_hw_get_rate(__clk_get_hw(osc)); | 1726 | input_rate = clk_hw_get_rate(__clk_get_hw(osc)); |
| 1708 | 1727 | ||
| 1709 | if (pll->lock) | 1728 | if (pll->lock) |
| @@ -2379,6 +2398,16 @@ struct clk *tegra_clk_register_pllre_tegra210(const char *name, · 669 unmodified lines | |||
| 2379 | return clk; | 2398 | return clk; |
| 2380 | } | 2399 | } |
| 2381 | 2400 | ||
| 2401 | static int clk_plle_tegra210_is_enabled(struct clk_hw *hw) | ||
| 2402 | { | ||
| 2403 | struct tegra_clk_pll *pll = to_clk_pll(hw); | ||
| 2404 | u32 val; | ||
| 2405 | |||
| 2406 | val = pll_readl_base(pll); | ||
| 2407 | |||
| 2408 | return val & PLLE_BASE_ENABLE ? 1 : 0; | ||
| 2409 | } | ||
| 2410 | |||
| 2382 | static int clk_plle_tegra210_enable(struct clk_hw *hw) | 2411 | static int clk_plle_tegra210_enable(struct clk_hw *hw) |
| 2383 | { | 2412 | { |
| 2384 | struct tegra_clk_pll *pll = to_clk_pll(hw); | 2413 | struct tegra_clk_pll *pll = to_clk_pll(hw); |
| @@ -2386,7 +2415,12 @@ static int clk_plle_tegra210_enable(struct clk_hw *hw) · 1 unmodified line | |||
| 2386 | u32 val; | 2415 | u32 val; |
| 2387 | int ret = 0; | 2416 | int ret = 0; |
| 2388 | unsigned long flags = 0; | 2417 | unsigned long flags = 0; |
| 2389 | unsigned long input_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); | 2418 | unsigned long input_rate; |
| 2419 | |||
| 2420 | if (clk_plle_tegra210_is_enabled(hw)) | ||
| 2421 | return 0; | ||
| 2422 | |||
| 2423 | input_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); | ||
| 2390 | 2424 | ||
| 2391 | if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate)) | 2425 | if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate)) |
| 2392 | return -EINVAL; | 2426 | return -EINVAL; |
| @@ -2497,16 +2531,6 @@ out: · 104 unmodified lines | |||
| 2497 | spin_unlock_irqrestore(pll->lock, flags); | 2531 | spin_unlock_irqrestore(pll->lock, flags); |
| 2498 | } | 2532 | } |
| 2499 | 2533 | ||
| 2500 | static int clk_plle_tegra210_is_enabled(struct clk_hw *hw) | ||
| 2501 | { | ||
| 2502 | struct tegra_clk_pll *pll = to_clk_pll(hw); | ||
| 2503 | u32 val; | ||
| 2504 | |||
| 2505 | val = pll_readl_base(pll); | ||
| 2506 | |||
| 2507 | return val & PLLE_BASE_ENABLE ? 1 : 0; | ||
| 2508 | } | ||
| 2509 | |||
| 2510 | static const struct clk_ops tegra_clk_plle_tegra210_ops = { | 2534 | static const struct clk_ops tegra_clk_plle_tegra210_ops = { |
| 2511 | .is_enabled = clk_plle_tegra210_is_enabled, | 2535 | .is_enabled = clk_plle_tegra210_is_enabled, |
| 2512 | .enable = clk_plle_tegra210_enable, | 2536 | .enable = clk_plle_tegra210_enable, |
| @@ -413,7 +413,6 @@ static struct tegra_clk_pll_params pll_m_params = { · 412 unmodified lines | |||
| 413 | .base_reg = PLLM_BASE, | 413 | .base_reg = PLLM_BASE, |
| 414 | .misc_reg = PLLM_MISC, | 414 | .misc_reg = PLLM_MISC, |
| 415 | .lock_mask = PLL_BASE_LOCK, | 415 | .lock_mask = PLL_BASE_LOCK, |
| 416 | .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, | ||
| 417 | .lock_delay = 300, | 416 | .lock_delay = 300, |
| 418 | .max_p = 5, | 417 | .max_p = 5, |
| 419 | .pdiv_tohw = pllm_p, | 418 | .pdiv_tohw = pllm_p, |
| @@ -421,7 +420,7 @@ static struct tegra_clk_pll_params pll_m_params = { · 1 unmodified line | |||
| 421 | .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE, | 420 | .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE, |
| 422 | .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE_2, | 421 | .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE_2, |
| 423 | .freq_table = pll_m_freq_table, | 422 | .freq_table = pll_m_freq_table, |
| 424 | .flags = TEGRA_PLL_USE_LOCK | TEGRA_PLL_HAS_LOCK_ENABLE, | 423 | .flags = TEGRA_PLL_USE_LOCK, |
| 425 | }; | 424 | }; |
| 426 | 425 | ||
| 427 | static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { | 426 | static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { |
| @@ -54,7 +54,10 @@ · 53 unmodified lines | |||
| 54 | #define CLK_I2C3 28 | 54 | #define CLK_I2C3 28 |
| 55 | #define CLK_I2C4 29 | 55 | #define CLK_I2C4 29 |
| 56 | #define CLK_LPTIMER 30 | 56 | #define CLK_LPTIMER 30 |
| 57 | 57 | #define CLK_PLL_SRC 31 | |
| 58 | #define END_PRIMARY_CLK_F7 31 | 58 | #define CLK_DFSDM1 32 |
| 59 | #define CLK_ADFSDM1 33 | ||
| 60 | #define CLK_F769_DSI 34 | ||
| 61 | #define END_PRIMARY_CLK_F7 35 | ||
| 59 | 62 | ||
| 60 | #endif | 63 | #endif |
| @@ -0,0 +1,79 @@ | |||
| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2018-2019 SiFive, Inc. | ||
| 4 | * Wesley Terpstra | ||
| 5 | * Paul Walmsley | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef __LINUX_CLK_ANALOGBITS_WRPLL_CLN28HPC_H | ||
| 9 | #define __LINUX_CLK_ANALOGBITS_WRPLL_CLN28HPC_H | ||
| 10 | |||
| 11 | #include <linux/types.h> | ||
| 12 | |||
| 13 | /* DIVQ_VALUES: number of valid DIVQ values */ | ||
| 14 | #define DIVQ_VALUES 6 | ||
| 15 | |||
| 16 | /* | ||
| 17 | * Bit definitions for struct wrpll_cfg.flags | ||
| 18 | * | ||
| 19 | * WRPLL_FLAGS_BYPASS_FLAG: if set, the PLL is either in bypass, or should be | ||
| 20 | * programmed to enter bypass | ||
| 21 | * WRPLL_FLAGS_RESET_FLAG: if set, the PLL is in reset | ||
| 22 | * WRPLL_FLAGS_INT_FEEDBACK_FLAG: if set, the PLL is configured for internal | ||
| 23 | * feedback mode | ||
| 24 | * WRPLL_FLAGS_EXT_FEEDBACK_FLAG: if set, the PLL is configured for external | ||
| 25 | * feedback mode (not yet supported by this driver) | ||
| 26 | */ | ||
| 27 | #define WRPLL_FLAGS_BYPASS_SHIFT 0 | ||
| 28 | #define WRPLL_FLAGS_BYPASS_MASK BIT(WRPLL_FLAGS_BYPASS_SHIFT) | ||
| 29 | #define WRPLL_FLAGS_RESET_SHIFT 1 | ||
| 30 | #define WRPLL_FLAGS_RESET_MASK BIT(WRPLL_FLAGS_RESET_SHIFT) | ||
| 31 | #define WRPLL_FLAGS_INT_FEEDBACK_SHIFT 2 | ||
| 32 | #define WRPLL_FLAGS_INT_FEEDBACK_MASK BIT(WRPLL_FLAGS_INT_FEEDBACK_SHIFT) | ||
| 33 | #define WRPLL_FLAGS_EXT_FEEDBACK_SHIFT 3 | ||
| 34 | #define WRPLL_FLAGS_EXT_FEEDBACK_MASK BIT(WRPLL_FLAGS_EXT_FEEDBACK_SHIFT) | ||
| 35 | |||
| 36 | /** | ||
| 37 | * struct wrpll_cfg - WRPLL configuration values | ||
| 38 | * @divr: reference divider value (6 bits), as presented to the PLL signals | ||
| 39 | * @divf: feedback divider value (9 bits), as presented to the PLL signals | ||
| 40 | * @divq: output divider value (3 bits), as presented to the PLL signals | ||
| 41 | * @flags: PLL configuration flags. See above for more information | ||
| 42 | * @range: PLL loop filter range. See below for more information | ||
| 43 | * @output_rate_cache: cached output rates, swept across DIVQ | ||
| 44 | * @parent_rate: PLL refclk rate for which values are valid | ||
| 45 | * @max_r: maximum possible R divider value, given @parent_rate | ||
| 46 | * @init_r: initial R divider value to start the search from | ||
| 47 | * | ||
| 48 | * @divr, @divq, @divq, @range represent what the PLL expects to see | ||
| 49 | * on its input signals. Thus @divr and @divf are the actual divisors | ||
| 50 | * minus one. @divq is a power-of-two divider; for example, 1 = | ||
| 51 | * divide-by-2 and 6 = divide-by-64. 0 is an invalid @divq value. | ||
| 52 | * | ||
| 53 | * When initially passing a struct wrpll_cfg record, the | ||
| 54 | * record should be zero-initialized with the exception of the @flags | ||
| 55 | * field. The only flag bits that need to be set are either | ||
| 56 | * WRPLL_FLAGS_INT_FEEDBACK or WRPLL_FLAGS_EXT_FEEDBACK. | ||
| 57 | */ | ||
| 58 | struct wrpll_cfg { | ||
| 59 | u8 divr; | ||
| 60 | u8 divq; | ||
| 61 | u8 range; | ||
| 62 | u8 flags; | ||
| 63 | u16 divf; | ||
| 64 | /* private: */ | ||
| 65 | u32 output_rate_cache[DIVQ_VALUES]; | ||
| 66 | unsigned long parent_rate; | ||
| 67 | u8 max_r; | ||
| 68 | u8 init_r; | ||
| 69 | }; | ||
| 70 | |||
| 71 | int wrpll_configure_for_rate(struct wrpll_cfg *c, u32 target_rate, | ||
| 72 | unsigned long parent_rate); | ||
| 73 | |||
| 74 | unsigned int wrpll_calc_max_lock_us(const struct wrpll_cfg *c); | ||
| 75 | |||
| 76 | unsigned long wrpll_calc_output_rate(const struct wrpll_cfg *c, | ||
| 77 | unsigned long parent_rate); | ||
| 78 | |||
| 79 | #endif /* __LINUX_CLK_ANALOGBITS_WRPLL_CLN28HPC_H */ | ||
| @@ -74,6 +74,8 @@ · 73 unmodified lines | |||
| 74 | #define AT91_PMC_USBDIV_4 (2 << 28) | 74 | #define AT91_PMC_USBDIV_4 (2 << 28) |
| 75 | #define AT91_PMC_USB96M (1 << 28) /* Divider by 2 Enable (PLLB only) */ | 75 | #define AT91_PMC_USB96M (1 << 28) /* Divider by 2 Enable (PLLB only) */ |
| 76 | 76 | ||
| 77 | #define AT91_PMC_CPU_CKR 0x28 /* CPU Clock Register */ | ||
| 78 | |||
| 77 | #define AT91_PMC_MCKR 0x30 /* Master Clock Register */ | 79 | #define AT91_PMC_MCKR 0x30 /* Master Clock Register */ |
| 78 | #define AT91_PMC_CSS (3 << 0) /* Master Clock Selection */ | 80 | #define AT91_PMC_CSS (3 << 0) /* Master Clock Selection */ |
| 79 | #define AT91_PMC_CSS_SLOW (0 << 0) | 81 | #define AT91_PMC_CSS_SLOW (0 << 0) |
| @@ -187,16 +189,8 @@ · 107 unmodified lines | |||
| 187 | 189 | ||
| 188 | #define AT91_PMC_PCR 0x10c /* Peripheral Control Register [some SAM9 and SAMA5] */ | 190 | #define AT91_PMC_PCR 0x10c /* Peripheral Control Register [some SAM9 and SAMA5] */ |
| 189 | #define AT91_PMC_PCR_PID_MASK 0x3f | 191 | #define AT91_PMC_PCR_PID_MASK 0x3f |
| 190 | #define AT91_PMC_PCR_GCKCSS_OFFSET 8 | ||
| 191 | #define AT91_PMC_PCR_GCKCSS_MASK (0x7 << AT91_PMC_PCR_GCKCSS_OFFSET) | ||
| 192 | #define AT91_PMC_PCR_GCKCSS(n) ((n) << AT91_PMC_PCR_GCKCSS_OFFSET) /* GCK Clock Source Selection */ | ||
| 193 | #define AT91_PMC_PCR_CMD (0x1 << 12) /* Command (read=0, write=1) */ | 192 | #define AT91_PMC_PCR_CMD (0x1 << 12) /* Command (read=0, write=1) */ |
| 194 | #define AT91_PMC_PCR_DIV_OFFSET 16 | 193 | #define AT91_PMC_PCR_GCKDIV_MASK GENMASK(27, 20) |
| 195 | #define AT91_PMC_PCR_DIV_MASK (0x3 << AT91_PMC_PCR_DIV_OFFSET) | ||
| 196 | #define AT91_PMC_PCR_DIV(n) ((n) << AT91_PMC_PCR_DIV_OFFSET) /* Divisor Value */ | ||
| 197 | #define AT91_PMC_PCR_GCKDIV_OFFSET 20 | ||
| 198 | #define AT91_PMC_PCR_GCKDIV_MASK (0xff << AT91_PMC_PCR_GCKDIV_OFFSET) | ||
| 199 | #define AT91_PMC_PCR_GCKDIV(n) ((n) << AT91_PMC_PCR_GCKDIV_OFFSET) /* Generated Clock Divisor Value */ | ||
| 200 | #define AT91_PMC_PCR_EN (0x1 << 28) /* Enable */ | 194 | #define AT91_PMC_PCR_EN (0x1 << 28) /* Enable */ |
| 201 | #define AT91_PMC_PCR_GCKEN (0x1 << 29) /* GCK Enable */ | 195 | #define AT91_PMC_PCR_GCKEN (0x1 << 29) /* GCK Enable */ |
| 202 | 196 | ||