- 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 | 9 | Required properties: |
| 10 | 10 | - compatible : shall be one of the following: |
| 11 | − "atmel,at91sam9x5-sckc" or | |
| 11 | + "atmel,at91sam9x5-sckc", | |
| 12 | + "atmel,sama5d3-sckc" or | |
| 12 | 13 | "atmel,sama5d4-sckc": |
| 13 | 14 | at91 SCKC (Slow Clock Controller) |
| 14 | − This node contains the slow clock definitions. | |
| 15 | − | |
| 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). | |
| 15 | +- #clock-cells : shall be 0. | |
| 16 | +- clocks : shall be the input parent clock phandle for the clock. | |
| 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 | 22 | For example: |
| 27 | − sckc: sckc@fffffe50 { | |
| 28 | − compatible = "atmel,sama5d3-pmc"; | |
| 29 | − reg = <0xfffffe50 0x4> | |
| 30 | − #size-cells = <0>; | |
| 31 | − #address-cells = <1>; | |
| 32 | − | |
| 33 | − /* put at91 slow clocks here */ | |
| 23 | + sckc@fffffe50 { | |
| 24 | + compatible = "atmel,at91sam9x5-sckc"; | |
| 25 | + reg = <0xfffffe50 0x4>; | |
| 26 | + clocks = <&slow_xtal>; | |
| 27 | + #clock-cells = <0>; | |
| 34 | 28 | }; |
| 35 | 29 | |
| 36 | 30 | Power Management Controller (PMC): |
| 37 | 31 | |
| 38 | 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 | 35 | <chip> can be: at91rm9200, at91sam9260, at91sam9261, |
| 41 | 36 | at91sam9263, at91sam9g45, at91sam9n12, at91sam9rl, at91sam9g15, |
| 42 | 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 | 11 | "st,stm32f42xx-rcc" |
| 12 | 12 | "st,stm32f469-rcc" |
| 13 | 13 | "st,stm32f746-rcc" |
| 14 | + "st,stm32f769-rcc" | |
| 15 | + | |
| 14 | 16 | - reg: should be register base and length as documented in the |
| 15 | 17 | datasheet |
| 16 | 18 | - #reset-cells: 1, see below |
| @@ -102,6 +104,10 @@ The secondary index is bound with the following magic numbers: · 85 unmodified lines | ||
| 102 | 104 | 28 CLK_I2C3 |
| 103 | 105 | 29 CLK_I2C4 |
| 104 | 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 | 113 | Example: |
| @@ -960,6 +960,12 @@ F: drivers/iio/adc/ltc2497* · 959 unmodified lines | ||
| 960 | 960 | X: drivers/iio/*/adjd* |
| 961 | 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 | 969 | ANDES ARCHITECTURE |
| 964 | 970 | M: Greentime Hu <green.hu@gmail.com> |
| 965 | 971 | M: Vincent Chen <deanbo422@gmail.com> |
| @@ -1,3 +1,4 @@ | ||
| 1 | +# SPDX-License-Identifier: GPL-2.0 | |
| 1 | 2 | |
| 2 | 3 | config CLKDEV_LOOKUP |
| 3 | 4 | bool |
| @@ -304,6 +305,7 @@ config COMMON_CLK_FIXED_MMIO · 300 unmodified lines | ||
| 304 | 305 | Support for Memory Mapped IO Fixed clocks |
| 305 | 306 | |
| 306 | 307 | source "drivers/clk/actions/Kconfig" |
| 308 | +source "drivers/clk/analogbits/Kconfig" | |
| 307 | 309 | source "drivers/clk/bcm/Kconfig" |
| 308 | 310 | source "drivers/clk/hisilicon/Kconfig" |
| 309 | 311 | source "drivers/clk/imgtec/Kconfig" |
| @@ -316,6 +318,7 @@ source "drivers/clk/mvebu/Kconfig" · 6 unmodified lines | ||
| 316 | 318 | source "drivers/clk/qcom/Kconfig" |
| 317 | 319 | source "drivers/clk/renesas/Kconfig" |
| 318 | 320 | source "drivers/clk/samsung/Kconfig" |
| 321 | +source "drivers/clk/sifive/Kconfig" | |
| 319 | 322 | source "drivers/clk/sprd/Kconfig" |
| 320 | 323 | source "drivers/clk/sunxi/Kconfig" |
| 321 | 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 | 67 | # please keep this section sorted lexicographically by directory path name |
| 68 | 68 | obj-y += actions/ |
| 69 | +obj-y += analogbits/ | |
| 69 | 70 | obj-$(CONFIG_COMMON_CLK_AT91) += at91/ |
| 70 | 71 | obj-$(CONFIG_ARCH_ARTPEC) += axis/ |
| 71 | 72 | obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/ |
| @@ -95,6 +96,7 @@ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ · 23 unmodified lines | ||
| 95 | 96 | obj-y += renesas/ |
| 96 | 97 | obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ |
| 97 | 98 | obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/ |
| 99 | +obj-$(CONFIG_CLK_SIFIVE) += sifive/ | |
| 98 | 100 | obj-$(CONFIG_ARCH_SIRF) += sirf/ |
| 99 | 101 | obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ |
| 100 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 14 | obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o |
| 15 | 15 | obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o |
| 16 | 16 | obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o |
| 17 | +obj-$(CONFIG_HAVE_AT91_SAM9X60_PLL) += clk-sam9x60-pll.o | |
| 17 | 18 | obj-$(CONFIG_SOC_AT91SAM9) += at91sam9260.o at91sam9rl.o at91sam9x5.o |
| 19 | +obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o | |
| 18 | 20 | obj-$(CONFIG_SOC_SAMA5D4) += sama5d4.o |
| 19 | 21 | obj-$(CONFIG_SOC_SAMA5D2) += sama5d2.o |
| @@ -41,7 +41,7 @@ static u8 sam9260_plla_out[] = { 0, 2 }; · 40 unmodified lines | ||
| 41 | 41 | |
| 42 | 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 | 45 | { .min = 80000000, .max = 160000000 }, |
| 46 | 46 | { .min = 150000000, .max = 240000000 }, |
| 47 | 47 | }; |
| @@ -58,7 +58,7 @@ static u8 sam9260_pllb_out[] = { 1 }; · 10 unmodified lines | ||
| 58 | 58 | |
| 59 | 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 | 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 | 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 | 132 | { .min = 745000000, .max = 800000000 }, |
| 133 | 133 | { .min = 695000000, .max = 750000000 }, |
| 134 | 134 | { .min = 645000000, .max = 700000000 }, |
| @@ -151,7 +151,7 @@ static u8 sam9g20_pllb_out[] = { 0 }; · 16 unmodified lines | ||
| 151 | 151 | |
| 152 | 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 | 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 | 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 | 186 | { .min = 80000000, .max = 200000000 }, |
| 187 | 187 | { .min = 190000000, .max = 240000000 }, |
| 188 | 188 | }; |
| @@ -199,7 +199,7 @@ static u8 sam9261_pllb_out[] = { 1 }; · 10 unmodified lines | ||
| 199 | 199 | |
| 200 | 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 | 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 | 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 | 266 | { .min = 80000000, .max = 200000000 }, |
| 267 | 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 | 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 | 18 | { .min = 80000000, .max = 200000000 }, |
| 19 | 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 | 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 | 21 | { .min = 745000000, .max = 800000000 }, |
| 22 | 22 | { .min = 695000000, .max = 750000000 }, |
| 23 | 23 | { .min = 645000000, .max = 700000000 }, |
| @@ -49,6 +49,13 @@ static const struct { · 25 unmodified lines | ||
| 49 | 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 | 59 | struct pck { |
| 53 | 60 | char *n; |
| 54 | 61 | u8 id; |
| @@ -242,6 +249,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np, · 187 unmodified lines | ||
| 242 | 249 | |
| 243 | 250 | for (i = 0; i < ARRAY_SIZE(at91sam9x5_periphck); i++) { |
| 244 | 251 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, |
| 252 | + &at91sam9x5_pcr_layout, | |
| 245 | 253 | at91sam9x5_periphck[i].n, |
| 246 | 254 | "masterck", |
| 247 | 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 | 263 | for (i = 0; extra_pcks[i].id; i++) { |
| 256 | 264 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, |
| 265 | + &at91sam9x5_pcr_layout, | |
| 257 | 266 | extra_pcks[i].n, |
| 258 | 267 | "masterck", |
| 259 | 268 | extra_pcks[i].id, |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | ||
| 11 | 11 | * |
| 12 | 12 | */ |
| 13 | 13 | |
| 14 | +#include <linux/bitfield.h> | |
| 14 | 15 | #include <linux/clk-provider.h> |
| 15 | 16 | #include <linux/clkdev.h> |
| 16 | 17 | #include <linux/clk/at91_pmc.h> |
| @@ -31,6 +32,7 @@ struct clk_generated { · 14 unmodified lines | ||
| 31 | 32 | spinlock_t *lock; |
| 32 | 33 | u32 id; |
| 33 | 34 | u32 gckdiv; |
| 35 | + const struct clk_pcr_layout *layout; | |
| 34 | 36 | u8 parent_id; |
| 35 | 37 | bool audio_pll_allowed; |
| 36 | 38 | }; |
| @@ -47,14 +49,14 @@ static int clk_generated_enable(struct clk_hw *hw) · 10 unmodified lines | ||
| 47 | 49 | __func__, gck->gckdiv, gck->parent_id); |
| 48 | 50 | |
| 49 | 51 | spin_lock_irqsave(gck->lock, flags); |
| 50 | − regmap_write(gck->regmap, AT91_PMC_PCR, | |
| 51 | − (gck->id & AT91_PMC_PCR_PID_MASK)); | |
| 52 | − regmap_update_bits(gck->regmap, AT91_PMC_PCR, | |
| 53 | − AT91_PMC_PCR_GCKDIV_MASK | AT91_PMC_PCR_GCKCSS_MASK | | |
| 54 | − AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN, | |
| 55 | − AT91_PMC_PCR_GCKCSS(gck->parent_id) | | |
| 56 | − AT91_PMC_PCR_CMD | | |
| 57 | − AT91_PMC_PCR_GCKDIV(gck->gckdiv) | | |
| 52 | + regmap_write(gck->regmap, gck->layout->offset, | |
| 53 | + (gck->id & gck->layout->pid_mask)); | |
| 54 | + regmap_update_bits(gck->regmap, gck->layout->offset, | |
| 55 | + AT91_PMC_PCR_GCKDIV_MASK | gck->layout->gckcss_mask | | |
| 56 | + gck->layout->cmd | AT91_PMC_PCR_GCKEN, | |
| 57 | + field_prep(gck->layout->gckcss_mask, gck->parent_id) | | |
| 58 | + gck->layout->cmd | | |
| 59 | + FIELD_PREP(AT91_PMC_PCR_GCKDIV_MASK, gck->gckdiv) | | |
| 58 | 60 | AT91_PMC_PCR_GCKEN); |
| 59 | 61 | spin_unlock_irqrestore(gck->lock, flags); |
| 60 | 62 | return 0; |
| @@ -66,11 +68,11 @@ static void clk_generated_disable(struct clk_hw *hw) · 5 unmodified lines | ||
| 66 | 68 | unsigned long flags; |
| 67 | 69 | |
| 68 | 70 | spin_lock_irqsave(gck->lock, flags); |
| 69 | − regmap_write(gck->regmap, AT91_PMC_PCR, | |
| 70 | − (gck->id & AT91_PMC_PCR_PID_MASK)); | |
| 71 | − regmap_update_bits(gck->regmap, AT91_PMC_PCR, | |
| 72 | − AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN, | |
| 73 | − AT91_PMC_PCR_CMD); | |
| 71 | + regmap_write(gck->regmap, gck->layout->offset, | |
| 72 | + (gck->id & gck->layout->pid_mask)); | |
| 73 | + regmap_update_bits(gck->regmap, gck->layout->offset, | |
| 74 | + gck->layout->cmd | AT91_PMC_PCR_GCKEN, | |
| 75 | + gck->layout->cmd); | |
| 74 | 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 | 83 | unsigned int status; |
| 82 | 84 | |
| 83 | 85 | spin_lock_irqsave(gck->lock, flags); |
| 84 | − regmap_write(gck->regmap, AT91_PMC_PCR, | |
| 85 | − (gck->id & AT91_PMC_PCR_PID_MASK)); | |
| 86 | − regmap_read(gck->regmap, AT91_PMC_PCR, &status); | |
| 86 | + regmap_write(gck->regmap, gck->layout->offset, | |
| 87 | + (gck->id & gck->layout->pid_mask)); | |
| 88 | + regmap_read(gck->regmap, gck->layout->offset, &status); | |
| 87 | 89 | spin_unlock_irqrestore(gck->lock, flags); |
| 88 | 90 | |
| 89 | 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 | 261 | unsigned long flags; |
| 260 | 262 | |
| 261 | 263 | spin_lock_irqsave(gck->lock, flags); |
| 262 | − regmap_write(gck->regmap, AT91_PMC_PCR, | |
| 263 | − (gck->id & AT91_PMC_PCR_PID_MASK)); | |
| 264 | − regmap_read(gck->regmap, AT91_PMC_PCR, &tmp); | |
| 264 | + regmap_write(gck->regmap, gck->layout->offset, | |
| 265 | + (gck->id & gck->layout->pid_mask)); | |
| 266 | + regmap_read(gck->regmap, gck->layout->offset, &tmp); | |
| 265 | 267 | spin_unlock_irqrestore(gck->lock, flags); |
| 266 | 268 | |
| 267 | − gck->parent_id = (tmp & AT91_PMC_PCR_GCKCSS_MASK) | |
| 268 | − >> AT91_PMC_PCR_GCKCSS_OFFSET; | |
| 269 | − gck->gckdiv = (tmp & AT91_PMC_PCR_GCKDIV_MASK) | |
| 270 | − >> AT91_PMC_PCR_GCKDIV_OFFSET; | |
| 269 | + gck->parent_id = field_get(gck->layout->gckcss_mask, tmp); | |
| 270 | + gck->gckdiv = FIELD_GET(AT91_PMC_PCR_GCKDIV_MASK, tmp); | |
| 271 | 271 | } |
| 272 | 272 | |
| 273 | 273 | struct clk_hw * __init |
| 274 | 274 | at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, |
| 275 | + const struct clk_pcr_layout *layout, | |
| 275 | 276 | const char *name, const char **parent_names, |
| 276 | 277 | u8 num_parents, u8 id, bool pll_audio, |
| 277 | 278 | const struct clk_range *range) |
| @@ -298,6 +299,7 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, · 20 unmodified lines | ||
| 298 | 299 | gck->lock = lock; |
| 299 | 300 | gck->range = *range; |
| 300 | 301 | gck->audio_pll_allowed = pll_audio; |
| 302 | + gck->layout = layout; | |
| 301 | 303 | |
| 302 | 304 | clk_generated_startup(gck); |
| 303 | 305 | hw = &gck->hw; |
| @@ -29,6 +29,7 @@ struct clk_master { · 28 unmodified lines | ||
| 29 | 29 | struct regmap *regmap; |
| 30 | 30 | const struct clk_master_layout *layout; |
| 31 | 31 | const struct clk_master_characteristics *characteristics; |
| 32 | + u32 mckr; | |
| 32 | 33 | }; |
| 33 | 34 | |
| 34 | 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 | 70 | master->characteristics; |
| 70 | 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 | 74 | mckr &= layout->mask; |
| 74 | 75 | |
| 75 | 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 | 96 | struct clk_master *master = to_clk_master(hw); |
| 96 | 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 | 101 | return mckr & AT91_PMC_CSS; |
| 101 | 102 | } |
| @@ -147,13 +148,14 @@ at91_clk_register_master(struct regmap *regmap, · 45 unmodified lines | ||
| 147 | 148 | return hw; |
| 148 | 149 | } |
| 149 | 150 | |
| 150 | − | |
| 151 | 151 | const struct clk_master_layout at91rm9200_master_layout = { |
| 152 | 152 | .mask = 0x31F, |
| 153 | 153 | .pres_shift = 2, |
| 154 | + .offset = AT91_PMC_MCKR, | |
| 154 | 155 | }; |
| 155 | 156 | |
| 156 | 157 | const struct clk_master_layout at91sam9x5_master_layout = { |
| 157 | 158 | .mask = 0x373, |
| 158 | 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 | 12 | #include <linux/clk-provider.h> |
| 12 | 13 | #include <linux/clkdev.h> |
| 13 | 14 | #include <linux/clk/at91_pmc.h> |
| @@ -23,9 +24,6 @@ DEFINE_SPINLOCK(pmc_pcr_lock); · 9 unmodified lines | ||
| 23 | 24 | #define PERIPHERAL_ID_MAX 31 |
| 24 | 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 | 27 | #define PERIPHERAL_MAX_SHIFT 3 |
| 30 | 28 | |
| 31 | 29 | struct clk_peripheral { |
| @@ -43,6 +41,7 @@ struct clk_sam9x5_peripheral { · 11 unmodified lines | ||
| 43 | 41 | spinlock_t *lock; |
| 44 | 42 | u32 id; |
| 45 | 43 | u32 div; |
| 44 | + const struct clk_pcr_layout *layout; | |
| 46 | 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 | 168 | return 0; |
| 170 | 169 | |
| 171 | 170 | spin_lock_irqsave(periph->lock, flags); |
| 172 | − regmap_write(periph->regmap, AT91_PMC_PCR, | |
| 173 | − (periph->id & AT91_PMC_PCR_PID_MASK)); | |
| 174 | − regmap_update_bits(periph->regmap, AT91_PMC_PCR, | |
| 175 | − AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD | | |
| 171 | + regmap_write(periph->regmap, periph->layout->offset, | |
| 172 | + (periph->id & periph->layout->pid_mask)); | |
| 173 | + regmap_update_bits(periph->regmap, periph->layout->offset, | |
| 174 | + periph->layout->div_mask | periph->layout->cmd | | |
| 176 | 175 | AT91_PMC_PCR_EN, |
| 177 | − AT91_PMC_PCR_DIV(periph->div) | | |
| 178 | − AT91_PMC_PCR_CMD | | |
| 176 | + field_prep(periph->layout->div_mask, periph->div) | | |
| 177 | + periph->layout->cmd | | |
| 179 | 178 | AT91_PMC_PCR_EN); |
| 180 | 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 | 190 | return; |
| 192 | 191 | |
| 193 | 192 | spin_lock_irqsave(periph->lock, flags); |
| 194 | − regmap_write(periph->regmap, AT91_PMC_PCR, | |
| 195 | − (periph->id & AT91_PMC_PCR_PID_MASK)); | |
| 196 | − regmap_update_bits(periph->regmap, AT91_PMC_PCR, | |
| 197 | − AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD, | |
| 198 | − AT91_PMC_PCR_CMD); | |
| 193 | + regmap_write(periph->regmap, periph->layout->offset, | |
| 194 | + (periph->id & periph->layout->pid_mask)); | |
| 195 | + regmap_update_bits(periph->regmap, periph->layout->offset, | |
| 196 | + AT91_PMC_PCR_EN | periph->layout->cmd, | |
| 197 | + periph->layout->cmd); | |
| 199 | 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 | 208 | return 1; |
| 210 | 209 | |
| 211 | 210 | spin_lock_irqsave(periph->lock, flags); |
| 212 | − regmap_write(periph->regmap, AT91_PMC_PCR, | |
| 213 | − (periph->id & AT91_PMC_PCR_PID_MASK)); | |
| 214 | − regmap_read(periph->regmap, AT91_PMC_PCR, &status); | |
| 211 | + regmap_write(periph->regmap, periph->layout->offset, | |
| 212 | + (periph->id & periph->layout->pid_mask)); | |
| 213 | + regmap_read(periph->regmap, periph->layout->offset, &status); | |
| 215 | 214 | spin_unlock_irqrestore(periph->lock, flags); |
| 216 | 215 | |
| 217 | 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 | 228 | return parent_rate; |
| 230 | 229 | |
| 231 | 230 | spin_lock_irqsave(periph->lock, flags); |
| 232 | − regmap_write(periph->regmap, AT91_PMC_PCR, | |
| 233 | − (periph->id & AT91_PMC_PCR_PID_MASK)); | |
| 234 | − regmap_read(periph->regmap, AT91_PMC_PCR, &status); | |
| 231 | + regmap_write(periph->regmap, periph->layout->offset, | |
| 232 | + (periph->id & periph->layout->pid_mask)); | |
| 233 | + regmap_read(periph->regmap, periph->layout->offset, &status); | |
| 235 | 234 | spin_unlock_irqrestore(periph->lock, flags); |
| 236 | 235 | |
| 237 | 236 | if (status & AT91_PMC_PCR_EN) { |
| 238 | − periph->div = PERIPHERAL_RSHIFT(status); | |
| 237 | + periph->div = field_get(periph->layout->div_mask, status); | |
| 239 | 238 | periph->auto_div = false; |
| 240 | 239 | } else { |
| 241 | 240 | clk_sam9x5_peripheral_autodiv(periph); |
| @@ -328,6 +327,7 @@ static const struct clk_ops sam9x5_peripheral_ops = { · 86 unmodified lines | ||
| 328 | 327 | |
| 329 | 328 | struct clk_hw * __init |
| 330 | 329 | at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, |
| 330 | + const struct clk_pcr_layout *layout, | |
| 331 | 331 | const char *name, const char *parent_name, |
| 332 | 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 | 354 | periph->div = 0; |
| 355 | 355 | periph->regmap = regmap; |
| 356 | 356 | periph->lock = lock; |
| 357 | − periph->auto_div = true; | |
| 357 | + if (layout->div_mask) | |
| 358 | + periph->auto_div = true; | |
| 359 | + periph->layout = layout; | |
| 358 | 360 | periph->range = *range; |
| 359 | 361 | |
| 360 | 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 | 23 | #define RM9200_USB_DIV_SHIFT 28 |
| 24 | 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 | 29 | struct at91sam9x5_clk_usb { |
| 27 | 30 | struct clk_hw hw; |
| 28 | 31 | struct regmap *regmap; |
| 32 | + u32 usbs_mask; | |
| 29 | 33 | }; |
| 30 | 34 | |
| 31 | 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 | 115 | if (index > 1) |
| 112 | 116 | return -EINVAL; |
| 113 | 117 | |
| 114 | − regmap_update_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, | |
| 115 | − index ? AT91_PMC_USBS : 0); | |
| 118 | + regmap_update_bits(usb->regmap, AT91_PMC_USB, usb->usbs_mask, index); | |
| 116 | 119 | |
| 117 | 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 | 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 | 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 | 193 | .set_rate = at91sam9x5_clk_usb_set_rate, |
| 191 | 194 | }; |
| 192 | 195 | |
| 193 | −struct clk_hw * __init | |
| 194 | −at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, | |
| 195 | − const char **parent_names, u8 num_parents) | |
| 196 | +static struct clk_hw * __init | |
| 197 | +_at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name, | |
| 198 | + const char **parent_names, u8 num_parents, | |
| 199 | + u32 usbs_mask) | |
| 196 | 200 | { |
| 197 | 201 | struct at91sam9x5_clk_usb *usb; |
| 198 | 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 | 217 | usb->hw.init = &init; |
| 214 | 218 | usb->regmap = regmap; |
| 219 | + usb->usbs_mask = SAM9X5_USBS_MASK; | |
| 215 | 220 | |
| 216 | 221 | hw = &usb->hw; |
| 217 | 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 | 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 | 247 | struct clk_hw * __init |
| 227 | 248 | at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, |
| 228 | 249 | const char *parent_name) |
| @@ -93,6 +93,14 @@ CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup, · 92 unmodified lines | ||
| 93 | 93 | of_sama5d2_clk_audio_pll_pmc_setup); |
| 94 | 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 | 104 | #ifdef CONFIG_HAVE_AT91_GENERATED_CLK |
| 97 | 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 | 154 | id == GCK_ID_CLASSD)) |
| 147 | 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 | 159 | parent_names, num_parents, |
| 151 | 160 | id, pll_audio, &range); |
| 152 | 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 | 458 | hw = at91_clk_register_sam9x5_peripheral(regmap, |
| 450 | 459 | &pmc_pcr_lock, |
| 460 | + &dt_pcr_layout, | |
| 451 | 461 | name, |
| 452 | 462 | parent_name, |
| 453 | 463 | id, &range); |
| @@ -38,6 +38,7 @@ struct clk_range { · 37 unmodified lines | ||
| 38 | 38 | #define CLK_RANGE(MIN, MAX) {.min = MIN, .max = MAX,} |
| 39 | 39 | |
| 40 | 40 | struct clk_master_layout { |
| 41 | + u32 offset; | |
| 41 | 42 | u32 mask; |
| 42 | 43 | u8 pres_shift; |
| 43 | 44 | }; |
| @@ -65,9 +66,10 @@ extern const struct clk_pll_layout sama5d3_pll_layout; · 21 unmodified lines | ||
| 65 | 66 | struct clk_pll_characteristics { |
| 66 | 67 | struct clk_range input; |
| 67 | 68 | int num_output; |
| 68 | − struct clk_range *output; | |
| 69 | + const struct clk_range *output; | |
| 69 | 70 | u16 *icpll; |
| 70 | 71 | u8 *out; |
| 72 | + u8 upll : 1; | |
| 71 | 73 | }; |
| 72 | 74 | |
| 73 | 75 | struct clk_programmable_layout { |
| @@ -82,6 +84,17 @@ extern const struct clk_programmable_layout at91rm9200_programmable_layout; · 8 unmodified lines | ||
| 82 | 84 | extern const struct clk_programmable_layout at91sam9g45_programmable_layout; |
| 83 | 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 | 98 | #define ndck(a, s) (a[s - 1].id + 1) |
| 86 | 99 | #define nck(a) (a[ARRAY_SIZE(a) - 1].id + 1) |
| 87 | 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 | 121 | struct clk_hw * __init |
| 109 | 122 | at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock, |
| 123 | + const struct clk_pcr_layout *layout, | |
| 110 | 124 | const char *name, const char **parent_names, |
| 111 | 125 | u8 num_parents, u8 id, bool pll_audio, |
| 112 | 126 | const struct clk_range *range); |
| @@ -145,6 +159,7 @@ at91_clk_register_peripheral(struct regmap *regmap, const char *name, · 32 unmodified lines | ||
| 145 | 159 | const char *parent_name, u32 id); |
| 146 | 160 | struct clk_hw * __init |
| 147 | 161 | at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, |
| 162 | + const struct clk_pcr_layout *layout, | |
| 148 | 163 | const char *name, const char *parent_name, |
| 149 | 164 | u32 id, const struct clk_range *range); |
| 150 | 165 | |
| @@ -157,6 +172,11 @@ struct clk_hw * __init · 6 unmodified lines | ||
| 157 | 172 | at91_clk_register_plldiv(struct regmap *regmap, const char *name, |
| 158 | 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 | 180 | struct clk_hw * __init |
| 161 | 181 | at91_clk_register_programmable(struct regmap *regmap, const char *name, |
| 162 | 182 | const char **parent_names, u8 num_parents, u8 id, |
| @@ -183,6 +203,9 @@ struct clk_hw * __init · 20 unmodified lines | ||
| 183 | 203 | at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name, |
| 184 | 204 | const char *parent_name); |
| 185 | 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 | 209 | at91rm9200_clk_register_usb(struct regmap *regmap, const char *name, |
| 187 | 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 | 17 | static u16 plla_icpll[] = { 0 }; |
| 18 | 18 | |
| 19 | −static struct clk_range plla_outputs[] = { | |
| 19 | +static const struct clk_range plla_outputs[] = { | |
| 20 | 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 | 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 | 38 | static const struct { |
| 32 | 39 | char *n; |
| 33 | 40 | char *p; |
| @@ -274,6 +281,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np) · 240 unmodified lines | ||
| 274 | 281 | |
| 275 | 282 | for (i = 0; i < ARRAY_SIZE(sama5d2_periphck); i++) { |
| 276 | 283 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, |
| 284 | + &sama5d2_pcr_layout, | |
| 277 | 285 | sama5d2_periphck[i].n, |
| 278 | 286 | "masterck", |
| 279 | 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 | 295 | for (i = 0; i < ARRAY_SIZE(sama5d2_periph32ck); i++) { |
| 288 | 296 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, |
| 297 | + &sama5d2_pcr_layout, | |
| 289 | 298 | sama5d2_periph32ck[i].n, |
| 290 | 299 | "h32mxck", |
| 291 | 300 | sama5d2_periph32ck[i].id, |
| @@ -304,6 +313,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np) · 12 unmodified lines | ||
| 304 | 313 | parent_names[5] = "audiopll_pmcck"; |
| 305 | 314 | for (i = 0; i < ARRAY_SIZE(sama5d2_gck); i++) { |
| 306 | 315 | hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, |
| 316 | + &sama5d2_pcr_layout, | |
| 307 | 317 | sama5d2_gck[i].n, |
| 308 | 318 | parent_names, 6, |
| 309 | 319 | sama5d2_gck[i].id, |
| @@ -16,7 +16,7 @@ static u8 plla_out[] = { 0 }; · 15 unmodified lines | ||
| 16 | 16 | |
| 17 | 17 | static u16 plla_icpll[] = { 0 }; |
| 18 | 18 | |
| 19 | −static struct clk_range plla_outputs[] = { | |
| 19 | +static const struct clk_range plla_outputs[] = { | |
| 20 | 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 | 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 | 37 | static const struct { |
| 32 | 38 | char *n; |
| 33 | 39 | char *p; |
| @@ -232,6 +238,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np) · 198 unmodified lines | ||
| 232 | 238 | |
| 233 | 239 | for (i = 0; i < ARRAY_SIZE(sama5d4_periphck); i++) { |
| 234 | 240 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, |
| 241 | + &sama5d4_pcr_layout, | |
| 235 | 242 | sama5d4_periphck[i].n, |
| 236 | 243 | "masterck", |
| 237 | 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 | 252 | for (i = 0; i < ARRAY_SIZE(sama5d4_periph32ck); i++) { |
| 246 | 253 | hw = at91_clk_register_sam9x5_peripheral(regmap, &pmc_pcr_lock, |
| 254 | + &sama5d4_pcr_layout, | |
| 247 | 255 | sama5d4_periph32ck[i].n, |
| 248 | 256 | "h32mxck", |
| 249 | 257 | sama5d4_periph32ck[i].id, |
| @@ -152,28 +152,6 @@ at91_clk_register_slow_osc(void __iomem *sckcr, · 151 unmodified lines | ||
| 152 | 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 | 155 | static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, |
| 178 | 156 | unsigned long parent_rate) |
| 179 | 157 | { |
| @@ -266,28 +244,6 @@ at91_clk_register_slow_rc_osc(void __iomem *sckcr, · 86 unmodified lines | ||
| 266 | 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 | 247 | static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) |
| 292 | 248 | { |
| 293 | 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 | 321 | return hw; |
| 366 | 322 | } |
| 367 | 323 | |
| 368 | −static void __init | |
| 369 | −of_at91sam9x5_clk_slow_setup(struct device_node *np, void __iomem *sckcr) | |
| 324 | +static void __init at91sam9x5_sckc_register(struct device_node *np, | |
| 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 | 331 | struct clk_hw *hw; |
| 372 | − const char *parent_names[2]; | |
| 373 | − unsigned int num_parents; | |
| 374 | − const char *name = np->name; | |
| 332 | + bool bypass; | |
| 375 | 333 | |
| 376 | − num_parents = of_clk_get_parent_count(np); | |
| 377 | − if (num_parents == 0 || num_parents > 2) | |
| 334 | + if (!regbase) | |
| 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 | 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, | |
| 385 | − num_parents); | |
| 365 | + hw = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2); | |
| 386 | 366 | if (IS_ERR(hw)) |
| 387 | 367 | return; |
| 388 | 368 | |
| 389 | 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 = { | |
| 393 | − /* Slow clock */ | |
| 394 | − { | |
| 395 | − .compatible = "atmel,at91sam9x5-clk-slow-osc", | |
| 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 | −}; | |
| 371 | + /* DT backward compatibility */ | |
| 372 | + if (child) | |
| 373 | + of_clk_add_hw_provider(child, of_clk_hw_simple_get, hw); | |
| 374 | +} | |
| 408 | 375 | |
| 409 | 376 | static void __init of_at91sam9x5_sckc_setup(struct device_node *np) |
| 410 | 377 | { |
| 411 | − struct device_node *childnp; | |
| 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 | − } | |
| 378 | + at91sam9x5_sckc_register(np, 75); | |
| 426 | 379 | } |
| 427 | 380 | CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc", |
| 428 | 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 | 390 | static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw) |
| 431 | 391 | { |
| 432 | 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 | 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 | 383 | * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx |
| 305 | 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 | 397 | 0x0000000000000003ull, |
| 319 | 398 | 0x04f77f833e01c9ffull }; |
| 320 | 399 | |
| 400 | +static const u64 stm32f769_gate_map[MAX_GATE_MAP] = { 0x000000f37ef417ffull, | |
| 401 | + 0x0000000000000003ull, | |
| 402 | + 0x44F77F833E01EDFFull }; | |
| 403 | + | |
| 321 | 404 | static const u64 *stm32f4_gate_map; |
| 322 | 405 | |
| 323 | 406 | static struct clk_hw **clks; |
| @@ -1048,6 +1131,10 @@ static const char *rtc_parents[4] = { · 724 unmodified lines | ||
| 1048 | 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 | 1138 | static const char *dsi_parent[2] = { NULL, "pll-r" }; |
| 1052 | 1139 | |
| 1053 | 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 | 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 | 1165 | struct stm32_aux_clk { |
| 1076 | 1166 | int idx; |
| 1077 | 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 | 1577 | static const struct stm32f4_clk_data stm32f429_clk_data = { |
| 1317 | 1578 | .end_primary = END_PRIMARY_CLK, |
| 1318 | 1579 | .gates_data = stm32f429_gates, |
| @@ -1343,6 +1604,16 @@ static const struct stm32f4_clk_data stm32f746_clk_data = { · 24 unmodified lines | ||
| 1343 | 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 | 1617 | static const struct of_device_id stm32f4_of_match[] = { |
| 1347 | 1618 | { |
| 1348 | 1619 | .compatible = "st,stm32f42xx-rcc", |
| @@ -1356,6 +1627,10 @@ static const struct of_device_id stm32f4_of_match[] = { · 7 unmodified lines | ||
| 1356 | 1627 | .compatible = "st,stm32f746-rcc", |
| 1357 | 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 | 1702 | int n; |
| 1428 | 1703 | const struct of_device_id *match; |
| 1429 | 1704 | const struct stm32f4_clk_data *data; |
| 1430 | − unsigned long pllcfgr; | |
| 1431 | − const char *pllsrc; | |
| 1432 | 1705 | unsigned long pllm; |
| 1706 | + struct clk_hw *pll_src_hw; | |
| 1433 | 1707 | |
| 1434 | 1708 | base = of_iomap(np, 0); |
| 1435 | 1709 | if (!base) { |
| @@ -1460,21 +1734,33 @@ static void __init stm32f4_rcc_init(struct device_node *np) · 24 unmodified lines | ||
| 1460 | 1734 | |
| 1461 | 1735 | hse_clk = of_clk_get_parent_name(np, 0); |
| 1462 | 1736 | dsi_parent[0] = hse_clk; |
| 1737 | + pllsrc_parent[1] = hse_clk; | |
| 1463 | 1738 | |
| 1464 | 1739 | i2s_in_clk = of_clk_get_parent_name(np, 1); |
| 1465 | 1740 | |
| 1466 | 1741 | i2s_parents[1] = i2s_in_clk; |
| 1467 | 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 | 1752 | clks[CLK_HSI] = clk_hw_register_fixed_rate_with_accuracy(NULL, "hsi", |
| 1470 | 1753 | NULL, 0, 16000000, 160000); |
| 1471 | 1754 | |
| 1472 | − pllcfgr = readl(base + STM32F4_RCC_PLLCFGR); | |
| 1473 | − pllsrc = pllcfgr & BIT(22) ? hse_clk : "hsi"; | |
| 1474 | − pllm = pllcfgr & 0x3f; | |
| 1755 | + pll_src_hw = clk_hw_register_mux(NULL, pll_src, pllsrc_parent, | |
| 1756 | + ARRAY_SIZE(pllsrc_parent), 0, | |
| 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, | |
| 1477 | − 0, 1, pllm); | |
| 1762 | + clk_hw_register_fixed_factor(NULL, "vco_in", pll_src, | |
| 1763 | + 0, 1, pllm); | |
| 1478 | 1764 | |
| 1479 | 1765 | stm32f4_rcc_register_pll("vco_in", &data->pll_data[0], |
| 1480 | 1766 | &stm32f4_clk_lock); |
| @@ -1612,12 +1898,16 @@ static void __init stm32f4_rcc_init(struct device_node *np) · 131 unmodified lines | ||
| 1612 | 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 | 1903 | clk_hw_register_fixed_factor(NULL, "hsi_div488", "hsi", 0, |
| 1618 | 1904 | 1, 488); |
| 1619 | 1905 | |
| 1906 | + clks[CLK_PLL_SRC] = pll_src_hw; | |
| 1907 | + } | |
| 1908 | + | |
| 1620 | 1909 | of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL); |
| 1910 | + | |
| 1621 | 1911 | return; |
| 1622 | 1912 | fail: |
| 1623 | 1913 | kfree(clks); |
| @@ -1626,3 +1916,4 @@ fail: · 2 unmodified lines | ||
| 1626 | 1916 | CLK_OF_DECLARE_DRIVER(stm32f42xx_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init); |
| 1627 | 1917 | CLK_OF_DECLARE_DRIVER(stm32f46xx_rcc, "st,stm32f469-rcc", stm32f4_rcc_init); |
| 1628 | 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 | 1402 | G_CRYP1, |
| 1403 | 1403 | G_HASH1, |
| 1404 | 1404 | G_BKPSRAM, |
| 1405 | + G_DDRPERFM, | |
| 1405 | 1406 | |
| 1406 | 1407 | G_LAST |
| 1407 | 1408 | }; |
| @@ -1488,6 +1489,7 @@ static struct stm32_gate_cfg per_gate_cfg[G_LAST] = { · 80 unmodified lines | ||
| 1488 | 1489 | K_GATE(G_STGENRO, RCC_APB4ENSETR, 20, 0), |
| 1489 | 1490 | K_MGATE(G_USBPHY, RCC_APB4ENSETR, 16, 0), |
| 1490 | 1491 | K_GATE(G_IWDG2, RCC_APB4ENSETR, 15, 0), |
| 1492 | + K_GATE(G_DDRPERFM, RCC_APB4ENSETR, 8, 0), | |
| 1491 | 1493 | K_MGATE(G_DSI, RCC_APB4ENSETR, 4, 0), |
| 1492 | 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 | 1901 | PCLK(CRC1, "crc1", "ck_axi", 0, G_CRC1), |
| 1900 | 1902 | PCLK(USBH, "usbh", "ck_axi", 0, G_USBH), |
| 1901 | 1903 | PCLK(ETHSTP, "ethstp", "ck_axi", 0, G_ETHSTP), |
| 1904 | + PCLK(DDRPERFM, "ddrperfm", "pclk4", 0, G_DDRPERFM), | |
| 1902 | 1905 | |
| 1903 | 1906 | /* Kernel clocks */ |
| 1904 | 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 | 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 | 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 | 3 | * Copyright (c) 2017, Linaro Limited |
| 4 | 4 | * Author: Georgi Djakov <georgi.djakov@linaro.org> |
| @@ -1,5 +1,5 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0 | |
| 2 | − * | |
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | +/* | |
| 3 | 3 | * R-Car Gen2 Clock Pulse Generator |
| 4 | 4 | * |
| 5 | 5 | * Copyright (C) 2016 Cogent Embedded Inc. |
| @@ -1,5 +1,5 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0 | |
| 2 | − * | |
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | +/* | |
| 3 | 3 | * R-Car Gen3 Clock Pulse Generator |
| 4 | 4 | * |
| 5 | 5 | * Copyright (C) 2015-2018 Glider bvba |
| @@ -1,5 +1,5 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0 | |
| 2 | − * | |
| 1 | +/* SPDX-License-Identifier: GPL-2.0 */ | |
| 2 | +/* | |
| 3 | 3 | * Renesas Clock Pulse Generator / Module Standby and Software Reset |
| 4 | 4 | * |
| 5 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 3 | * Copyright 2016 Icenowy Zheng <icenowy@aosc.io> |
| 4 | 4 | */ |
| @@ -1,5 +1,5 @@ | ||
| 1 | −/* SPDX-License-Identifier: GPL-2.0+ | |
| 2 | − * | |
| 1 | +/* SPDX-License-Identifier: GPL-2.0+ */ | |
| 2 | +/* | |
| 3 | 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 | 175 | void __iomem *reg, spinlock_t *lock) |
| 176 | 176 | { |
| 177 | 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 | 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 | 121 | struct tegra_clk_emc *tegra; |
| 122 | 122 | u8 ram_code = tegra_read_ram_code(); |
| 123 | 123 | struct emc_timing *timing = NULL; |
| 124 | − int i; | |
| 124 | + int i, k, t; | |
| 125 | 125 | |
| 126 | 126 | tegra = container_of(hw, struct tegra_clk_emc, hw); |
| 127 | 127 | |
| 128 | − for (i = 0; i < tegra->num_timings; i++) { | |
| 129 | − if (tegra->timings[i].ram_code != ram_code) | |
| 130 | − continue; | |
| 128 | + for (k = 0; k < tegra->num_timings; k++) { | |
| 129 | + if (tegra->timings[k].ram_code == ram_code) | |
| 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 | 139 | timing = tegra->timings + i; |
| 133 | 140 | |
| 141 | + if (timing->rate < req->rate && i != t - 1) | |
| 142 | + continue; | |
| 143 | + | |
| 134 | 144 | if (timing->rate > req->max_rate) { |
| 135 | − i = max(i, 1); | |
| 145 | + i = max(i, k + 1); | |
| 136 | 146 | req->rate = tegra->timings[i - 1].rate; |
| 137 | 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 | 150 | if (timing->rate < req->min_rate) |
| 141 | 151 | continue; |
| 142 | 152 | |
| 143 | − if (timing->rate >= req->rate) { | |
| 144 | − req->rate = timing->rate; | |
| 145 | − return 0; | |
| 146 | − } | |
| 153 | + req->rate = timing->rate; | |
| 154 | + return 0; | |
| 147 | 155 | } |
| 148 | 156 | |
| 149 | 157 | if (timing) { |
| @@ -214,7 +222,10 @@ static int emc_set_timing(struct tegra_clk_emc *tegra, · 64 unmodified lines | ||
| 214 | 222 | |
| 215 | 223 | if (emc_get_parent(&tegra->hw) == timing->parent_index && |
| 216 | 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 | 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 | 293 | for (i = timing_index+1; i < tegra->num_timings; i++) { |
| 283 | 294 | timing = tegra->timings + i; |
| 284 | 295 | if (timing->ram_code != ram_code) |
| 285 | − continue; | |
| 296 | + break; | |
| 286 | 297 | |
| 287 | 298 | if (emc_parent_clk_sources[timing->parent_index] != |
| 288 | 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 | 304 | for (i = timing_index-1; i >= 0; --i) { |
| 294 | 305 | timing = tegra->timings + i; |
| 295 | 306 | if (timing->ram_code != ram_code) |
| 296 | − continue; | |
| 307 | + break; | |
| 297 | 308 | |
| 298 | 309 | if (emc_parent_clk_sources[timing->parent_index] != |
| 299 | 310 | emc_parent_clk_sources[ |
| @@ -433,19 +444,23 @@ static int load_timings_from_dt(struct tegra_clk_emc *tegra, · 133 unmodified lines | ||
| 433 | 444 | struct device_node *node, |
| 434 | 445 | u32 ram_code) |
| 435 | 446 | { |
| 447 | + struct emc_timing *timings_ptr; | |
| 436 | 448 | struct device_node *child; |
| 437 | 449 | int child_count = of_get_child_count(node); |
| 438 | 450 | int i = 0, err; |
| 451 | + size_t size; | |
| 439 | 452 | |
| 440 | − tegra->timings = kcalloc(child_count, sizeof(struct emc_timing), | |
| 441 | − GFP_KERNEL); | |
| 453 | + size = (tegra->num_timings + child_count) * sizeof(struct emc_timing); | |
| 454 | + | |
| 455 | + tegra->timings = krealloc(tegra->timings, size, GFP_KERNEL); | |
| 442 | 456 | if (!tegra->timings) |
| 443 | 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 | 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 | 465 | err = load_one_timing_from_dt(tegra, timing, child); |
| 451 | 466 | if (err) { |
| @@ -456,7 +471,7 @@ static int load_timings_from_dt(struct tegra_clk_emc *tegra, · 4 unmodified lines | ||
| 456 | 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 | 475 | cmp_timings, NULL); |
| 461 | 476 | |
| 462 | 477 | return 0; |
| @@ -499,10 +514,10 @@ struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, · 36 unmodified lines | ||
| 499 | 514 | * fuses until the apbmisc driver is loaded. |
| 500 | 515 | */ |
| 501 | 516 | err = load_timings_from_dt(tegra, node, node_ram_code); |
| 502 | − of_node_put(node); | |
| 503 | − if (err) | |
| 517 | + if (err) { | |
| 518 | + of_node_put(node); | |
| 504 | 519 | return ERR_PTR(err); |
| 505 | − break; | |
| 520 | + } | |
| 506 | 521 | } |
| 507 | 522 | |
| 508 | 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 | 547 | /* Allow debugging tools to see the EMC clock */ |
| 533 | 548 | clk_register_clkdev(clk, "emc", "tegra-clk-debug"); |
| 534 | 549 | |
| 535 | − clk_prepare_enable(clk); | |
| 536 | − | |
| 537 | 550 | return clk; |
| 538 | 551 | }; |
| @@ -444,6 +444,9 @@ static int clk_pll_enable(struct clk_hw *hw) · 443 unmodified lines | ||
| 444 | 444 | unsigned long flags = 0; |
| 445 | 445 | int ret; |
| 446 | 446 | |
| 447 | + if (clk_pll_is_enabled(hw)) | |
| 448 | + return 0; | |
| 449 | + | |
| 447 | 450 | if (pll->lock) |
| 448 | 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 | 666 | pll_override_writel(val, params->pmc_divp_reg, pll); |
| 664 | 667 | |
| 665 | 668 | val = pll_override_readl(params->pmc_divnm_reg, pll); |
| 666 | − val &= ~(divm_mask(pll) << div_nmp->override_divm_shift) | | |
| 667 | − ~(divn_mask(pll) << div_nmp->override_divn_shift); | |
| 669 | + val &= ~((divm_mask(pll) << div_nmp->override_divm_shift) | | |
| 670 | + (divn_mask(pll) << div_nmp->override_divn_shift)); | |
| 668 | 671 | val |= (cfg->m << div_nmp->override_divm_shift) | |
| 669 | 672 | (cfg->n << div_nmp->override_divn_shift); |
| 670 | 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 | 943 | static int clk_plle_enable(struct clk_hw *hw) |
| 941 | 944 | { |
| 942 | 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 | 946 | struct tegra_clk_pll_freq_table sel; |
| 947 | + unsigned long input_rate; | |
| 945 | 948 | u32 val; |
| 946 | 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 | 956 | if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate)) |
| 949 | 957 | return -EINVAL; |
| 950 | 958 | |
| @@ -1355,6 +1363,9 @@ static int clk_pllc_enable(struct clk_hw *hw) · 404 unmodified lines | ||
| 1355 | 1363 | int ret; |
| 1356 | 1364 | unsigned long flags = 0; |
| 1357 | 1365 | |
| 1366 | + if (clk_pll_is_enabled(hw)) | |
| 1367 | + return 0; | |
| 1368 | + | |
| 1358 | 1369 | if (pll->lock) |
| 1359 | 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 | 1578 | u32 val; |
| 1568 | 1579 | int ret; |
| 1569 | 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 | 1588 | if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate)) |
| 1573 | 1589 | return -EINVAL; |
| @@ -1704,6 +1720,9 @@ static int clk_pllu_tegra114_enable(struct clk_hw *hw) · 130 unmodified lines | ||
| 1704 | 1720 | return -EINVAL; |
| 1705 | 1721 | } |
| 1706 | 1722 | |
| 1723 | + if (clk_pll_is_enabled(hw)) | |
| 1724 | + return 0; | |
| 1725 | + | |
| 1707 | 1726 | input_rate = clk_hw_get_rate(__clk_get_hw(osc)); |
| 1708 | 1727 | |
| 1709 | 1728 | if (pll->lock) |
| @@ -2379,6 +2398,16 @@ struct clk *tegra_clk_register_pllre_tegra210(const char *name, · 669 unmodified lines | ||
| 2379 | 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 | 2411 | static int clk_plle_tegra210_enable(struct clk_hw *hw) |
| 2383 | 2412 | { |
| 2384 | 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 | 2415 | u32 val; |
| 2387 | 2416 | int ret = 0; |
| 2388 | 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 | 2425 | if (_get_table_rate(hw, &sel, pll->params->fixed_rate, input_rate)) |
| 2392 | 2426 | return -EINVAL; |
| @@ -2497,16 +2531,6 @@ out: · 104 unmodified lines | ||
| 2497 | 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 | 2534 | static const struct clk_ops tegra_clk_plle_tegra210_ops = { |
| 2511 | 2535 | .is_enabled = clk_plle_tegra210_is_enabled, |
| 2512 | 2536 | .enable = clk_plle_tegra210_enable, |
| @@ -413,7 +413,6 @@ static struct tegra_clk_pll_params pll_m_params = { · 412 unmodified lines | ||
| 413 | 413 | .base_reg = PLLM_BASE, |
| 414 | 414 | .misc_reg = PLLM_MISC, |
| 415 | 415 | .lock_mask = PLL_BASE_LOCK, |
| 416 | − .lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE, | |
| 417 | 416 | .lock_delay = 300, |
| 418 | 417 | .max_p = 5, |
| 419 | 418 | .pdiv_tohw = pllm_p, |
| @@ -421,7 +420,7 @@ static struct tegra_clk_pll_params pll_m_params = { · 1 unmodified line | ||
| 421 | 420 | .pmc_divnm_reg = PMC_PLLM_WB0_OVERRIDE, |
| 422 | 421 | .pmc_divp_reg = PMC_PLLM_WB0_OVERRIDE_2, |
| 423 | 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 | 426 | static struct tegra_clk_pll_freq_table pll_e_freq_table[] = { |
| @@ -54,7 +54,10 @@ · 53 unmodified lines | ||
| 54 | 54 | #define CLK_I2C3 28 |
| 55 | 55 | #define CLK_I2C4 29 |
| 56 | 56 | #define CLK_LPTIMER 30 |
| 57 | − | |
| 58 | −#define END_PRIMARY_CLK_F7 31 | |
| 57 | +#define CLK_PLL_SRC 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 | 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 | 74 | #define AT91_PMC_USBDIV_4 (2 << 28) |
| 75 | 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 | 79 | #define AT91_PMC_MCKR 0x30 /* Master Clock Register */ |
| 78 | 80 | #define AT91_PMC_CSS (3 << 0) /* Master Clock Selection */ |
| 79 | 81 | #define AT91_PMC_CSS_SLOW (0 << 0) |
| @@ -187,16 +189,8 @@ · 107 unmodified lines | ||
| 187 | 189 | |
| 188 | 190 | #define AT91_PMC_PCR 0x10c /* Peripheral Control Register [some SAM9 and SAMA5] */ |
| 189 | 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 | 192 | #define AT91_PMC_PCR_CMD (0x1 << 12) /* Command (read=0, write=1) */ |
| 194 | −#define AT91_PMC_PCR_DIV_OFFSET 16 | |
| 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 */ | |
| 193 | +#define AT91_PMC_PCR_GCKDIV_MASK GENMASK(27, 20) | |
| 200 | 194 | #define AT91_PMC_PCR_EN (0x1 << 28) /* Enable */ |
| 201 | 195 | #define AT91_PMC_PCR_GCKEN (0x1 << 29) /* GCK Enable */ |
| 202 | 196 | |