Merge branches 'clk-airoha', 'clk-rockchip', 'clk-stm', 'clk-thead' and 'clk-bcm' into clk-next

* clk-airoha:
  clk: en7523: Add clock for eMMC for EN7581
  dt-bindings: clock: add ID for eMMC for EN7581
  dt-bindings: clock: drop NUM_CLOCKS define for EN7581
  clk: en7523: Rework clock handling for different clock numbers
  clk: en7523: Initialize num before accessing hws in en7523_register_clocks()
  clk: en7523: Fix wrong BUS clock for EN7581
  clk: amlogic: axg-audio: revert reset implementation
  Revert "clk: Fix invalid execution of clk_set_rate"

* clk-rockchip:
  clk: rockchip: rk3588: make refclko25m_ethX critical
  clk: rockchip: rk3588: drop RK3588_LINKED_CLK
  clk: rockchip: implement linked gate clock support
  clk: rockchip: expose rockchip_clk_set_lookup
  clk: rockchip: rk3588: register GATE_LINK later
  clk: rockchip: support clocks registered late

* clk-stm:
  clk: stm32f4: support spread spectrum clock generation
  clk: stm32f4: use FIELD helpers to access the PLLCFGR fields
  dt-bindings: clock: st,stm32-rcc: support spread spectrum clocking
  dt-bindings: clock: convert stm32 rcc bindings to json-schema

* clk-thead:
  clk: thead: Fix cpu2vp_clk for TH1520 AP_SUBSYS clocks
  clk: thead: Add CLK_IGNORE_UNUSED to fix TH1520 boot
  clk: thead: Fix clk gate registration to pass flags

* clk-bcm:
  clk: bcm: rpi: Add disp clock
  clk: bcm: rpi: Create helper to retrieve private data
  clk: bcm: rpi: Enable minimize for all firmware clocks
  clk: bcm: rpi: Allow cpufreq driver to also adjust gpu clocks
  clk: bcm: rpi: Add ISP to exported clocks
This commit is contained in:
Stephen Boyd 2025-01-21 11:22:26 -08:00
18 changed files with 742 additions and 244 deletions

View File

@ -1,138 +0,0 @@
STMicroelectronics STM32 Reset and Clock Controller
===================================================
The RCC IP is both a reset and a clock controller.
Please refer to clock-bindings.txt for common clock controller binding usage.
Please also refer to reset.txt for common reset controller binding usage.
Required properties:
- compatible: Should be:
"st,stm32f42xx-rcc"
"st,stm32f469-rcc"
"st,stm32f746-rcc"
"st,stm32f769-rcc"
- reg: should be register base and length as documented in the
datasheet
- #reset-cells: 1, see below
- #clock-cells: 2, device nodes should specify the clock in their "clocks"
property, containing a phandle to the clock device node, an index selecting
between gated clocks and other clocks and an index specifying the clock to
use.
- clocks: External oscillator clock phandle
- high speed external clock signal (HSE)
- external I2S clock (I2S_CKIN)
Example:
rcc: rcc@40023800 {
#reset-cells = <1>;
#clock-cells = <2>
compatible = "st,stm32f42xx-rcc", "st,stm32-rcc";
reg = <0x40023800 0x400>;
clocks = <&clk_hse>, <&clk_i2s_ckin>;
};
Specifying gated clocks
=======================
The primary index must be set to 0.
The secondary index is the bit number within the RCC register bank, starting
from the first RCC clock enable register (RCC_AHB1ENR, address offset 0x30).
It is calculated as: index = register_offset / 4 * 32 + bit_offset.
Where bit_offset is the bit offset within the register (LSB is 0, MSB is 31).
To simplify the usage and to share bit definition with the reset and clock
drivers of the RCC IP, macros are available to generate the index in
human-readble format.
For STM32F4 series, the macro are available here:
- include/dt-bindings/mfd/stm32f4-rcc.h
Example:
/* Gated clock, AHB1 bit 0 (GPIOA) */
... {
clocks = <&rcc 0 STM32F4_AHB1_CLOCK(GPIOA)>
};
/* Gated clock, AHB2 bit 4 (CRYP) */
... {
clocks = <&rcc 0 STM32F4_AHB2_CLOCK(CRYP)>
};
Specifying other clocks
=======================
The primary index must be set to 1.
The secondary index is bound with the following magic numbers:
0 SYSTICK
1 FCLK
2 CLK_LSI (low-power clock source)
3 CLK_LSE (generated from a 32.768 kHz low-speed external
crystal or ceramic resonator)
4 CLK_HSE_RTC (HSE division factor for RTC clock)
5 CLK_RTC (real-time clock)
6 PLL_VCO_I2S (vco frequency of I2S pll)
7 PLL_VCO_SAI (vco frequency of SAI pll)
8 CLK_LCD (LCD-TFT)
9 CLK_I2S (I2S clocks)
10 CLK_SAI1 (audio clocks)
11 CLK_SAI2
12 CLK_I2SQ_PDIV (post divisor of pll i2s q divisor)
13 CLK_SAIQ_PDIV (post divisor of pll sai q divisor)
14 CLK_HSI (Internal ocscillator clock)
15 CLK_SYSCLK (System Clock)
16 CLK_HDMI_CEC (HDMI-CEC clock)
17 CLK_SPDIF (SPDIF-Rx clock)
18 CLK_USART1 (U(s)arts clocks)
19 CLK_USART2
20 CLK_USART3
21 CLK_UART4
22 CLK_UART5
23 CLK_USART6
24 CLK_UART7
25 CLK_UART8
26 CLK_I2C1 (I2S clocks)
27 CLK_I2C2
28 CLK_I2C3
29 CLK_I2C4
30 CLK_LPTIMER (LPTimer1 clock)
31 CLK_PLL_SRC
32 CLK_DFSDM1
33 CLK_ADFSDM1
34 CLK_F769_DSI
)
Example:
/* Misc clock, FCLK */
... {
clocks = <&rcc 1 STM32F4_APB1_CLOCK(TIM2)>
};
Specifying softreset control of devices
=======================================
Device nodes should specify the reset channel required in their "resets"
property, containing a phandle to the reset device node and an index specifying
which channel to use.
The index is the bit number within the RCC registers bank, starting from RCC
base address.
It is calculated as: index = register_offset / 4 * 32 + bit_offset.
Where bit_offset is the bit offset within the register.
For example, for CRC reset:
crc = AHB1RSTR_offset / 4 * 32 + CRCRST_bit_offset = 0x10 / 4 * 32 + 12 = 140
example:
timer2 {
resets = <&rcc STM32F4_APB1_RESET(TIM2)>;
};

View File

@ -0,0 +1,144 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/st,stm32-rcc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: STMicroelectronics STM32 Reset Clock Controller
maintainers:
- Dario Binacchi <dario.binacchi@amarulasolutions.com>
description: |
The RCC IP is both a reset and a clock controller.
The reset phandle argument is the bit number within the RCC registers bank,
starting from RCC base address.
properties:
compatible:
oneOf:
- items:
- enum:
- st,stm32f42xx-rcc
- st,stm32f746-rcc
- st,stm32h743-rcc
- const: st,stm32-rcc
- items:
- enum:
- st,stm32f469-rcc
- const: st,stm32f42xx-rcc
- const: st,stm32-rcc
- items:
- enum:
- st,stm32f769-rcc
- const: st,stm32f746-rcc
- const: st,stm32-rcc
reg:
maxItems: 1
'#reset-cells':
const: 1
'#clock-cells':
enum: [1, 2]
clocks:
minItems: 2
maxItems: 3
st,syscfg:
$ref: /schemas/types.yaml#/definitions/phandle
description:
Phandle to system configuration controller. It can be used to control the
power domain circuitry.
st,ssc-modfreq-hz:
description:
The modulation frequency for main PLL (in Hz)
st,ssc-moddepth-permyriad:
$ref: /schemas/types.yaml#/definitions/uint32
description:
The modulation rate for main PLL (in permyriad, i.e. 0.01%)
minimum: 25
maximum: 200
st,ssc-modmethod:
$ref: /schemas/types.yaml#/definitions/string
description:
The modulation techniques for main PLL.
items:
enum:
- center-spread
- down-spread
required:
- compatible
- reg
- '#reset-cells'
- '#clock-cells'
- clocks
- st,syscfg
allOf:
- if:
properties:
compatible:
contains:
const: st,stm32h743-rcc
then:
properties:
'#clock-cells':
const: 1
description: |
The clock index for the specified type.
clocks:
items:
- description: high speed external (HSE) clock input
- description: low speed external (LSE) clock input
- description: Inter-IC sound (I2S) clock input
st,ssc-modfreq-hz: false
st,ssc-moddepth-permyriad: false
st,ssc-modmethod: false
else:
properties:
'#clock-cells':
const: 2
description: |
- The first cell is the clock type, possible values are 0 for
gated clocks and 1 otherwise.
- The second cell is the clock index for the specified type.
clocks:
items:
- description: high speed external (HSE) clock input
- description: Inter-IC sound (I2S) clock input
additionalProperties: false
examples:
# Reset and Clock Control Module node:
- |
clock-controller@40023800 {
compatible = "st,stm32f42xx-rcc", "st,stm32-rcc";
reg = <0x40023800 0x400>;
#clock-cells = <2>;
#reset-cells = <1>;
clocks = <&clk_hse>, <&clk_i2s_ckin>;
st,syscfg = <&pwrcfg>;
st,ssc-modfreq-hz = <10000>;
st,ssc-moddepth-permyriad = <200>;
st,ssc-modmethod = "center-spread";
};
- |
clock-controller@58024400 {
compatible = "st,stm32h743-rcc", "st,stm32-rcc";
reg = <0x58024400 0x400>;
#clock-cells = <1>;
#reset-cells = <1>;
clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s>;
st,syscfg = <&pwrcfg>;
};
...

View File

@ -3,4 +3,4 @@ STMicroelectronics STM32 Peripheral Reset Controller
The RCC IP is both a reset and a clock controller.
Please see Documentation/devicetree/bindings/clock/st,stm32-rcc.txt
Please see Documentation/devicetree/bindings/clock/st,stm32-rcc.yaml

View File

@ -34,6 +34,7 @@ static char *rpi_firmware_clk_names[] = {
[RPI_FIRMWARE_M2MC_CLK_ID] = "m2mc",
[RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = "pixel-bvb",
[RPI_FIRMWARE_VEC_CLK_ID] = "vec",
[RPI_FIRMWARE_DISP_CLK_ID] = "disp",
};
#define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0)
@ -56,6 +57,12 @@ struct raspberrypi_clk_data {
struct raspberrypi_clk *rpi;
};
static inline
const struct raspberrypi_clk_data *clk_hw_to_data(const struct clk_hw *hw)
{
return container_of(hw, struct raspberrypi_clk_data, hw);
}
struct raspberrypi_clk_variant {
bool export;
char *clkdev;
@ -111,18 +118,31 @@ raspberrypi_clk_variants[RPI_FIRMWARE_NUM_CLK_ID] = {
},
[RPI_FIRMWARE_V3D_CLK_ID] = {
.export = true,
.minimize = true,
},
[RPI_FIRMWARE_PIXEL_CLK_ID] = {
.export = true,
.minimize = true,
},
[RPI_FIRMWARE_HEVC_CLK_ID] = {
.export = true,
.minimize = true,
},
[RPI_FIRMWARE_ISP_CLK_ID] = {
.export = true,
.minimize = true,
},
[RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = {
.export = true,
.minimize = true,
},
[RPI_FIRMWARE_VEC_CLK_ID] = {
.export = true,
.minimize = true,
},
[RPI_FIRMWARE_DISP_CLK_ID] = {
.export = true,
.minimize = true,
},
};
@ -153,7 +173,6 @@ static int raspberrypi_clock_property(struct rpi_firmware *firmware,
struct raspberrypi_firmware_prop msg = {
.id = cpu_to_le32(data->id),
.val = cpu_to_le32(*val),
.disable_turbo = cpu_to_le32(1),
};
int ret;
@ -168,8 +187,7 @@ static int raspberrypi_clock_property(struct rpi_firmware *firmware,
static int raspberrypi_fw_is_prepared(struct clk_hw *hw)
{
struct raspberrypi_clk_data *data =
container_of(hw, struct raspberrypi_clk_data, hw);
const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
struct raspberrypi_clk *rpi = data->rpi;
u32 val = 0;
int ret;
@ -186,8 +204,7 @@ static int raspberrypi_fw_is_prepared(struct clk_hw *hw)
static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct raspberrypi_clk_data *data =
container_of(hw, struct raspberrypi_clk_data, hw);
const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
struct raspberrypi_clk *rpi = data->rpi;
u32 val = 0;
int ret;
@ -203,8 +220,7 @@ static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw,
static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct raspberrypi_clk_data *data =
container_of(hw, struct raspberrypi_clk_data, hw);
const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
struct raspberrypi_clk *rpi = data->rpi;
u32 _rate = rate;
int ret;
@ -221,8 +237,7 @@ static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate,
static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct raspberrypi_clk_data *data =
container_of(hw, struct raspberrypi_clk_data, hw);
const struct raspberrypi_clk_data *data = clk_hw_to_data(hw);
struct raspberrypi_clk_variant *variant = data->variant;
/*

View File

@ -75,6 +75,7 @@ struct en_rst_data {
};
struct en_clk_soc_data {
u32 num_clocks;
const struct clk_ops pcie_ops;
int (*hw_init)(struct platform_device *pdev,
struct clk_hw_onecell_data *clk_data);
@ -87,8 +88,10 @@ static const u32 slic_base[] = { 100000000, 3125000 };
static const u32 npu_base[] = { 333000000, 400000000, 500000000 };
/* EN7581 */
static const u32 emi7581_base[] = { 540000000, 480000000, 400000000, 300000000 };
static const u32 bus7581_base[] = { 600000000, 540000000 };
static const u32 npu7581_base[] = { 800000000, 750000000, 720000000, 600000000 };
static const u32 crypto_base[] = { 540000000, 480000000 };
static const u32 emmc7581_base[] = { 200000000, 150000000 };
static const struct en_clk_desc en7523_base_clks[] = {
{
@ -222,8 +225,8 @@ static const struct en_clk_desc en7581_base_clks[] = {
.base_reg = REG_BUS_CLK_DIV_SEL,
.base_bits = 1,
.base_shift = 8,
.base_values = bus_base,
.n_base_values = ARRAY_SIZE(bus_base),
.base_values = bus7581_base,
.n_base_values = ARRAY_SIZE(bus7581_base),
.div_bits = 3,
.div_shift = 0,
@ -279,6 +282,15 @@ static const struct en_clk_desc en7581_base_clks[] = {
.base_shift = 0,
.base_values = crypto_base,
.n_base_values = ARRAY_SIZE(crypto_base),
}, {
.id = EN7581_CLK_EMMC,
.name = "emmc",
.base_reg = REG_CRYPTO_CLKSRC2,
.base_bits = 1,
.base_shift = 12,
.base_values = emmc7581_base,
.n_base_values = ARRAY_SIZE(emmc7581_base),
}
};
@ -524,8 +536,6 @@ static void en7523_register_clocks(struct device *dev, struct clk_hw_onecell_dat
hw = en7523_register_pcie_clk(dev, np_base);
clk_data->hws[EN7523_CLK_PCIE] = hw;
clk_data->num = EN7523_NUM_CLOCKS;
}
static int en7523_clk_hw_init(struct platform_device *pdev,
@ -586,8 +596,6 @@ static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_dat
hw = en7523_register_pcie_clk(dev, base);
clk_data->hws[EN7523_CLK_PCIE] = hw;
clk_data->num = EN7523_NUM_CLOCKS;
}
static int en7523_reset_update(struct reset_controller_dev *rcdev,
@ -701,13 +709,15 @@ static int en7523_clk_probe(struct platform_device *pdev)
struct clk_hw_onecell_data *clk_data;
int r;
soc_data = device_get_match_data(&pdev->dev);
clk_data = devm_kzalloc(&pdev->dev,
struct_size(clk_data, hws, EN7523_NUM_CLOCKS),
struct_size(clk_data, hws, soc_data->num_clocks),
GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
soc_data = device_get_match_data(&pdev->dev);
clk_data->num = soc_data->num_clocks;
r = soc_data->hw_init(pdev, clk_data);
if (r)
return r;
@ -716,6 +726,7 @@ static int en7523_clk_probe(struct platform_device *pdev)
}
static const struct en_clk_soc_data en7523_data = {
.num_clocks = ARRAY_SIZE(en7523_base_clks) + 1,
.pcie_ops = {
.is_enabled = en7523_pci_is_enabled,
.prepare = en7523_pci_prepare,
@ -725,6 +736,8 @@ static const struct en_clk_soc_data en7523_data = {
};
static const struct en_clk_soc_data en7581_data = {
/* We increment num_clocks by 1 to account for additional PCIe clock */
.num_clocks = ARRAY_SIZE(en7581_base_clks) + 1,
.pcie_ops = {
.is_enabled = en7581_pci_is_enabled,
.enable = en7581_pci_enable,

View File

@ -5,6 +5,7 @@
* Inspired by clk-asm9260.c .
*/
#include <linux/bitfield.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/io.h>
@ -34,11 +35,20 @@
#define STM32F4_RCC_APB2ENR 0x44
#define STM32F4_RCC_BDCR 0x70
#define STM32F4_RCC_CSR 0x74
#define STM32F4_RCC_SSCGR 0x80
#define STM32F4_RCC_PLLI2SCFGR 0x84
#define STM32F4_RCC_PLLSAICFGR 0x88
#define STM32F4_RCC_DCKCFGR 0x8c
#define STM32F7_RCC_DCKCFGR2 0x90
#define STM32F4_RCC_PLLCFGR_N_MASK GENMASK(14, 6)
#define STM32F4_RCC_SSCGR_SSCGEN BIT(31)
#define STM32F4_RCC_SSCGR_SPREADSEL BIT(30)
#define STM32F4_RCC_SSCGR_RESERVED_MASK GENMASK(29, 28)
#define STM32F4_RCC_SSCGR_INCSTEP_MASK GENMASK(27, 13)
#define STM32F4_RCC_SSCGR_MODPER_MASK GENMASK(12, 0)
#define NONE -1
#define NO_IDX NONE
#define NO_MUX NONE
@ -364,6 +374,16 @@ static const struct stm32f4_gate_data stm32f769_gates[] __initconst = {
{ STM32F4_RCC_APB2ENR, 30, "mdio", "apb2_div" },
};
enum stm32f4_pll_ssc_mod_type {
STM32F4_PLL_SSC_CENTER_SPREAD,
STM32F4_PLL_SSC_DOWN_SPREAD,
};
static const char * const stm32f4_ssc_mod_methods[] __initconst = {
[STM32F4_PLL_SSC_DOWN_SPREAD] = "down-spread",
[STM32F4_PLL_SSC_CENTER_SPREAD] = "center-spread",
};
/*
* This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx
* have gate bits associated with them. Its combined hweight is 71.
@ -509,6 +529,12 @@ static const struct clk_div_table pll_divr_table[] = {
{ 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, { 0 }
};
struct stm32f4_pll_ssc {
unsigned int mod_freq;
unsigned int mod_depth;
enum stm32f4_pll_ssc_mod_type mod_type;
};
struct stm32f4_pll {
spinlock_t *lock;
struct clk_gate gate;
@ -516,6 +542,8 @@ struct stm32f4_pll {
u8 bit_rdy_idx;
u8 status;
u8 n_start;
bool ssc_enable;
struct stm32f4_pll_ssc ssc_conf;
};
#define to_stm32f4_pll(_gate) container_of(_gate, struct stm32f4_pll, gate)
@ -538,6 +566,7 @@ struct stm32f4_vco_data {
u8 offset;
u8 bit_idx;
u8 bit_rdy_idx;
bool sscg;
};
static const struct stm32f4_vco_data vco_data[] = {
@ -632,9 +661,11 @@ static unsigned long stm32f4_pll_recalc(struct clk_hw *hw,
{
struct clk_gate *gate = to_clk_gate(hw);
struct stm32f4_pll *pll = to_stm32f4_pll(gate);
unsigned long val;
unsigned long n;
n = (readl(base + pll->offset) >> 6) & 0x1ff;
val = readl(base + pll->offset);
n = FIELD_GET(STM32F4_RCC_PLLCFGR_N_MASK, val);
return parent_rate * n;
}
@ -656,6 +687,32 @@ static long stm32f4_pll_round_rate(struct clk_hw *hw, unsigned long rate,
return *prate * n;
}
static void stm32f4_pll_set_ssc(struct clk_hw *hw, unsigned long parent_rate,
unsigned int ndiv)
{
struct clk_gate *gate = to_clk_gate(hw);
struct stm32f4_pll *pll = to_stm32f4_pll(gate);
struct stm32f4_pll_ssc *ssc = &pll->ssc_conf;
u32 modeper, incstep;
u32 sscgr;
sscgr = readl(base + STM32F4_RCC_SSCGR);
/* reserved field must be kept at reset value */
sscgr &= STM32F4_RCC_SSCGR_RESERVED_MASK;
modeper = DIV_ROUND_CLOSEST(parent_rate, 4 * ssc->mod_freq);
incstep = DIV_ROUND_CLOSEST(((1 << 15) - 1) * ssc->mod_depth * ndiv,
5 * 10000 * modeper);
sscgr |= STM32F4_RCC_SSCGR_SSCGEN |
FIELD_PREP(STM32F4_RCC_SSCGR_INCSTEP_MASK, incstep) |
FIELD_PREP(STM32F4_RCC_SSCGR_MODPER_MASK, modeper);
if (ssc->mod_type)
sscgr |= STM32F4_RCC_SSCGR_SPREADSEL;
writel(sscgr, base + STM32F4_RCC_SSCGR);
}
static int stm32f4_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
@ -673,9 +730,13 @@ static int stm32f4_pll_set_rate(struct clk_hw *hw, unsigned long rate,
n = rate / parent_rate;
val = readl(base + pll->offset) & ~(0x1ff << 6);
val = readl(base + pll->offset) & ~STM32F4_RCC_PLLCFGR_N_MASK;
val |= FIELD_PREP(STM32F4_RCC_PLLCFGR_N_MASK, n);
writel(val | ((n & 0x1ff) << 6), base + pll->offset);
writel(val, base + pll->offset);
if (pll->ssc_enable)
stm32f4_pll_set_ssc(hw, parent_rate, n);
if (pll_state)
stm32f4_pll_enable(hw);
@ -782,6 +843,84 @@ static struct clk_hw *clk_register_pll_div(const char *name,
return hw;
}
static int __init stm32f4_pll_init_ssc(struct clk_hw *hw,
const struct stm32f4_pll_ssc *conf)
{
struct clk_gate *gate = to_clk_gate(hw);
struct stm32f4_pll *pll = to_stm32f4_pll(gate);
struct clk_hw *parent;
unsigned long parent_rate;
int pll_state;
unsigned long n, val;
parent = clk_hw_get_parent(hw);
if (!parent) {
pr_err("%s: failed to get clock parent\n", __func__);
return -ENODEV;
}
parent_rate = clk_hw_get_rate(parent);
pll->ssc_enable = true;
memcpy(&pll->ssc_conf, conf, sizeof(pll->ssc_conf));
pll_state = stm32f4_pll_is_enabled(hw);
if (pll_state)
stm32f4_pll_disable(hw);
val = readl(base + pll->offset);
n = FIELD_GET(STM32F4_RCC_PLLCFGR_N_MASK, val);
pr_debug("%s: pll: %s, parent: %s, parent-rate: %lu, n: %lu\n",
__func__, clk_hw_get_name(hw), clk_hw_get_name(parent),
parent_rate, n);
stm32f4_pll_set_ssc(hw, parent_rate, n);
if (pll_state)
stm32f4_pll_enable(hw);
return 0;
}
static int __init stm32f4_pll_ssc_parse_dt(struct device_node *np,
struct stm32f4_pll_ssc *conf)
{
int ret;
const char *s;
if (!conf)
return -EINVAL;
ret = of_property_read_u32(np, "st,ssc-modfreq-hz", &conf->mod_freq);
if (ret)
return ret;
ret = of_property_read_u32(np, "st,ssc-moddepth-permyriad",
&conf->mod_depth);
if (ret) {
pr_err("%pOF: missing st,ssc-moddepth-permyriad\n", np);
return ret;
}
ret = fwnode_property_match_property_string(of_fwnode_handle(np),
"st,ssc-modmethod",
stm32f4_ssc_mod_methods,
ARRAY_SIZE(stm32f4_ssc_mod_methods));
if (ret < 0) {
pr_err("%pOF: failed to get st,ssc-modmethod\n", np);
return ret;
}
conf->mod_type = ret;
pr_debug("%pOF: SSCG settings: mod_freq: %d, mod_depth: %d mod_method: %s [%d]\n",
np, conf->mod_freq, conf->mod_depth, s, conf->mod_type);
return 0;
}
static struct clk_hw *stm32f4_rcc_register_pll(const char *pllsrc,
const struct stm32f4_pll_data *data, spinlock_t *lock)
{
@ -1689,7 +1828,8 @@ static void __init stm32f4_rcc_init(struct device_node *np)
const struct of_device_id *match;
const struct stm32f4_clk_data *data;
unsigned long pllm;
struct clk_hw *pll_src_hw;
struct clk_hw *pll_src_hw, *pll_vco_hw;
struct stm32f4_pll_ssc ssc_conf;
base = of_iomap(np, 0);
if (!base) {
@ -1748,8 +1888,8 @@ static void __init stm32f4_rcc_init(struct device_node *np)
clk_hw_register_fixed_factor(NULL, "vco_in", pll_src,
0, 1, pllm);
stm32f4_rcc_register_pll("vco_in", &data->pll_data[0],
&stm32f4_clk_lock);
pll_vco_hw = stm32f4_rcc_register_pll("vco_in", &data->pll_data[0],
&stm32f4_clk_lock);
clks[PLL_VCO_I2S] = stm32f4_rcc_register_pll("vco_in",
&data->pll_data[1], &stm32f4_clk_lock);
@ -1894,6 +2034,9 @@ static void __init stm32f4_rcc_init(struct device_node *np)
of_clk_add_hw_provider(np, stm32f4_rcc_lookup_clk, NULL);
if (!stm32f4_pll_ssc_parse_dt(np, &ssc_conf))
stm32f4_pll_init_ssc(pll_vco_hw, &ssc_conf);
return;
fail:
kfree(clks);

View File

@ -2530,7 +2530,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
rate = clk_core_req_round_rate_nolock(core, req_rate);
/* bail early if nothing to do */
if (rate == clk_core_get_rate_recalc(core))
if (rate == clk_core_get_rate_nolock(core))
return 0;
/* fail on a direct rate set of a protected provider */

View File

@ -106,7 +106,7 @@ config COMMON_CLK_AXG_AUDIO
select COMMON_CLK_MESON_SCLK_DIV
select COMMON_CLK_MESON_CLKC_UTILS
select REGMAP_MMIO
depends on RESET_MESON_AUX
select RESET_CONTROLLER
help
Support for the audio clock controller on AmLogic A113D devices,
aka axg, Say Y if you want audio subsystem to work.

View File

@ -15,8 +15,6 @@
#include <linux/reset-controller.h>
#include <linux/slab.h>
#include <soc/amlogic/reset-meson-aux.h>
#include "meson-clkc-utils.h"
#include "axg-audio.h"
#include "clk-regmap.h"
@ -1680,6 +1678,84 @@ static struct clk_regmap *const sm1_clk_regmaps[] = {
&sm1_earcrx_dmac_clk,
};
struct axg_audio_reset_data {
struct reset_controller_dev rstc;
struct regmap *map;
unsigned int offset;
};
static void axg_audio_reset_reg_and_bit(struct axg_audio_reset_data *rst,
unsigned long id,
unsigned int *reg,
unsigned int *bit)
{
unsigned int stride = regmap_get_reg_stride(rst->map);
*reg = (id / (stride * BITS_PER_BYTE)) * stride;
*reg += rst->offset;
*bit = id % (stride * BITS_PER_BYTE);
}
static int axg_audio_reset_update(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct axg_audio_reset_data *rst =
container_of(rcdev, struct axg_audio_reset_data, rstc);
unsigned int offset, bit;
axg_audio_reset_reg_and_bit(rst, id, &offset, &bit);
regmap_update_bits(rst->map, offset, BIT(bit),
assert ? BIT(bit) : 0);
return 0;
}
static int axg_audio_reset_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct axg_audio_reset_data *rst =
container_of(rcdev, struct axg_audio_reset_data, rstc);
unsigned int val, offset, bit;
axg_audio_reset_reg_and_bit(rst, id, &offset, &bit);
regmap_read(rst->map, offset, &val);
return !!(val & BIT(bit));
}
static int axg_audio_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return axg_audio_reset_update(rcdev, id, true);
}
static int axg_audio_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return axg_audio_reset_update(rcdev, id, false);
}
static int axg_audio_reset_toggle(struct reset_controller_dev *rcdev,
unsigned long id)
{
int ret;
ret = axg_audio_reset_assert(rcdev, id);
if (ret)
return ret;
return axg_audio_reset_deassert(rcdev, id);
}
static const struct reset_control_ops axg_audio_rstc_ops = {
.assert = axg_audio_reset_assert,
.deassert = axg_audio_reset_deassert,
.reset = axg_audio_reset_toggle,
.status = axg_audio_reset_status,
};
static struct regmap_config axg_audio_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
@ -1690,14 +1766,16 @@ struct audioclk_data {
struct clk_regmap *const *regmap_clks;
unsigned int regmap_clk_num;
struct meson_clk_hw_data hw_clks;
unsigned int reset_offset;
unsigned int reset_num;
unsigned int max_register;
const char *rst_drvname;
};
static int axg_audio_clkc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct audioclk_data *data;
struct axg_audio_reset_data *rst;
struct regmap *map;
void __iomem *regs;
struct clk_hw *hw;
@ -1756,11 +1834,22 @@ static int axg_audio_clkc_probe(struct platform_device *pdev)
if (ret)
return ret;
/* Register auxiliary reset driver when applicable */
if (data->rst_drvname)
ret = devm_meson_rst_aux_register(dev, map, data->rst_drvname);
/* Stop here if there is no reset */
if (!data->reset_num)
return 0;
return ret;
rst = devm_kzalloc(dev, sizeof(*rst), GFP_KERNEL);
if (!rst)
return -ENOMEM;
rst->map = map;
rst->offset = data->reset_offset;
rst->rstc.nr_resets = data->reset_num;
rst->rstc.ops = &axg_audio_rstc_ops;
rst->rstc.of_node = dev->of_node;
rst->rstc.owner = THIS_MODULE;
return devm_reset_controller_register(dev, &rst->rstc);
}
static const struct audioclk_data axg_audioclk_data = {
@ -1780,8 +1869,9 @@ static const struct audioclk_data g12a_audioclk_data = {
.hws = g12a_audio_hw_clks,
.num = ARRAY_SIZE(g12a_audio_hw_clks),
},
.reset_offset = AUDIO_SW_RESET,
.reset_num = 26,
.max_register = AUDIO_CLK_SPDIFOUT_B_CTRL,
.rst_drvname = "rst-g12a",
};
static const struct audioclk_data sm1_audioclk_data = {
@ -1791,8 +1881,9 @@ static const struct audioclk_data sm1_audioclk_data = {
.hws = sm1_audio_hw_clks,
.num = ARRAY_SIZE(sm1_audio_hw_clks),
},
.reset_offset = AUDIO_SM1_SW_RESET0,
.reset_num = 39,
.max_register = AUDIO_EARCRX_DMAC_CLK_CTRL,
.rst_drvname = "rst-sm1",
};
static const struct of_device_id clkc_match_table[] = {

View File

@ -13,6 +13,7 @@ clk-rockchip-y += clk-inverter.o
clk-rockchip-y += clk-mmc-phase.o
clk-rockchip-y += clk-muxgrf.o
clk-rockchip-y += clk-ddr.o
clk-rockchip-y += gate-link.o
clk-rockchip-$(CONFIG_RESET_CONTROLLER) += softrst.o
obj-$(CONFIG_CLK_PX30) += clk-px30.o

View File

@ -12,28 +12,6 @@
#include <dt-bindings/clock/rockchip,rk3588-cru.h>
#include "clk.h"
/*
* Recent Rockchip SoCs have a new hardware block called Native Interface
* Unit (NIU), which gates clocks to devices behind them. These effectively
* need two parent clocks.
*
* Downstream enables the linked clock via runtime PM whenever the gate is
* enabled. This implementation uses separate clock nodes for each of the
* linked gate clocks, which leaks parts of the clock tree into DT.
*
* The GATE_LINK macro instead takes the second parent via 'linkname', but
* ignores the information. Once the clock framework is ready to handle it, the
* information should be passed on here. But since these clocks are required to
* access multiple relevant IP blocks, such as PCIe or USB, we mark all linked
* clocks critical until a better solution is available. This will waste some
* power, but avoids leaking implementation details into DT or hanging the
* system.
*/
#define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf) \
GATE(_id, cname, pname, f, o, b, gf)
#define RK3588_LINKED_CLK CLK_IS_CRITICAL
#define RK3588_GRF_SOC_STATUS0 0x600
#define RK3588_PHYREF_ALT_GATE 0xc38
@ -266,6 +244,8 @@ static struct rockchip_pll_rate_table rk3588_pll_rates[] = {
}, \
}
static struct rockchip_clk_provider *early_ctx;
static struct rockchip_cpuclk_rate_table rk3588_cpub0clk_rates[] __initdata = {
RK3588_CPUB01CLK_RATE(2496000000, 1),
RK3588_CPUB01CLK_RATE(2400000000, 1),
@ -694,7 +674,7 @@ static struct rockchip_pll_clock rk3588_pll_clks[] __initdata = {
RK3588_MODE_CON0, 10, 15, 0, rk3588_pll_rates),
};
static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = {
/*
* CRU Clock-Architecture
*/
@ -792,10 +772,10 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
COMPOSITE(MCLK_GMAC0_OUT, "mclk_gmac0_out", gpll_cpll_p, 0,
RK3588_CLKSEL_CON(15), 7, 1, MFLAGS, 0, 7, DFLAGS,
RK3588_CLKGATE_CON(5), 3, GFLAGS),
COMPOSITE(REFCLKO25M_ETH0_OUT, "refclko25m_eth0_out", gpll_cpll_p, 0,
COMPOSITE(REFCLKO25M_ETH0_OUT, "refclko25m_eth0_out", gpll_cpll_p, CLK_IS_CRITICAL,
RK3588_CLKSEL_CON(15), 15, 1, MFLAGS, 8, 7, DFLAGS,
RK3588_CLKGATE_CON(5), 4, GFLAGS),
COMPOSITE(REFCLKO25M_ETH1_OUT, "refclko25m_eth1_out", gpll_cpll_p, 0,
COMPOSITE(REFCLKO25M_ETH1_OUT, "refclko25m_eth1_out", gpll_cpll_p, CLK_IS_CRITICAL,
RK3588_CLKSEL_CON(16), 7, 1, MFLAGS, 0, 7, DFLAGS,
RK3588_CLKGATE_CON(5), 5, GFLAGS),
COMPOSITE(CLK_CIFOUT_OUT, "clk_cifout_out", gpll_cpll_24m_spll_p, 0,
@ -1456,7 +1436,7 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
COMPOSITE_NODIV(HCLK_NVM_ROOT, "hclk_nvm_root", mux_200m_100m_50m_24m_p, 0,
RK3588_CLKSEL_CON(77), 0, 2, MFLAGS,
RK3588_CLKGATE_CON(31), 0, GFLAGS),
COMPOSITE(ACLK_NVM_ROOT, "aclk_nvm_root", gpll_cpll_p, RK3588_LINKED_CLK,
COMPOSITE(ACLK_NVM_ROOT, "aclk_nvm_root", gpll_cpll_p, 0,
RK3588_CLKSEL_CON(77), 7, 1, MFLAGS, 2, 5, DFLAGS,
RK3588_CLKGATE_CON(31), 1, GFLAGS),
GATE(ACLK_EMMC, "aclk_emmc", "aclk_nvm_root", 0,
@ -1685,13 +1665,13 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
RK3588_CLKGATE_CON(42), 9, GFLAGS),
/* vdpu */
COMPOSITE(ACLK_VDPU_ROOT, "aclk_vdpu_root", gpll_cpll_aupll_p, RK3588_LINKED_CLK,
COMPOSITE(ACLK_VDPU_ROOT, "aclk_vdpu_root", gpll_cpll_aupll_p, 0,
RK3588_CLKSEL_CON(98), 5, 2, MFLAGS, 0, 5, DFLAGS,
RK3588_CLKGATE_CON(44), 0, GFLAGS),
COMPOSITE_NODIV(ACLK_VDPU_LOW_ROOT, "aclk_vdpu_low_root", mux_400m_200m_100m_24m_p, 0,
RK3588_CLKSEL_CON(98), 7, 2, MFLAGS,
RK3588_CLKGATE_CON(44), 1, GFLAGS),
COMPOSITE_NODIV(HCLK_VDPU_ROOT, "hclk_vdpu_root", mux_200m_100m_50m_24m_p, RK3588_LINKED_CLK,
COMPOSITE_NODIV(HCLK_VDPU_ROOT, "hclk_vdpu_root", mux_200m_100m_50m_24m_p, 0,
RK3588_CLKSEL_CON(98), 9, 2, MFLAGS,
RK3588_CLKGATE_CON(44), 2, GFLAGS),
COMPOSITE(ACLK_JPEG_DECODER_ROOT, "aclk_jpeg_decoder_root", gpll_cpll_aupll_spll_p, 0,
@ -1742,9 +1722,9 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
COMPOSITE(ACLK_RKVENC0_ROOT, "aclk_rkvenc0_root", gpll_cpll_npll_p, 0,
RK3588_CLKSEL_CON(102), 7, 2, MFLAGS, 2, 5, DFLAGS,
RK3588_CLKGATE_CON(47), 1, GFLAGS),
GATE(HCLK_RKVENC0, "hclk_rkvenc0", "hclk_rkvenc0_root", RK3588_LINKED_CLK,
GATE(HCLK_RKVENC0, "hclk_rkvenc0", "hclk_rkvenc0_root", 0,
RK3588_CLKGATE_CON(47), 4, GFLAGS),
GATE(ACLK_RKVENC0, "aclk_rkvenc0", "aclk_rkvenc0_root", RK3588_LINKED_CLK,
GATE(ACLK_RKVENC0, "aclk_rkvenc0", "aclk_rkvenc0_root", 0,
RK3588_CLKGATE_CON(47), 5, GFLAGS),
COMPOSITE(CLK_RKVENC0_CORE, "clk_rkvenc0_core", gpll_cpll_aupll_npll_p, 0,
RK3588_CLKSEL_CON(102), 14, 2, MFLAGS, 9, 5, DFLAGS,
@ -1754,10 +1734,10 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
RK3588_CLKGATE_CON(48), 6, GFLAGS),
/* vi */
COMPOSITE(ACLK_VI_ROOT, "aclk_vi_root", gpll_cpll_npll_aupll_spll_p, RK3588_LINKED_CLK,
COMPOSITE(ACLK_VI_ROOT, "aclk_vi_root", gpll_cpll_npll_aupll_spll_p, 0,
RK3588_CLKSEL_CON(106), 5, 3, MFLAGS, 0, 5, DFLAGS,
RK3588_CLKGATE_CON(49), 0, GFLAGS),
COMPOSITE_NODIV(HCLK_VI_ROOT, "hclk_vi_root", mux_200m_100m_50m_24m_p, RK3588_LINKED_CLK,
COMPOSITE_NODIV(HCLK_VI_ROOT, "hclk_vi_root", mux_200m_100m_50m_24m_p, 0,
RK3588_CLKSEL_CON(106), 8, 2, MFLAGS,
RK3588_CLKGATE_CON(49), 1, GFLAGS),
COMPOSITE_NODIV(PCLK_VI_ROOT, "pclk_vi_root", mux_100m_50m_24m_p, 0,
@ -1927,10 +1907,10 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
COMPOSITE(ACLK_VOP_ROOT, "aclk_vop_root", gpll_cpll_dmyaupll_npll_spll_p, 0,
RK3588_CLKSEL_CON(110), 5, 3, MFLAGS, 0, 5, DFLAGS,
RK3588_CLKGATE_CON(52), 0, GFLAGS),
COMPOSITE_NODIV(ACLK_VOP_LOW_ROOT, "aclk_vop_low_root", mux_400m_200m_100m_24m_p, RK3588_LINKED_CLK,
COMPOSITE_NODIV(ACLK_VOP_LOW_ROOT, "aclk_vop_low_root", mux_400m_200m_100m_24m_p, 0,
RK3588_CLKSEL_CON(110), 8, 2, MFLAGS,
RK3588_CLKGATE_CON(52), 1, GFLAGS),
COMPOSITE_NODIV(HCLK_VOP_ROOT, "hclk_vop_root", mux_200m_100m_50m_24m_p, RK3588_LINKED_CLK,
COMPOSITE_NODIV(HCLK_VOP_ROOT, "hclk_vop_root", mux_200m_100m_50m_24m_p, 0,
RK3588_CLKSEL_CON(110), 10, 2, MFLAGS,
RK3588_CLKGATE_CON(52), 2, GFLAGS),
COMPOSITE_NODIV(PCLK_VOP_ROOT, "pclk_vop_root", mux_100m_50m_24m_p, 0,
@ -2428,10 +2408,12 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
RK3588_CLKGATE_CON(68), 5, GFLAGS),
GATE(ACLK_AV1, "aclk_av1", "aclk_av1_pre", 0,
RK3588_CLKGATE_CON(68), 2, GFLAGS),
};
static struct rockchip_clk_branch rk3588_clk_branches[] = {
GATE_LINK(ACLK_ISP1_PRE, "aclk_isp1_pre", "aclk_isp1_root", ACLK_VI_ROOT, 0, RK3588_CLKGATE_CON(26), 6, GFLAGS),
GATE_LINK(HCLK_ISP1_PRE, "hclk_isp1_pre", "hclk_isp1_root", HCLK_VI_ROOT, 0, RK3588_CLKGATE_CON(26), 8, GFLAGS),
GATE_LINK(HCLK_NVM, "hclk_nvm", "hclk_nvm_root", ACLK_NVM_ROOT, RK3588_LINKED_CLK, RK3588_CLKGATE_CON(31), 2, GFLAGS),
GATE_LINK(HCLK_NVM, "hclk_nvm", "hclk_nvm_root", ACLK_NVM_ROOT, 0, RK3588_CLKGATE_CON(31), 2, GFLAGS),
GATE_LINK(ACLK_USB, "aclk_usb", "aclk_usb_root", ACLK_VO1USB_TOP_ROOT, 0, RK3588_CLKGATE_CON(42), 2, GFLAGS),
GATE_LINK(HCLK_USB, "hclk_usb", "hclk_usb_root", HCLK_VO1USB_TOP_ROOT, 0, RK3588_CLKGATE_CON(42), 3, GFLAGS),
GATE_LINK(ACLK_JPEG_DECODER_PRE, "aclk_jpeg_decoder_pre", "aclk_jpeg_decoder_root", ACLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(44), 7, GFLAGS),
@ -2443,9 +2425,9 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
GATE_LINK(HCLK_RKVDEC1_PRE, "hclk_rkvdec1_pre", "hclk_rkvdec1_root", HCLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(41), 4, GFLAGS),
GATE_LINK(ACLK_RKVDEC1_PRE, "aclk_rkvdec1_pre", "aclk_rkvdec1_root", ACLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(41), 5, GFLAGS),
GATE_LINK(ACLK_HDCP0_PRE, "aclk_hdcp0_pre", "aclk_vo0_root", ACLK_VOP_LOW_ROOT, 0, RK3588_CLKGATE_CON(55), 9, GFLAGS),
GATE_LINK(HCLK_VO0, "hclk_vo0", "hclk_vo0_root", HCLK_VOP_ROOT, RK3588_LINKED_CLK, RK3588_CLKGATE_CON(55), 5, GFLAGS),
GATE_LINK(HCLK_VO0, "hclk_vo0", "hclk_vo0_root", HCLK_VOP_ROOT, 0, RK3588_CLKGATE_CON(55), 5, GFLAGS),
GATE_LINK(ACLK_HDCP1_PRE, "aclk_hdcp1_pre", "aclk_hdcp1_root", ACLK_VO1USB_TOP_ROOT, 0, RK3588_CLKGATE_CON(59), 6, GFLAGS),
GATE_LINK(HCLK_VO1, "hclk_vo1", "hclk_vo1_root", HCLK_VO1USB_TOP_ROOT, RK3588_LINKED_CLK, RK3588_CLKGATE_CON(59), 9, GFLAGS),
GATE_LINK(HCLK_VO1, "hclk_vo1", "hclk_vo1_root", HCLK_VO1USB_TOP_ROOT, 0, RK3588_CLKGATE_CON(59), 9, GFLAGS),
GATE_LINK(ACLK_AV1_PRE, "aclk_av1_pre", "aclk_av1_root", ACLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(68), 1, GFLAGS),
GATE_LINK(PCLK_AV1_PRE, "pclk_av1_pre", "pclk_av1_root", HCLK_VDPU_ROOT, 0, RK3588_CLKGATE_CON(68), 4, GFLAGS),
GATE_LINK(HCLK_SDIO_PRE, "hclk_sdio_pre", "hclk_sdio_root", HCLK_NVM, 0, RK3588_CLKGATE_CON(75), 1, GFLAGS),
@ -2453,26 +2435,31 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = {
GATE_LINK(PCLK_VO1GRF, "pclk_vo1grf", "pclk_vo1_root", HCLK_VO1, CLK_IGNORE_UNUSED, RK3588_CLKGATE_CON(59), 12, GFLAGS),
};
static void __init rk3588_clk_init(struct device_node *np)
static void __init rk3588_clk_early_init(struct device_node *np)
{
struct rockchip_clk_provider *ctx;
unsigned long clk_nr_clks;
unsigned long clk_nr_clks, max_clk_id1, max_clk_id2;
void __iomem *reg_base;
clk_nr_clks = rockchip_clk_find_max_clk_id(rk3588_clk_branches,
ARRAY_SIZE(rk3588_clk_branches)) + 1;
max_clk_id1 = rockchip_clk_find_max_clk_id(rk3588_clk_branches,
ARRAY_SIZE(rk3588_clk_branches));
max_clk_id2 = rockchip_clk_find_max_clk_id(rk3588_early_clk_branches,
ARRAY_SIZE(rk3588_early_clk_branches));
clk_nr_clks = max(max_clk_id1, max_clk_id2) + 1;
reg_base = of_iomap(np, 0);
if (!reg_base) {
pr_err("%s: could not map cru region\n", __func__);
return;
}
ctx = rockchip_clk_init(np, reg_base, clk_nr_clks);
ctx = rockchip_clk_init_early(np, reg_base, clk_nr_clks);
if (IS_ERR(ctx)) {
pr_err("%s: rockchip clk init failed\n", __func__);
iounmap(reg_base);
return;
}
early_ctx = ctx;
rockchip_clk_register_plls(ctx, rk3588_pll_clks,
ARRAY_SIZE(rk3588_pll_clks),
@ -2491,14 +2478,55 @@ static void __init rk3588_clk_init(struct device_node *np)
&rk3588_cpub1clk_data, rk3588_cpub1clk_rates,
ARRAY_SIZE(rk3588_cpub1clk_rates));
rockchip_clk_register_branches(ctx, rk3588_clk_branches,
ARRAY_SIZE(rk3588_clk_branches));
rk3588_rst_init(np, reg_base);
rockchip_register_restart_notifier(ctx, RK3588_GLB_SRST_FST, NULL);
rockchip_clk_register_branches(ctx, rk3588_early_clk_branches,
ARRAY_SIZE(rk3588_early_clk_branches));
rockchip_clk_of_add_provider(np, ctx);
}
CLK_OF_DECLARE_DRIVER(rk3588_cru, "rockchip,rk3588-cru", rk3588_clk_early_init);
CLK_OF_DECLARE(rk3588_cru, "rockchip,rk3588-cru", rk3588_clk_init);
static int clk_rk3588_probe(struct platform_device *pdev)
{
struct rockchip_clk_provider *ctx = early_ctx;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
rockchip_clk_register_late_branches(dev, ctx, rk3588_clk_branches,
ARRAY_SIZE(rk3588_clk_branches));
rockchip_clk_finalize(ctx);
rk3588_rst_init(np, ctx->reg_base);
rockchip_register_restart_notifier(ctx, RK3588_GLB_SRST_FST, NULL);
/*
* Re-add clock provider, so that the newly added clocks are also
* re-parented and get their defaults configured.
*/
of_clk_del_provider(np);
rockchip_clk_of_add_provider(np, ctx);
return 0;
}
static const struct of_device_id clk_rk3588_match_table[] = {
{
.compatible = "rockchip,rk3588-cru",
},
{ }
};
static struct platform_driver clk_rk3588_driver = {
.probe = clk_rk3588_probe,
.driver = {
.name = "clk-rk3588",
.of_match_table = clk_rk3588_match_table,
.suppress_bind_attrs = true,
},
};
static int __init rockchip_clk_rk3588_drv_register(void)
{
return platform_driver_register(&clk_rk3588_driver);
}
core_initcall(rockchip_clk_rk3588_drv_register);

View File

@ -19,6 +19,7 @@
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reboot.h>
@ -197,12 +198,6 @@ static void rockchip_fractional_approximation(struct clk_hw *hw,
clk_fractional_divider_general_approximation(hw, rate, parent_rate, m, n);
}
static void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
struct clk *clk, unsigned int id)
{
ctx->clk_data.clks[id] = clk;
}
static struct clk *rockchip_clk_register_frac_branch(
struct rockchip_clk_provider *ctx, const char *name,
const char *const *parent_names, u8 num_parents,
@ -292,7 +287,7 @@ static struct clk *rockchip_clk_register_frac_branch(
return mux_clk;
}
rockchip_clk_add_lookup(ctx, mux_clk, child->id);
rockchip_clk_set_lookup(ctx, mux_clk, child->id);
/* notifier on the fraction divider to catch rate changes */
if (frac->mux_frac_idx >= 0) {
@ -359,14 +354,17 @@ static struct clk *rockchip_clk_register_factor_branch(const char *name,
return hw->clk;
}
struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
void __iomem *base,
unsigned long nr_clks)
static struct rockchip_clk_provider *rockchip_clk_init_base(
struct device_node *np, void __iomem *base,
unsigned long nr_clks, bool has_late_clocks)
{
struct rockchip_clk_provider *ctx;
struct clk **clk_table;
struct clk *default_clk_val;
int i;
default_clk_val = ERR_PTR(has_late_clocks ? -EPROBE_DEFER : -ENOENT);
ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
if (!ctx)
return ERR_PTR(-ENOMEM);
@ -376,7 +374,7 @@ struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
goto err_free;
for (i = 0; i < nr_clks; ++i)
clk_table[i] = ERR_PTR(-ENOENT);
clk_table[i] = default_clk_val;
ctx->reg_base = base;
ctx->clk_data.clks = clk_table;
@ -393,8 +391,33 @@ err_free:
kfree(ctx);
return ERR_PTR(-ENOMEM);
}
struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
void __iomem *base,
unsigned long nr_clks)
{
return rockchip_clk_init_base(np, base, nr_clks, false);
}
EXPORT_SYMBOL_GPL(rockchip_clk_init);
struct rockchip_clk_provider *rockchip_clk_init_early(struct device_node *np,
void __iomem *base,
unsigned long nr_clks)
{
return rockchip_clk_init_base(np, base, nr_clks, true);
}
EXPORT_SYMBOL_GPL(rockchip_clk_init_early);
void rockchip_clk_finalize(struct rockchip_clk_provider *ctx)
{
int i;
for (i = 0; i < ctx->clk_data.clk_num; ++i)
if (ctx->clk_data.clks[i] == ERR_PTR(-EPROBE_DEFER))
ctx->clk_data.clks[i] = ERR_PTR(-ENOENT);
}
EXPORT_SYMBOL_GPL(rockchip_clk_finalize);
void rockchip_clk_of_add_provider(struct device_node *np,
struct rockchip_clk_provider *ctx)
{
@ -424,7 +447,7 @@ void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
continue;
}
rockchip_clk_add_lookup(ctx, clk, list->id);
rockchip_clk_set_lookup(ctx, clk, list->id);
}
}
EXPORT_SYMBOL_GPL(rockchip_clk_register_plls);
@ -446,6 +469,29 @@ unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
}
EXPORT_SYMBOL_GPL(rockchip_clk_find_max_clk_id);
static struct platform_device *rockchip_clk_register_gate_link(
struct device *parent_dev,
struct rockchip_clk_provider *ctx,
struct rockchip_clk_branch *clkbr)
{
struct rockchip_gate_link_platdata gate_link_pdata = {
.ctx = ctx,
.clkbr = clkbr,
};
struct platform_device_info pdevinfo = {
.parent = parent_dev,
.name = "rockchip-gate-link-clk",
.id = clkbr->id,
.fwnode = dev_fwnode(parent_dev),
.of_node_reused = true,
.data = &gate_link_pdata,
.size_data = sizeof(gate_link_pdata),
};
return platform_device_register_full(&pdevinfo);
}
void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
struct rockchip_clk_branch *list,
unsigned int nr_clk)
@ -571,6 +617,9 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
list->div_width, list->div_flags,
ctx->reg_base, &ctx->lock);
break;
case branch_linked_gate:
/* must be registered late, fall-through for error message */
break;
}
/* none of the cases above matched */
@ -586,11 +635,36 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
continue;
}
rockchip_clk_add_lookup(ctx, clk, list->id);
rockchip_clk_set_lookup(ctx, clk, list->id);
}
}
EXPORT_SYMBOL_GPL(rockchip_clk_register_branches);
void rockchip_clk_register_late_branches(struct device *dev,
struct rockchip_clk_provider *ctx,
struct rockchip_clk_branch *list,
unsigned int nr_clk)
{
unsigned int idx;
for (idx = 0; idx < nr_clk; idx++, list++) {
struct platform_device *pdev = NULL;
switch (list->branch_type) {
case branch_linked_gate:
pdev = rockchip_clk_register_gate_link(dev, ctx, list);
break;
default:
dev_err(dev, "unknown clock type %d\n", list->branch_type);
break;
}
if (!pdev)
dev_err(dev, "failed to register device for clock %s\n", list->name);
}
}
EXPORT_SYMBOL_GPL(rockchip_clk_register_late_branches);
void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
unsigned int lookup_id,
const char *name, const char *const *parent_names,
@ -610,7 +684,7 @@ void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
return;
}
rockchip_clk_add_lookup(ctx, clk, lookup_id);
rockchip_clk_set_lookup(ctx, clk, lookup_id);
}
EXPORT_SYMBOL_GPL(rockchip_clk_register_armclk);

View File

@ -570,6 +570,7 @@ enum rockchip_clk_branch_type {
branch_divider,
branch_fraction_divider,
branch_gate,
branch_linked_gate,
branch_mmc,
branch_inverter,
branch_factor,
@ -597,6 +598,7 @@ struct rockchip_clk_branch {
int gate_offset;
u8 gate_shift;
u8 gate_flags;
unsigned int linked_clk_id;
struct rockchip_clk_branch *child;
};
@ -895,6 +897,20 @@ struct rockchip_clk_branch {
.gate_flags = gf, \
}
#define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf) \
{ \
.id = _id, \
.branch_type = branch_linked_gate, \
.name = cname, \
.parent_names = (const char *[]){ pname }, \
.linked_clk_id = linkedclk, \
.num_parents = 1, \
.flags = f, \
.gate_offset = o, \
.gate_shift = b, \
.gate_flags = gf, \
}
#define MMC(_id, cname, pname, offset, shift) \
{ \
.id = _id, \
@ -1022,8 +1038,28 @@ struct rockchip_clk_branch {
#define SGRF_GATE(_id, cname, pname) \
FACTOR(_id, cname, pname, 0, 1, 1)
static inline struct clk *rockchip_clk_get_lookup(struct rockchip_clk_provider *ctx,
unsigned int id)
{
return ctx->clk_data.clks[id];
}
static inline void rockchip_clk_set_lookup(struct rockchip_clk_provider *ctx,
struct clk *clk, unsigned int id)
{
ctx->clk_data.clks[id] = clk;
}
struct rockchip_gate_link_platdata {
struct rockchip_clk_provider *ctx;
struct rockchip_clk_branch *clkbr;
};
struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
void __iomem *base, unsigned long nr_clks);
struct rockchip_clk_provider *rockchip_clk_init_early(struct device_node *np,
void __iomem *base, unsigned long nr_clks);
void rockchip_clk_finalize(struct rockchip_clk_provider *ctx);
void rockchip_clk_of_add_provider(struct device_node *np,
struct rockchip_clk_provider *ctx);
unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
@ -1031,6 +1067,10 @@ unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list,
void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
struct rockchip_clk_branch *list,
unsigned int nr_clk);
void rockchip_clk_register_late_branches(struct device *dev,
struct rockchip_clk_provider *ctx,
struct rockchip_clk_branch *list,
unsigned int nr_clk);
void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
struct rockchip_pll_clock *pll_list,
unsigned int nr_pll, int grf_lock_offset);

View File

@ -0,0 +1,85 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2024 Collabora Ltd.
* Author: Sebastian Reichel <sebastian.reichel@collabora.com>
*/
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/pm_clock.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include "clk.h"
static int rk_clk_gate_link_register(struct device *dev,
struct rockchip_clk_provider *ctx,
struct rockchip_clk_branch *clkbr)
{
unsigned long flags = clkbr->flags | CLK_SET_RATE_PARENT;
struct clk *clk;
clk = clk_register_gate(dev, clkbr->name, clkbr->parent_names[0],
flags, ctx->reg_base + clkbr->gate_offset,
clkbr->gate_shift, clkbr->gate_flags,
&ctx->lock);
if (IS_ERR(clk))
return PTR_ERR(clk);
rockchip_clk_set_lookup(ctx, clk, clkbr->id);
return 0;
}
static int rk_clk_gate_link_probe(struct platform_device *pdev)
{
struct rockchip_gate_link_platdata *pdata;
struct device *dev = &pdev->dev;
struct clk *linked_clk;
int ret;
pdata = dev_get_platdata(dev);
if (!pdata)
return dev_err_probe(dev, -ENODEV, "missing platform data");
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
ret = devm_pm_clk_create(dev);
if (ret)
return ret;
linked_clk = rockchip_clk_get_lookup(pdata->ctx, pdata->clkbr->linked_clk_id);
ret = pm_clk_add_clk(dev, linked_clk);
if (ret)
return ret;
ret = rk_clk_gate_link_register(dev, pdata->ctx, pdata->clkbr);
if (ret)
goto err;
return 0;
err:
pm_clk_remove_clk(dev, linked_clk);
return ret;
}
static const struct dev_pm_ops rk_clk_gate_link_pm_ops = {
SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
};
static struct platform_driver rk_clk_gate_link_driver = {
.probe = rk_clk_gate_link_probe,
.driver = {
.name = "rockchip-gate-link-clk",
.pm = &rk_clk_gate_link_pm_ops,
.suppress_bind_attrs = true,
},
};
static int __init rk_clk_gate_link_drv_register(void)
{
return platform_driver_register(&rk_clk_gate_link_driver);
}
core_initcall(rk_clk_gate_link_drv_register);

View File

@ -657,7 +657,7 @@ static struct ccu_div apb_pclk = {
.hw.init = CLK_HW_INIT_PARENTS_DATA("apb-pclk",
apb_parents,
&ccu_div_ops,
0),
CLK_IGNORE_UNUSED),
},
};
@ -787,13 +787,13 @@ static CCU_GATE(CLK_X2X_CPUSYS, x2x_cpusys_clk, "x2x-cpusys", axi4_cpusys2_aclk_
0x134, BIT(7), 0);
static CCU_GATE(CLK_CPU2AON_X2H, cpu2aon_x2h_clk, "cpu2aon-x2h", axi_aclk_pd, 0x138, BIT(8), 0);
static CCU_GATE(CLK_CPU2PERI_X2H, cpu2peri_x2h_clk, "cpu2peri-x2h", axi4_cpusys2_aclk_pd,
0x140, BIT(9), 0);
0x140, BIT(9), CLK_IGNORE_UNUSED);
static CCU_GATE(CLK_PERISYS_APB1_HCLK, perisys_apb1_hclk, "perisys-apb1-hclk", perisys_ahb_hclk_pd,
0x150, BIT(9), 0);
static CCU_GATE(CLK_PERISYS_APB2_HCLK, perisys_apb2_hclk, "perisys-apb2-hclk", perisys_ahb_hclk_pd,
0x150, BIT(10), 0);
0x150, BIT(10), CLK_IGNORE_UNUSED);
static CCU_GATE(CLK_PERISYS_APB3_HCLK, perisys_apb3_hclk, "perisys-apb3-hclk", perisys_ahb_hclk_pd,
0x150, BIT(11), 0);
0x150, BIT(11), CLK_IGNORE_UNUSED);
static CCU_GATE(CLK_PERISYS_APB4_HCLK, perisys_apb4_hclk, "perisys-apb4-hclk", perisys_ahb_hclk_pd,
0x150, BIT(12), 0);
static CCU_GATE(CLK_NPU_AXI, npu_axi_clk, "npu-axi", axi_aclk_pd, 0x1c8, BIT(5), 0);
@ -889,7 +889,6 @@ static struct ccu_common *th1520_div_clks[] = {
&vo_axi_clk.common,
&vp_apb_clk.common,
&vp_axi_clk.common,
&cpu2vp_clk.common,
&venc_clk.common,
&dpu0_clk.common,
&dpu1_clk.common,
@ -909,6 +908,7 @@ static struct ccu_common *th1520_gate_clks[] = {
&bmu_clk.common,
&cpu2aon_x2h_clk.common,
&cpu2peri_x2h_clk.common,
&cpu2vp_clk.common,
&perisys_apb1_hclk.common,
&perisys_apb2_hclk.common,
&perisys_apb3_hclk.common,
@ -1041,7 +1041,8 @@ static int th1520_clk_probe(struct platform_device *pdev)
hw = devm_clk_hw_register_gate_parent_data(dev,
cg->common.hw.init->name,
cg->common.hw.init->parent_data,
0, base + cg->common.cfg0,
cg->common.hw.init->flags,
base + cg->common.cfg0,
ffs(cg->enable) - 1, 0, NULL);
if (IS_ERR(hw))
return PTR_ERR(hw);

View File

@ -12,6 +12,6 @@
#define EN7523_CLK_CRYPTO 6
#define EN7523_CLK_PCIE 7
#define EN7523_NUM_CLOCKS 8
#define EN7581_CLK_EMMC 8
#endif /* _DT_BINDINGS_CLOCK_AIROHA_EN7523_H_ */

View File

@ -10,7 +10,7 @@
* List of clocks which are not derived from system clock (SYSCLOCK)
*
* The index of these clocks is the secondary index of DT bindings
* (see Documentation/devicetree/bindings/clock/st,stm32-rcc.txt)
* (see Documentation/devicetree/bindings/clock/st,stm32-rcc.yaml)
*
* e.g:
<assigned-clocks = <&rcc 1 CLK_LSE>;

View File

@ -152,6 +152,7 @@ enum rpi_firmware_clk_id {
RPI_FIRMWARE_M2MC_CLK_ID,
RPI_FIRMWARE_PIXEL_BVB_CLK_ID,
RPI_FIRMWARE_VEC_CLK_ID,
RPI_FIRMWARE_DISP_CLK_ID,
RPI_FIRMWARE_NUM_CLK_ID,
};