/* SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause */ /* * Copyright (C) STMicroelectronics 2022 - All Rights Reserved * Author: Gabriel Fernandez for STMicroelectronics. */ struct stm32_clock_match_data; /** * struct stm32_mux_cfg - multiplexer configuration * * @parent_names: array of string names for all possible parents * @num_parents: number of possible parents * @reg_off: register controlling multiplexer * @shift: shift to multiplexer bit field * @width: width of the multiplexer bit field * @mux_flags: hardware-specific flags * @table: array of register values corresponding to the parent * index */ struct stm32_mux_cfg { const char * const *parent_names; u8 num_parents; u32 reg_off; u8 shift; u8 width; u8 mux_flags; u32 *table; }; /** * struct stm32_gate_cfg - gating configuration * * @reg_off: register controlling gate * @bit_idx: single bit controlling gate * @gate_flags: hardware-specific flags * @set_clr: 0 : normal gate, 1 : has a register to clear the gate */ struct stm32_gate_cfg { u32 reg_off; u8 bit_idx; u8 gate_flags; u8 set_clr; }; /** * struct stm32_div_cfg - divider configuration * * @reg_off: register containing the divider * @shift: shift to the divider bit field * @width: width of the divider bit field * @table: array of value/divider pairs, last entry should have div = 0 */ struct stm32_div_cfg { u32 reg_off; u8 shift; u8 width; u8 div_flags; const struct clk_div_table *table; }; #define NO_STM32_MUX -1 #define NO_STM32_DIV -1 #define NO_STM32_GATE -1 /** * struct stm32_composite_cfg - composite configuration * * @mux: index of a multiplexer * @gate: index of a gate * @div: index of a divider */ struct stm32_composite_cfg { int mux; int gate; int div; }; /** * struct clock_config - clock configuration * * @id: binding id of the clock * @name: clock name * @parent_name: name of the clock parent * @flags: framework-specific flags * @sec_id: secure id (use to known if the clock is secured or not) * @clock_cfg: specific clock data configuration * @setup: specific call back to reister the clock (will use * clock_cfg data as input) */ struct clock_config { unsigned long id; const char *name; const char *parent_name; unsigned long flags; int sec_id; void *clock_cfg; struct clk *(*setup)(struct udevice *dev, const struct clock_config *cfg); }; /** * struct clk_stm32_clock_data - clock data * * @num_gates: number of defined gates * @gates: array of gate configuration * @muxes: array of multiplexer configuration * @dividers: array of divider configuration */ struct clk_stm32_clock_data { unsigned int num_gates; const struct stm32_gate_cfg *gates; const struct stm32_mux_cfg *muxes; const struct stm32_div_cfg *dividers; }; /** * struct stm32_clock_match_data - clock match data * * @num_gates: number of clocks * @tab_clocks: array of clock configuration * @clock_data: definition of all gates / dividers / multiplexers * @check_security: call back to check if clock is secured or not */ struct stm32_clock_match_data { unsigned int num_clocks; const struct clock_config *tab_clocks; const struct clk_stm32_clock_data *clock_data; int (*check_security)(void __iomem *base, const struct clock_config *cfg); }; /** * struct stm32mp_rcc_priv - private struct for stm32mp clocks * * @base: base register of RCC driver * @gate_cpt: array of refcounting for gate with more than one * clocks as input. See explanation of Peripheral clock enabling * below. * @data: data for gate / divider / multiplexer configuration */ struct stm32mp_rcc_priv { void __iomem *base; u8 *gate_cpt; const struct clk_stm32_clock_data *data; }; int stm32_rcc_init(struct udevice *dev, const struct stm32_clock_match_data *data); /** * STM32 Gate * * PCE (Peripheral Clock Enabling) Peripheral * * ------------------------------ ---------- * | | | | * | | | PERx | * bus_ck | ----- | | | * ------------->|------------------| | | ckg_bus_perx | | * | | AND |-----|---------------->| | * | -----------| | | | | * | | ----- | | | * | | | | | * | ----- | | | * Perx_EN |-----|---| GCL | Gating | | | * | ----- Control | | | * | | Logic | | | * | | | | | * | | ----- | | | * | -----------| | | ckg_ker_perx | | * perx_ker_ck | | AND |-----|---------------->| | * ------------->|------------------| | | | | * | ----- | | | * | | | | * | | | | * ------------------------------ ---------- * Each peripheral requires a bus interface clock, named ckg_bus_perx * (for peripheral ‘x’). * Some peripherals (SAI, UART...) need also a dedicated clock for their * communication interface, this clock is generally asynchronous with respect to * the bus interface clock, and is named kernel clock (ckg_ker_perx). * Both clocks can be gated by one Perx_EN enable bit. * Then we have to manage a refcounting on gate level to avoid gate if one * the bus or the Kernel was enable. * * Example: * 1) enable the bus clock  * --> bus_clk ref_counting = 1, gate_ref_count = 1  * 2) enable the kernel clock  * --> perx_ker_ck ref_counting = 1, gate_ref_count = 2  * 3) disable kernel clock  *  ---> perx_ker_ck ref_counting = 0, gate_ref_count = 1  *  ==> then i will not gate because gate_ref_count > 0  * 4) disable bus clock  * --> bus_clk ref_counting = 0, gate_ref_count = 0  * ==> then i can gate (write in the register) because * gate_ref_count = 0 */ struct clk_stm32_gate { struct clk clk; struct stm32mp_rcc_priv *priv; int gate_id; }; #define to_clk_stm32_gate(_clk) container_of(_clk, struct clk_stm32_gate, clk) struct clk * clk_stm32_gate_register(struct udevice *dev, const struct clock_config *cfg); struct clk * clk_stm32_register_composite(struct udevice *dev, const struct clock_config *cfg); struct stm32_clk_gate_cfg { int gate_id; }; #define STM32_GATE(_id, _name, _parent, _flags, _gate_id, _sec_id) \ { \ .id = _id, \ .sec_id = _sec_id, \ .name = _name, \ .parent_name = _parent, \ .flags = _flags, \ .clock_cfg = &(struct stm32_clk_gate_cfg) { \ .gate_id = _gate_id, \ }, \ .setup = clk_stm32_gate_register, \ } struct stm32_clk_composite_cfg { int gate_id; int mux_id; int div_id; }; #define STM32_COMPOSITE(_id, _name, _flags, _sec_id, \ _gate_id, _mux_id, _div_id) \ { \ .id = _id, \ .name = _name, \ .sec_id = _sec_id, \ .flags = _flags, \ .clock_cfg = &(struct stm32_clk_composite_cfg) { \ .gate_id = _gate_id, \ .mux_id = _mux_id, \ .div_id = _div_id, \ }, \ .setup = clk_stm32_register_composite, \ } #define STM32_COMPOSITE_NOMUX(_id, _name, _parent, _flags, _sec_id, \ _gate_id, _div_id) \ { \ .id = _id, \ .name = _name, \ .parent_name = _parent, \ .sec_id = _sec_id, \ .flags = _flags, \ .clock_cfg = &(struct stm32_clk_composite_cfg) { \ .gate_id = _gate_id, \ .mux_id = NO_STM32_MUX, \ .div_id = _div_id, \ }, \ .setup = clk_stm32_register_composite, \ } extern const struct clk_ops stm32_clk_ops; ulong clk_stm32_get_rate_by_name(const char *name);