Message ID | 1489569810-24350-1-git-send-email-gabriel.fernandez@st.com (mailing list archive) |
---|---|
State | Superseded, archived |
Delegated to: | Stephen Boyd |
Headers | show |
On Wed, 15 Mar 2017, gabriel.fernandez@st.com wrote: > From: Gabriel Fernandez <gabriel.fernandez@st.com> > > This patch enables clocks for STM32H743 boards. > > Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com> > --- > .../devicetree/bindings/clock/st,stm32h7-rcc.txt | 152 ++ > drivers/clk/Makefile | 1 + > drivers/clk/clk-stm32h7.c | 1586 ++++++++++++++++++++ > include/dt-bindings/clock/stm32h7-clks.h | 165 ++ > include/dt-bindings/mfd/stm32h7-rcc.h | 138 ++ Just for the MFD changes: Acked-by: Lee Jones <lee.jones@linaro.org> > 5 files changed, 2042 insertions(+) > create mode 100644 Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt > create mode 100644 drivers/clk/clk-stm32h7.c > create mode 100644 include/dt-bindings/clock/stm32h7-clks.h > create mode 100644 include/dt-bindings/mfd/stm32h7-rcc.h > > diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt > new file mode 100644 > index 0000000..9d4b587 > --- /dev/null > +++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt > @@ -0,0 +1,152 @@ > +STMicroelectronics STM32H7 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,stm32h743-rcc" > + > +- reg: should be register base and length as documented in the > + datasheet > + > +- #reset-cells: 1, see below > + > +- #clock-cells : from common clock binding; shall be set to 1 > + > +- clocks: External oscillator clock phandle > + - high speed external clock signal (HSE) > + - low speed external clock signal (LSE) > + - external I2S clock (I2S_CKIN) > + > +- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain > + write protection (RTC clock). > + > +- pll x node: Allow to register a pll with specific parameters. > + Please see PLL section below. > + > +Example: > + > + rcc: rcc@58024400 { > + #reset-cells = <1>; > + #clock-cells = <2> > + compatible = "st,stm32h743-rcc", "st,stm32-rcc"; > + reg = <0x58024400 0x400>; > + clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>; > + > + st,syscfg = <&pwrcfg>; > + > + #address-cells = <1>; > + #size-cells = <0>; > + > + vco1@58024430 { > + #clock-cells = <0>; > + compatible = "stm32,pll"; > + reg = <0>; > + }; > + > + vco2@58024438 { > + #clock-cells = <0>; > + compatible = "stm32,pll"; > + reg = <1>; > + st,clock-div = <2>; > + st,clock-mult = <40>; > + st,frac-status = <0>; > + st,frac = <0>; > + st,vcosel = <1>; > + st,pllrge = <2>; > + }; > + }; > + > + > +STM32H7 PLL > +----------- > + > +The VCO of STM32 PLL could be reprensented like this: > + > + Vref --------- -------- > + ---->| / DIVM |---->| x DIVN | ------> VCO > + --------- -------- > + ^ > + | > + ------- > + | FRACN | > + ------- > + > +When the PLL is configured in integer mode: > +- VCO = ( Vref / DIVM ) * DIVN > + > +When the PLL is configured in fractional mode: > +- VCO = ( Vref / DIVM ) * ( DIVN + FRACN / 2^13) > + > + > +Required properties for pll node: > +- compatible: Should be: > + "stm32,pll" > + > +- #clock-cells: from common clock binding; shall be set to 0 > +- reg: Should be the pll number. > + > +Optional properties: > +- st,clock-div: DIVM division factor : <1..63> > +- st,clock-mult: DIVN multiplication factor : <4..512> > + > +- st,frac-status: > + - 0 Pll is configured in integer mode > + - 1 Pll is configure in fractional mode > + > +- st,frac: Fractional part of the multiplication factor : <0..8191> > + > +- st,vcosel: VCO selection > + - 0: Wide VCO range:192 to 836 MHz > + - 1: Medium VCO range:150 to 420 MHz > + > +- st,pllrge: PLL input frequency range > + - 0: The PLL input (Vref / DIVM) clock range frequency is between 1 and 2 MHz > + - 1: The PLL input (Vref / DIVM) clock range frequency is between 2 and 4 MHz > + - 2: The PLL input (Vref / DIVM) clock range frequency is between 4 and 8 MHz > + - 3: The PLL input (Vref / DIVM) clock range frequency is between 8 and 16 MHz > + > + > +The peripheral clock consumer should specify the desired clock by > +having the clock ID in its "clocks" phandle cell. > + > +All available clocks are defined as preprocessor macros in > +dt-bindings/clock/stm32h7-clks.h header and can be used in device > +tree sources. > + > +Example: > + > + timer5: timer@40000c00 { > + compatible = "st,stm32-timer"; > + reg = <0x40000c00 0x400>; > + interrupts = <50>; > + clocks = <&rcc TIM5_CK>; > + > + }; > + > +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 = AHB4RSTR_offset / 4 * 32 + CRCRST_bit_offset = 0x88 / 4 * 32 + 19 = 1107 > + > +All available preprocessor macros for reset are defined dt-bindings//mfd/stm32h7-rcc.h > +header and can be used in device tree sources. > + > +example: > + > + timer2 { > + resets = <&rcc STM32H7_APB1L_RESET(TIM2)>; > + }; > diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile > index 92c12b8..734aa02 100644 > --- a/drivers/clk/Makefile > +++ b/drivers/clk/Makefile > @@ -42,6 +42,7 @@ obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o > obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o > obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o > obj-$(CONFIG_ARCH_STM32) += clk-stm32f4.o > +obj-$(CONFIG_ARCH_STM32) += clk-stm32h7.o > obj-$(CONFIG_ARCH_TANGO) += clk-tango4.o > obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o > obj-$(CONFIG_ARCH_U300) += clk-u300.o > diff --git a/drivers/clk/clk-stm32h7.c b/drivers/clk/clk-stm32h7.c > new file mode 100644 > index 0000000..c8eb729 > --- /dev/null > +++ b/drivers/clk/clk-stm32h7.c > @@ -0,0 +1,1586 @@ > +/* > + * Copyright (C) Gabriel Fernandez 2017 > + * Author: Gabriel Fernandez <gabriel.fernandez@st.com> > + * > + * License terms: GPL V2.0. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/clk.h> > +#include <linux/clk-provider.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/mfd/syscon.h> > +#include <linux/of.h> > +#include <linux/of_address.h> > +#include <linux/slab.h> > +#include <linux/spinlock.h> > +#include <linux/regmap.h> > + > +#include <dt-bindings/clock/stm32h7-clks.h> > + > +/* Reset Clock Control Registers */ > +#define RCC_CR 0x00 > +#define RCC_CFGR 0x10 > +#define RCC_D1CFGR 0x18 > +#define RCC_D2CFGR 0x1C > +#define RCC_D3CFGR 0x20 > +#define RCC_PLLCKSELR 0x28 > +#define RCC_PLLCFGR 0x2C > +#define RCC_PLL1DIVR 0x30 > +#define RCC_PLL1FRACR 0x34 > +#define RCC_PLL2DIVR 0x38 > +#define RCC_PLL2FRACR 0x3C > +#define RCC_PLL3DIVR 0x40 > +#define RCC_PLL3FRACR 0x44 > +#define RCC_D1CCIPR 0x4C > +#define RCC_D2CCIP1R 0x50 > +#define RCC_D2CCIP2R 0x54 > +#define RCC_D3CCIPR 0x58 > +#define RCC_BDCR 0x70 > +#define RCC_CSR 0x74 > +#define RCC_AHB3ENR 0xD4 > +#define RCC_AHB1ENR 0xD8 > +#define RCC_AHB2ENR 0xDC > +#define RCC_AHB4ENR 0xE0 > +#define RCC_APB3ENR 0xE4 > +#define RCC_APB1LENR 0xE8 > +#define RCC_APB1HENR 0xEC > +#define RCC_APB2ENR 0xF0 > +#define RCC_APB4ENR 0xF4 > + > +static DEFINE_SPINLOCK(rlock); > + > +static void __iomem *base; > +static struct regmap *pdrm; > +static struct clk_hw **hws; > + > +/* System clock parent */ > +static const char * const sys_src[] = { > + "hsi_ck", "csi_ck", "hse_ck", "pll1_p" }; > + > +static const char * const tracein_src[] = { > + "hsi_ck", "csi_ck", "hse_ck", "pll1_r" }; > + > +static const char * const per_src[] = { > + "hsi_ker", "csi_ker", "hse_ck", "disabled" }; > + > +static const char * const pll_src[] = { > + "hsi_ck", "csi_ck", "hse_ck", "no clock" }; > + > +static const char * const sdmmc_src[] = { "pll1_q", "pll2_r" }; > + > +static const char * const dsi_src[] = { "ck_dsi_phy", "pll2_q" }; > + > +static const char * const qspi_src[] = { > + "hclk", "pll1_q", "pll2_r", "per_ck" }; > + > +static const char * const fmc_src[] = { > + "hclk", "pll1_q", "pll2_r", "per_ck" }; > + > +/* Kernel clock parent */ > +static const char * const swp_src[] = { "pclk1", "hsi_ker" }; > + > +static const char * const fdcan_src[] = { "hse_ck", "pll1_q", "pll2_q" }; > + > +static const char * const dfsdm1_src[] = { "pclk2", "sys_ck" }; > + > +static const char * const spdifrx_src[] = { > + "pll1_q", "pll2_r", "pll3_r", "hsi_ker" }; > + > +static const char *spi_src1[5] = { > + "pll1_q", "pll2_p", "pll3_p", NULL, "per_ck" }; > + > +static const char * const spi_src2[] = { > + "pclk2", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "hse_ck" }; > + > +static const char * const spi_src3[] = { > + "pclk4", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "hse_ck" }; > + > +static const char * const lptim_src1[] = { > + "pclk1", "pll2_p", "pll3_r", "lse_ck", "lsi_ck", "per_ck" }; > + > +static const char * const lptim_src2[] = { > + "pclk4", "pll2_p", "pll3_r", "lse_ck", "lsi_ck", "per_ck" }; > + > +static const char * const cec_src[] = {"lse_ck", "lsi_ck", "csi_ker_div122" }; > + > +static const char * const usbotg_src[] = {"pll1_q", "pll3_q", "rc48_ck" }; > + > +/* i2c 1,2,3 src */ > +static const char * const i2c_src1[] = { > + "pclk1", "pll3_r", "hsi_ker", "csi_ker" }; > + > +static const char * const i2c_src2[] = { > + "pclk4", "pll3_r", "hsi_ker", "csi_ker" }; > + > +static const char * const rng_src[] = { > + "rc48_ck", "pll1_q", "lse_ck", "lsi_ck" }; > + > +/* usart 1,6 src */ > +static const char * const usart_src1[] = { > + "pclk2", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "lse_ck" }; > + > +/* usart 2,3,4,5,7,8 src */ > +static const char * const usart_src2[] = { > + "pclk1", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "lse_ck" }; > + > +static const char *sai_src[5] = { > + "pll1_q", "pll2_p", "pll3_p", NULL, "per_ck" }; > + > +static const char * const adc_src[] = { "pll2_p", "pll3_r", "per_ck" }; > + > +/* lptim 2,3,4,5 src */ > +static const char * const lpuart1_src[] = { > + "pclk3", "pll2_q", "pll3_q", "csi_ker", "lse_ck" }; > + > +static const char * const hrtim_src[] = { "tim2_ker", "d1cpre" }; > + > +/* RTC clock parent */ > +static const char * const rtc_src[] = { "off", "lse_ck", "lsi_ck", "hse_1M" }; > + > +/* Micro-controller output clock parent */ > +static const char * const mco_src1[] = { > + "hsi_ck", "lse_ck", "hse_ck", "pll1_q", "rc48_ck" }; > + > +static const char * const mco_src2[] = { > + "sys_ck", "pll2_p", "hse_ck", "pll1_p", "csi", "lsi_ck" }; > + > +/* LCD clock */ > +static const char * const ltdc_src[] = {"pll3_r"}; > + > +/* Power domain helper */ > +static inline void disable_power_domain_write_protection(void) > +{ > + if (pdrm) > + regmap_update_bits(pdrm, 0x00, (1 << 8), (1 << 8)); > +} > + > +static inline void enable_power_domain_write_protection(void) > +{ > + if (pdrm) > + regmap_update_bits(pdrm, 0x00, (1 << 8), (0 << 8)); > +} > + > +static inline int is_enable_power_domain_write_protection(void) > +{ > + if (pdrm) { > + u32 val; > + > + regmap_read(pdrm, 0x00, &val); > + > + return !(val & 0x100); > + } > + return -1; > +} > + > +/* Gate clock with ready bit and backup domain management */ > +struct stm32_ready_gate { > + struct clk_gate gate; > + u8 bit_rdy; > + u8 backup_domain; > +}; > + > +#define to_ready_gate_clk(_rgate) container_of(_rgate, struct stm32_ready_gate,\ > + gate) > + > +#define RGATE_TIMEOUT 10000 > + > +static int ready_gate_clk_is_enabled(struct clk_hw *hw) > +{ > + return clk_gate_ops.is_enabled(hw); > +} > + > +static int ready_gate_clk_enable(struct clk_hw *hw) > +{ > + struct clk_gate *gate = to_clk_gate(hw); > + struct stm32_ready_gate *rgate = to_ready_gate_clk(gate); > + int dbp_status; > + int bit_status; > + unsigned int timeout = RGATE_TIMEOUT; > + > + if (clk_gate_ops.is_enabled(hw)) > + return 0; > + > + dbp_status = is_enable_power_domain_write_protection(); > + > + if (rgate->backup_domain && dbp_status) > + disable_power_domain_write_protection(); > + > + clk_gate_ops.enable(hw); > + > + do { > + bit_status = !(readl(gate->reg) & BIT(rgate->bit_rdy)); > + > + if (bit_status) > + udelay(100); > + > + } while (bit_status && --timeout); > + > + if (rgate->backup_domain && dbp_status) > + enable_power_domain_write_protection(); > + > + return bit_status; > +} > + > +static void ready_gate_clk_disable(struct clk_hw *hw) > +{ > + clk_gate_ops.disable(hw); > +} > + > +static const struct clk_ops ready_gate_clk_ops = { > + .enable = ready_gate_clk_enable, > + .disable = ready_gate_clk_disable, > + .is_enabled = ready_gate_clk_is_enabled, > +}; > + > +static struct clk_hw *clk_register_ready_gate(struct device *dev, > + const char *name, const char *parent_name, > + void __iomem *reg, u8 bit_idx, u8 bit_rdy, > + u8 backup_domain, unsigned long flags, spinlock_t *lock) > +{ > + struct stm32_ready_gate *rgate; > + struct clk_init_data init = { NULL }; > + struct clk_hw *hw; > + int ret; > + > + rgate = kzalloc(sizeof(*rgate), GFP_KERNEL); > + if (!rgate) > + return ERR_PTR(-ENOMEM); > + > + init.name = name; > + init.ops = &ready_gate_clk_ops; > + init.flags = flags; > + init.parent_names = &parent_name; > + init.num_parents = 1; > + > + rgate->bit_rdy = bit_rdy; > + rgate->backup_domain = backup_domain; > + > + rgate->gate.lock = lock; > + rgate->gate.reg = reg; > + rgate->gate.bit_idx = bit_idx; > + rgate->gate.hw.init = &init; > + > + hw = &rgate->gate.hw; > + ret = clk_hw_register(dev, hw); > + if (ret) { > + kfree(rgate); > + hw = ERR_PTR(ret); > + } > + > + return hw; > +} > + > +struct gate_cfg { > + u32 offset; > + u8 bit_idx; > +}; > + > +struct muxdiv_cfg { > + u32 offset; > + u8 shift; > + u8 width; > +}; > + > +struct composite_clk_cfg { > + struct gate_cfg *gate; > + struct muxdiv_cfg *mux; > + struct muxdiv_cfg *div; > + const char *name; > + const char * const *parent_name; > + int num_parents; > + u32 flags; > +}; > + > +struct composite_clk_gcfg_t { > + u8 flags; > + const struct clk_ops *ops; > +}; > + > +/* > + * General config definition of a composite clock (only clock diviser for rate) > + */ > +struct composite_clk_gcfg { > + struct composite_clk_gcfg_t *mux; > + struct composite_clk_gcfg_t *div; > + struct composite_clk_gcfg_t *gate; > +}; > + > +#define M_CFG_MUX(_mux_ops, _mux_flags)\ > + .mux = &(struct composite_clk_gcfg_t) { _mux_flags, _mux_ops} > + > +#define M_CFG_DIV(_rate_ops, _rate_flags)\ > + .div = &(struct composite_clk_gcfg_t) {_rate_flags, _rate_ops} > + > +#define M_CFG_GATE(_gate_ops, _gate_flags)\ > + .gate = &(struct composite_clk_gcfg_t) { _gate_flags, _gate_ops} > + > +static struct clk_mux *_get_cmux(void __iomem *reg, u8 shift, u8 width, > + u32 flags, spinlock_t *lock) > +{ > + struct clk_mux *mux; > + > + mux = kzalloc(sizeof(*mux), GFP_KERNEL); > + if (!mux) > + return ERR_PTR(-ENOMEM); > + > + mux->reg = reg; > + mux->shift = shift; > + mux->mask = (1 << width) - 1; > + mux->flags = flags; > + mux->lock = lock; > + > + return mux; > +} > + > +static struct clk_divider *_get_cdiv(void __iomem *reg, u8 shift, u8 width, > + u32 flags, spinlock_t *lock) > +{ > + struct clk_divider *div; > + > + div = kzalloc(sizeof(*div), GFP_KERNEL); > + > + if (!div) > + return ERR_PTR(-ENOMEM); > + > + div->reg = reg; > + div->shift = shift; > + div->width = width; > + div->flags = flags; > + div->lock = lock; > + > + return div; > +} > + > +static struct clk_gate *_get_cgate(void __iomem *reg, u8 bit_idx, u32 flags, > + spinlock_t *lock) > +{ > + struct clk_gate *gate; > + > + gate = kzalloc(sizeof(*gate), GFP_KERNEL); > + if (!gate) > + return ERR_PTR(-ENOMEM); > + > + gate->reg = reg; > + gate->bit_idx = bit_idx; > + gate->flags = flags; > + gate->lock = lock; > + > + return gate; > +} > + > +struct composite_cfg { > + struct clk_hw *mux_hw; > + struct clk_hw *div_hw; > + struct clk_hw *gate_hw; > + > + const struct clk_ops *mux_ops; > + const struct clk_ops *div_ops; > + const struct clk_ops *gate_ops; > +}; > + > +static void get_cfg_composite_div(const struct composite_clk_gcfg *gcfg, > + const struct composite_clk_cfg *cfg, > + struct composite_cfg *composite, spinlock_t *lock) > +{ > + struct clk_mux *mux = NULL; > + struct clk_divider *div = NULL; > + struct clk_gate *gate = NULL; > + const struct clk_ops *mux_ops, *div_ops, *gate_ops; > + struct clk_hw *mux_hw; > + struct clk_hw *div_hw; > + struct clk_hw *gate_hw; > + > + mux_ops = div_ops = gate_ops = NULL; > + mux_hw = div_hw = gate_hw = NULL; > + > + if (gcfg->mux) { > + mux = _get_cmux(base + cfg->mux->offset, > + cfg->mux->shift, > + cfg->mux->width, > + gcfg->mux->flags, lock); > + > + if (!IS_ERR(mux)) { > + mux_hw = &mux->hw; > + mux_ops = gcfg->mux->ops ? > + gcfg->mux->ops : &clk_mux_ops; > + } > + } > + > + if (gcfg->div) { > + div = _get_cdiv(base + cfg->div->offset, > + cfg->div->shift, > + cfg->div->width, > + gcfg->div->flags, lock); > + > + if (!IS_ERR(div)) { > + div_hw = &div->hw; > + div_ops = gcfg->div->ops ? > + gcfg->div->ops : &clk_divider_ops; > + } > + > + } > + > + if (gcfg->gate) { > + gate = _get_cgate(base + cfg->gate->offset, > + cfg->gate->bit_idx, > + gcfg->gate->flags, lock); > + > + if (!IS_ERR(gate)) { > + gate_hw = &gate->hw; > + gate_ops = gcfg->gate->ops ? > + gcfg->gate->ops : &clk_gate_ops; > + } > + > + } > + > + composite->mux_hw = mux_hw; > + composite->mux_ops = mux_ops; > + > + composite->div_hw = div_hw; > + composite->div_ops = div_ops; > + > + composite->gate_hw = gate_hw; > + composite->gate_ops = gate_ops; > +} > + > +/* Kernel Timer */ > +struct timer_ker { > + u8 dppre_shift; > + struct clk_hw hw; > + spinlock_t *lock; > +}; > + > +#define to_timer_ker(_hw) container_of(_hw, struct timer_ker, hw) > + > +static unsigned long timer_ker_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + struct timer_ker *clk_elem = to_timer_ker(hw); > + u32 timpre; > + u32 dppre_shift = clk_elem->dppre_shift; > + u32 prescaler; > + u32 mul; > + > + timpre = (readl(base + RCC_CFGR) >> 15) & 0x01; > + > + prescaler = (readl(base + RCC_D2CFGR) >> dppre_shift) & 0x03; > + > + mul = 2; > + > + if (prescaler < 4) > + mul = 1; > + > + else if (timpre && prescaler > 4) > + mul = 4; > + > + return parent_rate * mul; > +} > + > +static const struct clk_ops timer_ker_ops = { > + .recalc_rate = timer_ker_recalc_rate, > +}; > + > +static struct clk_hw *clk_register_stm32_timer_ker(struct device *dev, > + const char *name, const char *parent_name, > + unsigned long flags, > + u8 dppre_shift, > + spinlock_t lock) > +{ > + struct timer_ker *element; > + struct clk_init_data init; > + struct clk_hw *hw; > + int err; > + > + element = kzalloc(sizeof(*element), GFP_KERNEL); > + if (!element) > + return ERR_PTR(-ENOMEM); > + > + init.name = name; > + init.ops = &timer_ker_ops; > + init.flags = flags; > + init.parent_names = &parent_name; > + init.num_parents = 1; > + > + element->hw.init = &init; > + element->lock = &lock; > + element->dppre_shift = dppre_shift; > + > + hw = &element->hw; > + err = clk_hw_register(dev, hw); > + > + if (err) { > + kfree(element); > + return ERR_PTR(err); > + } > + > + return hw; > +} > + > +static const struct clk_div_table d1cpre_div_table[] = { > + { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1}, > + { 4, 1 }, { 5, 1 }, { 6, 1 }, { 7, 1}, > + { 8, 2 }, { 9, 4 }, { 10, 8 }, { 11, 16 }, > + { 12, 64 }, { 13, 128 }, { 14, 256 }, > + { 15, 512 }, > + { 0 }, > +}; > + > +static const struct clk_div_table ppre_div_table[] = { > + { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1}, > + { 4, 2 }, { 5, 4 }, { 6, 8 }, { 7, 16 }, > + { 0 }, > +}; > + > +static void register_core_and_bus_clocks(void) > +{ > + /* CORE AND BUS */ > + hws[SYS_D1CPRE] = clk_hw_register_divider_table(NULL, "d1cpre", > + "sys_ck", CLK_IGNORE_UNUSED, base + RCC_D1CFGR, 8, 4, 0, > + d1cpre_div_table, &rlock); > + > + hws[HCLK] = clk_hw_register_divider_table(NULL, "hclk", "d1cpre", > + CLK_IGNORE_UNUSED, base + RCC_D1CFGR, 0, 4, 0, > + d1cpre_div_table, &rlock); > + > + /* D1 DOMAIN */ > + /* * CPU Systick */ > + hws[CPU_SYSTICK] = clk_hw_register_fixed_factor(NULL, "systick", > + "d1cpre", 0, 1, 8); > + > + /* * APB3 peripheral */ > + hws[PCLK3] = clk_hw_register_divider_table(NULL, "pclk3", "hclk", 0, > + base + RCC_D1CFGR, 4, 3, 0, > + ppre_div_table, &rlock); > + > + /* D2 DOMAIN */ > + /* * APB1 peripheral */ > + hws[PCLK1] = clk_hw_register_divider_table(NULL, "pclk1", "hclk", 0, > + base + RCC_D2CFGR, 4, 3, 0, > + ppre_div_table, &rlock); > + > + /* Timers prescaler clocks */ > + clk_register_stm32_timer_ker(NULL, "tim1_ker", "pclk1", 0, > + 4, rlock); > + > + /* * APB2 peripheral */ > + hws[PCLK2] = clk_hw_register_divider_table(NULL, "pclk2", "hclk", 0, > + base + RCC_D2CFGR, 8, 3, 0, ppre_div_table, &rlock); > + > + clk_register_stm32_timer_ker(NULL, "tim2_ker", "pclk2", 0, 8, rlock); > + > + /* D3 DOMAIN */ > + /* * APB4 peripheral */ > + hws[PCLK4] = clk_hw_register_divider_table(NULL, "pclk4", "hclk", 0, > + base + RCC_D3CFGR, 4, 3, 0, > + ppre_div_table, &rlock); > +} > + > +/* MUX clock configuration */ > +struct stm32_mux_clk { > + const char *name; > + const char * const *parents; > + u8 num_parents; > + u32 offset; > + u8 shift; > + u8 width; > + u32 flags; > +}; > + > +#define M_MCLOCF(_name, _parents, _mux_offset, _mux_shift, _mux_width, _flags)\ > +{\ > + .name = _name,\ > + .parents = _parents,\ > + .num_parents = ARRAY_SIZE(_parents),\ > + .offset = _mux_offset,\ > + .shift = _mux_shift,\ > + .width = _mux_width,\ > + .flags = _flags,\ > +} > +#define M_MCLOC(_name, _parents, _mux_offset, _mux_shift, _mux_width)\ > + M_MCLOCF(_name, _parents, _mux_offset, _mux_shift, _mux_width, 0)\ > + > +static const struct stm32_mux_clk stm32_mclk[] __initconst = { > + M_MCLOC("per_ck", per_src, RCC_D1CCIPR, 28, 3), > + M_MCLOC("pllsrc", pll_src, RCC_PLLCKSELR, 0, 3), > + M_MCLOC("sys_ck", sys_src, RCC_CFGR, 0, 3), > + M_MCLOC("tracein_ck", tracein_src, RCC_CFGR, 0, 3), > +}; > + > +/* Oscillary clock configuration */ > +struct stm32_osc_clk { > + const char *name; > + const char *parent; > + u32 gate_offset; > + u8 bit_idx; > + u8 bit_rdy; > + u32 flags; > +}; > + > +#define OSC_CLKF(_name, _parent, _gate_offset, _bit_idx, _bit_rdy, _flags)\ > +{\ > + .name = _name,\ > + .parent = _parent,\ > + .gate_offset = _gate_offset,\ > + .bit_idx = _bit_idx,\ > + .bit_rdy = _bit_rdy,\ > + .flags = _flags,\ > +} > +#define OSC_CLK(_name, _parent, _gate_offset, _bit_idx, _bit_rdy)\ > + OSC_CLKF(_name, _parent, _gate_offset, _bit_idx, _bit_rdy, 0) > + > +static const struct stm32_osc_clk stm32_oclk[] __initconst = { > + OSC_CLK("hsi_ck", "hsidiv", RCC_CR, 0, 2), > + OSC_CLK("hsi_ker", "hsidiv", RCC_CR, 1, 2), > + OSC_CLK("csi_ck", "clk-csi", RCC_CR, 7, 8), > + OSC_CLK("csi_ker", "clk-csi", RCC_CR, 9, 8), > + OSC_CLK("rc48_ck", "clk-rc48", RCC_CR, 12, 13), > + OSC_CLK("lsi_ck", "clk-lsi", RCC_CSR, 0, 1), > +}; > + > +/* PLL configuration */ > +struct st32h7_pll_cfg { > + u8 bit_idx; > + u32 offset_divr; > + u8 bit_frac_en; > + u32 offset_frac; > + u8 divm; > +}; > + > +struct stm32_pll_data { > + const char *name; > + const char *parent_name; > + unsigned long flags; > + const struct st32h7_pll_cfg *cfg; > +}; > + > +static const struct st32h7_pll_cfg stm32h7_pll1 = { > + .bit_idx = 24, > + .offset_divr = RCC_PLL1DIVR, > + .bit_frac_en = 0, > + .offset_frac = RCC_PLL1FRACR, > + .divm = 4, > +}; > + > +static const struct st32h7_pll_cfg stm32h7_pll2 = { > + .bit_idx = 26, > + .offset_divr = RCC_PLL2DIVR, > + .bit_frac_en = 4, > + .offset_frac = RCC_PLL2FRACR, > + .divm = 12, > +}; > + > +static const struct st32h7_pll_cfg stm32h7_pll3 = { > + .bit_idx = 28, > + .offset_divr = RCC_PLL3DIVR, > + .bit_frac_en = 8, > + .offset_frac = RCC_PLL3FRACR, > + .divm = 20, > +}; > + > +static const struct stm32_pll_data stm32_pll[] = { > + { "vco1", "pllsrc", 0, &stm32h7_pll1 }, > + { "vco2", "pllsrc", 0, &stm32h7_pll2 }, > + { "vco3", "pllsrc", 0, &stm32h7_pll3 }, > +}; > + > +struct stm32_fractional_divider { > + void __iomem *mreg; > + u8 mshift; > + u8 mwidth; > + u32 mmask; > + > + void __iomem *nreg; > + u8 nshift; > + u8 nwidth; > + > + void __iomem *freg_status; > + u8 freg_bit; > + void __iomem *freg_value; > + u8 fshift; > + u8 fwidth; > + > + u8 flags; > + struct clk_hw hw; > + spinlock_t *lock; > +}; > + > +struct stm32_pll_obj { > + spinlock_t *lock; > + struct stm32_fractional_divider div; > + struct stm32_ready_gate rgate; > + struct clk_hw hw; > +}; > + > +#define to_pll(_hw) container_of(_hw, struct stm32_pll_obj, hw) > + > +static int pll_is_enabled(struct clk_hw *hw) > +{ > + struct stm32_pll_obj *clk_elem = to_pll(hw); > + struct clk_hw *_hw = &clk_elem->rgate.gate.hw; > + > + __clk_hw_set_clk(_hw, hw); > + > + return ready_gate_clk_ops.is_enabled(_hw); > +} > + > +static int pll_enable(struct clk_hw *hw) > +{ > + struct stm32_pll_obj *clk_elem = to_pll(hw); > + struct clk_hw *_hw = &clk_elem->rgate.gate.hw; > + > + __clk_hw_set_clk(_hw, hw); > + > + return ready_gate_clk_ops.enable(_hw); > +} > + > +static void pll_disable(struct clk_hw *hw) > +{ > + struct stm32_pll_obj *clk_elem = to_pll(hw); > + struct clk_hw *_hw = &clk_elem->rgate.gate.hw; > + > + __clk_hw_set_clk(_hw, hw); > + > + ready_gate_clk_ops.disable(_hw); > +} > + > +static int pll_frac_is_enabled(struct clk_hw *hw) > +{ > + struct stm32_pll_obj *clk_elem = to_pll(hw); > + struct stm32_fractional_divider *fd = &clk_elem->div; > + > + return (readl(fd->freg_status) >> fd->freg_bit) & 0x01; > +} > + > +static unsigned long pll_read_frac(struct clk_hw *hw) > +{ > + struct stm32_pll_obj *clk_elem = to_pll(hw); > + struct stm32_fractional_divider *fd = &clk_elem->div; > + > + return (readl(fd->freg_value) >> fd->fshift) & > + GENMASK(fd->fwidth - 1, 0); > +} > + > +static unsigned long pll_fd_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + struct stm32_pll_obj *clk_elem = to_pll(hw); > + struct stm32_fractional_divider *fd = &clk_elem->div; > + unsigned long m, n; > + u32 val, mask; > + u64 rate, rate1 = 0; > + > + val = clk_readl(fd->mreg); > + mask = (GENMASK(fd->mwidth - 1, 0) << fd->mshift); > + m = (val & mask) >> fd->mshift; > + > + val = clk_readl(fd->nreg); > + mask = (GENMASK(fd->nwidth - 1, 0) << fd->nshift); > + n = ((val & mask) >> fd->nshift) + 1; > + > + if (!n || !m) > + return parent_rate; > + > + rate = (u64)parent_rate * n; > + do_div(rate, m); > + > + if (pll_frac_is_enabled(hw)) { > + val = pll_read_frac(hw); > + rate1 = (u64) parent_rate * (u64) val; > + do_div(rate1, (m * 8191)); > + } > + > + return rate + rate1; > +} > + > +static const struct clk_ops pll_ops = { > + .enable = pll_enable, > + .disable = pll_disable, > + .is_enabled = pll_is_enabled, > + .recalc_rate = pll_fd_recalc_rate, > +}; > + > +static struct clk_hw *clk_register_stm32_pll(struct device *dev, > + const char *name, > + const char *parent, > + unsigned long flags, > + const struct st32h7_pll_cfg *cfg, > + spinlock_t *lock) > +{ > + > + struct stm32_pll_obj *pll; > + struct clk_init_data init = { NULL }; > + struct clk_hw *hw; > + int ret; > + struct stm32_fractional_divider *div = NULL; > + struct stm32_ready_gate *rgate; > + > + > + pll = kzalloc(sizeof(*pll), GFP_KERNEL); > + if (!pll) > + return ERR_PTR(-ENOMEM); > + > + init.name = name; > + init.ops = &pll_ops; > + init.flags = flags; > + init.parent_names = &parent; > + init.num_parents = 1; > + pll->hw.init = &init; > + > + hw = &pll->hw; > + rgate = &pll->rgate; > + > + rgate->bit_rdy = cfg->bit_idx + 1; > + rgate->gate.lock = lock; > + rgate->gate.reg = base + RCC_CR; > + rgate->gate.bit_idx = cfg->bit_idx; > + > + div = &pll->div; > + div->flags = 0; > + div->mreg = base + RCC_PLLCKSELR; > + div->mshift = cfg->divm; > + div->mwidth = 6; > + div->nreg = base + cfg->offset_divr; > + div->nshift = 0; > + div->nwidth = 9; > + > + div->freg_status = base + RCC_PLLCFGR; > + div->freg_bit = cfg->bit_frac_en; > + div->freg_value = base + cfg->offset_frac; > + div->fshift = 3; > + div->fwidth = 13; > + > + div->lock = lock; > + > + ret = clk_hw_register(dev, hw); > + if (ret) { > + kfree(pll); > + hw = ERR_PTR(ret); > + } > + > + return hw; > +} > + > +/* ODF CLOCKS */ > +static unsigned long odf_divider_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + return clk_divider_ops.recalc_rate(hw, parent_rate); > +} > + > +static long odf_divider_round_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long *prate) > +{ > + return clk_divider_ops.round_rate(hw, rate, prate); > +} > + > +static int odf_divider_set_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long parent_rate) > +{ > + struct clk_hw *hwp; > + int pll_status; > + int ret; > + > + hwp = clk_hw_get_parent(hw); > + > + pll_status = pll_is_enabled(hwp); > + > + if (pll_status) > + pll_disable(hwp); > + > + ret = clk_divider_ops.set_rate(hw, rate, parent_rate); > + > + if (pll_status) > + pll_enable(hwp); > + > + return ret; > +} > + > +static const struct clk_ops odf_divider_ops = { > + .recalc_rate = odf_divider_recalc_rate, > + .round_rate = odf_divider_round_rate, > + .set_rate = odf_divider_set_rate, > +}; > + > +static int odf_gate_enable(struct clk_hw *hw) > +{ > + struct clk_hw *hwp; > + int pll_status; > + int ret; > + > + hwp = clk_hw_get_parent(hw); > + > + pll_status = pll_is_enabled(hwp); > + > + if (pll_status) > + pll_disable(hwp); > + > + ret = clk_gate_ops.enable(hw); > + > + if (pll_status) > + pll_enable(hwp); > + > + return ret; > +} > + > +static void odf_gate_disable(struct clk_hw *hw) > +{ > + clk_gate_ops.disable(hw); > +} > + > +static int odf_gate_is_enabled(struct clk_hw *hw) > +{ > + return clk_gate_ops.is_enabled(hw); > +} > + > +static const struct clk_ops odf_gate_ops = { > + .enable = odf_gate_enable, > + .disable = odf_gate_disable, > + .is_enabled = odf_gate_is_enabled, > +}; > + > +static struct composite_clk_gcfg odf_clk_gcfg = { > + M_CFG_DIV(&odf_divider_ops, 0), > + M_CFG_GATE(&odf_gate_ops, 0), > +}; > + > +#define M_ODF_F(_name, _parent, _gate_offset, _bit_idx, _rate_offset,\ > + _rate_shift, _rate_width, _flags)\ > +{\ > + .mux = NULL,\ > + .div = &(struct muxdiv_cfg) {_rate_offset, _rate_shift, _rate_width},\ > + .gate = &(struct gate_cfg) {_gate_offset, _bit_idx },\ > + .name = _name,\ > + .parent_name = &(const char *) {_parent},\ > + .num_parents = 1,\ > + .flags = _flags,\ > +} > + > +#define M_ODF(_name, _parent, _gate_offset, _bit_idx, _rate_offset,\ > + _rate_shift, _rate_width)\ > +M_ODF_F(_name, _parent, _gate_offset, _bit_idx, _rate_offset,\ > + _rate_shift, _rate_width, 0)\ > + > +static const struct composite_clk_cfg stm32_odf[3][3] = { > + { > + M_ODF("pll1_p", "vco1", RCC_PLLCFGR, 16, RCC_PLL1DIVR, 9, 7), > + M_ODF("pll1_q", "vco1", RCC_PLLCFGR, 17, RCC_PLL1DIVR, 16, 7), > + M_ODF("pll1_r", "vco1", RCC_PLLCFGR, 18, RCC_PLL1DIVR, 24, 7), > + }, > + > + { > + M_ODF("pll2_p", "vco2", RCC_PLLCFGR, 19, RCC_PLL2DIVR, 9, 7), > + M_ODF("pll2_q", "vco2", RCC_PLLCFGR, 20, RCC_PLL2DIVR, 16, 7), > + M_ODF("pll2_r", "vco2", RCC_PLLCFGR, 21, RCC_PLL2DIVR, 24, 7), > + }, > + { > + M_ODF("pll3_p", "vco3", RCC_PLLCFGR, 22, RCC_PLL3DIVR, 9, 7), > + M_ODF("pll3_q", "vco3", RCC_PLLCFGR, 23, RCC_PLL3DIVR, 16, 7), > + M_ODF("pll3_r", "vco3", RCC_PLLCFGR, 24, RCC_PLL3DIVR, 24, 7), > + } > +}; > + > +/* PLL config structure from DT */ > +struct pll_param { > + u32 index; > + u32 mult; > + u32 div; > + u32 frac_en; > + u32 frac; > + u32 vcosel; > + u32 pllrge; > +}; > + > +static int of_get_stm32_pll(struct device_node *np, struct pll_param *param) > +{ > + if (of_property_read_u32(np, "reg", ¶m->index) || > + param->index >= ARRAY_SIZE(stm32_pll)) > + return -EINVAL; > + > + of_property_read_u32(np, "st,clock-div", ¶m->div); > + > + of_property_read_u32(np, "st,clock-mult", ¶m->mult); > + > + param->frac_en = ~0; > + of_property_read_u32(np, "st,frac-status", ¶m->frac_en); > + > + of_property_read_u32(np, "st,frac", ¶m->frac); > + > + param->vcosel = ~0; > + of_property_read_u32(np, "st,vcosel", ¶m->vcosel); > + > + param->pllrge = ~0; > + of_property_read_u32(np, "st,pllrge", ¶m->pllrge); > + > + return 0; > +} > + > +static void stm32_pll_save_param(struct clk_hw *hw, > + struct pll_param *pll_dt_cfg) > +{ > + struct stm32_pll_obj *clk_elem = to_pll(hw); > + struct stm32_fractional_divider *fd = &clk_elem->div; > + unsigned long m, n; > + u32 val; > + int pll_status; > + > + /* Save PLL parameters from DT if needed */ > + pll_status = pll_ops.is_enabled(hw); > + > + if (pll_status) > + pll_ops.disable(hw); > + > + m = pll_dt_cfg->div; > + if (m) { > + val = clk_readl(fd->mreg); > + val &= ~(GENMASK(fd->mwidth - 1, 0) << fd->mshift); > + val |= (m << fd->mshift); > + writel(val, fd->mreg); > + } > + > + n = pll_dt_cfg->mult; > + if (n) { > + val = clk_readl(fd->nreg); > + val &= ~((GENMASK(fd->nwidth - 1, 0) << fd->nshift)); > + val |= ((n - 1) << fd->nshift); > + writel(val, fd->nreg); > + } > + > + if (pll_dt_cfg->frac_en != ~0) { > + int status; > + > + status = !!pll_dt_cfg->frac_en; > + > + /* write frac status */ > + val = readl(fd->freg_status); > + > + val &= ~BIT(fd->freg_bit); > + val |= status << fd->freg_bit; > + writel(val, fd->freg_status); > + > + /* write frac value */ > + val = clk_readl(fd->freg_value); > + val &= ~((GENMASK(fd->fshift - 1, 0) << fd->fshift)); > + > + /* if enable, set to '0' before */ > + if (status) > + writel(val, fd->freg_value); > + > + val |= ((pll_dt_cfg->frac) << fd->fshift); > + writel(val, fd->freg_value); > + } > + > + if (pll_dt_cfg->vcosel != ~0) { > + val = readl(fd->freg_status); > + val &= ~BIT(fd->freg_bit + 1); > + val |= (pll_dt_cfg->vcosel & 0x01) << (fd->freg_bit + 1); > + writel(val, fd->freg_status); > + > + } > + > + if (pll_dt_cfg->pllrge != ~0) { > + val = readl(fd->freg_status); > + val &= ~(0x3 << (fd->freg_bit + 2)); > + val |= (pll_dt_cfg->pllrge & 0x3) << (fd->freg_bit + 2); > + writel(val, fd->freg_status); > + } > + > + if (pll_status) > + pll_ops.enable(hw); > +} > + > +static void stm32_h7_pll_init(struct device_node *np) > +{ > + struct pll_param pll_dt_cfg = { }; > + struct clk_hw *hw; > + int idx, n; > + > + if (of_get_stm32_pll(np, &pll_dt_cfg)) > + return; > + > + idx = pll_dt_cfg.index; > + > + /* Register the VCO */ > + hw = clk_register_stm32_pll(NULL, stm32_pll[idx].name, > + stm32_pll[idx].parent_name, stm32_pll[idx].flags, > + stm32_pll[idx].cfg, > + &rlock); > + > + stm32_pll_save_param(hw, &pll_dt_cfg); > + > + /* Register the 3 output dividers */ > + for (n = 0; n < 3; n++) { > + struct composite_cfg c_cfg; > + > + get_cfg_composite_div(&odf_clk_gcfg, &stm32_odf[idx][n], > + &c_cfg, &rlock); > + > + hws[ODF_BANK + (idx * 3) + n] = clk_hw_register_composite(NULL, > + stm32_odf[idx][n].name, > + stm32_odf[idx][n].parent_name, > + stm32_odf[idx][n].num_parents, > + c_cfg.mux_hw, c_cfg.mux_ops, > + c_cfg.div_hw, c_cfg.div_ops, > + c_cfg.gate_hw, c_cfg.gate_ops, > + stm32_odf[idx][n].flags); > + } > +} > + > +/* PERIF CLOCKS */ > +struct pclk_t { > + u32 gate_offset; > + u8 bit_idx; > + const char *name; > + const char *parent; > + u32 flags; > +}; > + > +#define PER_CLKF(_gate_offset, _bit_idx, _name, _parent, _flags)\ > +{\ > + .gate_offset = _gate_offset,\ > + .bit_idx = _bit_idx,\ > + .name = _name,\ > + .parent = _parent,\ > + .flags = _flags,\ > +} > +#define PER_CLK(_gate_offset, _bit_idx, _name, _parent)\ > + PER_CLKF(_gate_offset, _bit_idx, _name, _parent, 0) > + > +static const struct pclk_t pclk[] = { > + PER_CLK(RCC_AHB3ENR, 31, "d1sram1", "hclk"), > + PER_CLK(RCC_AHB3ENR, 30, "itcm", "hclk"), > + PER_CLK(RCC_AHB3ENR, 29, "dtcm2", "hclk"), > + PER_CLK(RCC_AHB3ENR, 28, "dtcm1", "hclk"), > + PER_CLK(RCC_AHB3ENR, 8, "flitf", "hclk"), > + PER_CLK(RCC_AHB3ENR, 5, "jpgdec", "hclk"), > + PER_CLK(RCC_AHB3ENR, 4, "dma2d", "hclk"), > + PER_CLK(RCC_AHB3ENR, 0, "mdma", "hclk"), > + PER_CLK(RCC_AHB1ENR, 28, "usb2ulpi", "hclk"), > + PER_CLK(RCC_AHB1ENR, 26, "usb1ulpi", "hclk"), > + PER_CLK(RCC_AHB1ENR, 17, "eth1rx", "hclk"), > + PER_CLK(RCC_AHB1ENR, 16, "eth1tx", "hclk"), > + PER_CLK(RCC_AHB1ENR, 15, "eth1mac", "hclk"), > + PER_CLK(RCC_AHB1ENR, 14, "art", "hclk"), > + PER_CLK(RCC_AHB1ENR, 1, "dma2", "hclk"), > + PER_CLK(RCC_AHB1ENR, 0, "dma1", "hclk"), > + PER_CLK(RCC_AHB2ENR, 31, "d2sram3", "hclk"), > + PER_CLK(RCC_AHB2ENR, 30, "d2sram2", "hclk"), > + PER_CLK(RCC_AHB2ENR, 29, "d2sram1", "hclk"), > + PER_CLK(RCC_AHB2ENR, 5, "hash", "hclk"), > + PER_CLK(RCC_AHB2ENR, 4, "crypt", "hclk"), > + PER_CLK(RCC_AHB2ENR, 0, "camitf", "hclk"), > + PER_CLK(RCC_AHB4ENR, 28, "bkpram", "hclk"), > + PER_CLK(RCC_AHB4ENR, 25, "hsem", "hclk"), > + PER_CLK(RCC_AHB4ENR, 21, "bdma", "hclk"), > + PER_CLK(RCC_AHB4ENR, 19, "crc", "hclk"), > + PER_CLK(RCC_AHB4ENR, 10, "gpiok", "hclk"), > + PER_CLK(RCC_AHB4ENR, 9, "gpioj", "hclk"), > + PER_CLK(RCC_AHB4ENR, 8, "gpioi", "hclk"), > + PER_CLK(RCC_AHB4ENR, 7, "gpioh", "hclk"), > + PER_CLK(RCC_AHB4ENR, 6, "gpiog", "hclk"), > + PER_CLK(RCC_AHB4ENR, 5, "gpiof", "hclk"), > + PER_CLK(RCC_AHB4ENR, 4, "gpioe", "hclk"), > + PER_CLK(RCC_AHB4ENR, 3, "gpiod", "hclk"), > + PER_CLK(RCC_AHB4ENR, 2, "gpioc", "hclk"), > + PER_CLK(RCC_AHB4ENR, 1, "gpiob", "hclk"), > + PER_CLK(RCC_AHB4ENR, 0, "gpioa", "hclk"), > + PER_CLK(RCC_APB3ENR, 6, "wwdg1", "pclk3"), > + PER_CLK(RCC_APB1LENR, 29, "dac12", "pclk1"), > + PER_CLK(RCC_APB1LENR, 11, "wwdg2", "pclk1"), > + PER_CLK(RCC_APB1LENR, 8, "tim14", "pclk1"), > + PER_CLK(RCC_APB1LENR, 7, "tim13", "pclk1"), > + PER_CLK(RCC_APB1LENR, 6, "tim12", "pclk1"), > + PER_CLK(RCC_APB1LENR, 5, "tim7", "pclk1"), > + PER_CLK(RCC_APB1LENR, 4, "tim6", "pclk1"), > + PER_CLK(RCC_APB1LENR, 3, "tim5", "pclk1"), > + PER_CLK(RCC_APB1LENR, 2, "tim4", "pclk1"), > + PER_CLK(RCC_APB1LENR, 1, "tim3", "pclk1"), > + PER_CLK(RCC_APB1LENR, 0, "tim2", "pclk1"), > + PER_CLK(RCC_APB1HENR, 5, "mdios", "pclk1"), > + PER_CLK(RCC_APB1HENR, 4, "opamp", "pclk1"), > + PER_CLK(RCC_APB1HENR, 1, "crs", "pclk1"), > + PER_CLK(RCC_APB2ENR, 18, "tim17", "pclk2"), > + PER_CLK(RCC_APB2ENR, 17, "tim16", "pclk2"), > + PER_CLK(RCC_APB2ENR, 16, "tim15", "pclk2"), > + PER_CLK(RCC_APB2ENR, 1, "tim8", "pclk2"), > + PER_CLK(RCC_APB2ENR, 0, "tim1", "pclk2"), > + PER_CLK(RCC_APB4ENR, 26, "tmpsens", "pclk4"), > + PER_CLK(RCC_APB4ENR, 16, "rtcapb", "pclk4"), > + PER_CLK(RCC_APB4ENR, 15, "vref", "pclk4"), > + PER_CLK(RCC_APB4ENR, 14, "comp12", "pclk4"), > + PER_CLK(RCC_APB4ENR, 1, "syscfg", "pclk4"), > +}; > + > +/* KERNEL CLOCKS */ > +#define KER_CLKF(_gate_offset, _bit_idx,\ > + _mux_offset, _mux_shift, _mux_width,\ > + _name, _parent_name,\ > + _flags) \ > +{ \ > + .gate = &(struct gate_cfg) {_gate_offset, _bit_idx},\ > + .mux = &(struct muxdiv_cfg) {_mux_offset, _mux_shift, _mux_width },\ > + .name = _name, \ > + .parent_name = _parent_name, \ > + .num_parents = ARRAY_SIZE(_parent_name),\ > + .flags = _flags,\ > +} > +#define KER_CLK(_gate_offset, _bit_idx, _mux_offset, _mux_shift, _mux_width,\ > + _name, _parent_name) \ > +KER_CLKF(_gate_offset, _bit_idx, _mux_offset, _mux_shift, _mux_width,\ > + _name, _parent_name, 0)\ > + > +#define KER_CLKF_NOMUX(_gate_offset, _bit_idx,\ > + _name, _parent_name,\ > + _flags) \ > +{ \ > + .gate = &(struct gate_cfg) {_gate_offset, _bit_idx},\ > + .mux = NULL,\ > + .name = _name, \ > + .parent_name = _parent_name, \ > + .num_parents = 1,\ > + .flags = _flags,\ > +} > + > +static const struct composite_clk_cfg kclk[] = { > + KER_CLK(RCC_AHB3ENR, 16, RCC_D1CCIPR, 16, 1, "sdmmc1", sdmmc_src), > + KER_CLKF(RCC_AHB3ENR, 14, RCC_D1CCIPR, 4, 2, "quadspi", qspi_src, > + CLK_IGNORE_UNUSED), > + KER_CLKF(RCC_AHB3ENR, 12, RCC_D1CCIPR, 0, 2, "fmc", fmc_src, > + CLK_IGNORE_UNUSED), > + KER_CLK(RCC_AHB1ENR, 27, RCC_D2CCIP2R, 20, 2, "usb2otg", usbotg_src), > + KER_CLK(RCC_AHB1ENR, 25, RCC_D2CCIP2R, 20, 2, "usb1otg", usbotg_src), > + KER_CLK(RCC_AHB1ENR, 5, RCC_D3CCIPR, 16, 2, "adc12", adc_src), > + KER_CLK(RCC_AHB2ENR, 9, RCC_D1CCIPR, 16, 1, "sdmmc2", sdmmc_src), > + KER_CLK(RCC_AHB2ENR, 6, RCC_D2CCIP2R, 8, 2, "rng", rng_src), > + KER_CLK(RCC_AHB4ENR, 24, RCC_D3CCIPR, 16, 2, "adc3", adc_src), > + KER_CLK(RCC_APB3ENR, 4, RCC_D1CCIPR, 8, 1, "dsi", dsi_src), > + KER_CLKF_NOMUX(RCC_APB3ENR, 3, "ltdc", ltdc_src, 0), > + KER_CLK(RCC_APB1LENR, 31, RCC_D2CCIP2R, 0, 3, "usart8", usart_src2), > + KER_CLK(RCC_APB1LENR, 30, RCC_D2CCIP2R, 0, 3, "usart7", usart_src2), > + KER_CLK(RCC_APB1LENR, 27, RCC_D2CCIP2R, 22, 2, "hdmicec", cec_src), > + KER_CLK(RCC_APB1LENR, 23, RCC_D2CCIP2R, 12, 2, "i2c3", i2c_src1), > + KER_CLK(RCC_APB1LENR, 22, RCC_D2CCIP2R, 12, 2, "i2c2", i2c_src1), > + KER_CLK(RCC_APB1LENR, 21, RCC_D2CCIP2R, 12, 2, "i2c1", i2c_src1), > + KER_CLK(RCC_APB1LENR, 20, RCC_D2CCIP2R, 0, 3, "uart5", usart_src2), > + KER_CLK(RCC_APB1LENR, 19, RCC_D2CCIP2R, 0, 3, "uart4", usart_src2), > + KER_CLK(RCC_APB1LENR, 18, RCC_D2CCIP2R, 0, 3, "usart3", usart_src2), > + KER_CLK(RCC_APB1LENR, 17, RCC_D2CCIP2R, 0, 3, "usart2", usart_src2), > + KER_CLK(RCC_APB1LENR, 16, RCC_D2CCIP1R, 20, 2, "spdifrx", spdifrx_src), > + KER_CLK(RCC_APB1LENR, 15, RCC_D2CCIP1R, 16, 3, "spi3", spi_src1), > + KER_CLK(RCC_APB1LENR, 14, RCC_D2CCIP1R, 16, 3, "spi2", spi_src1), > + KER_CLK(RCC_APB1LENR, 9, RCC_D2CCIP2R, 28, 3, "lptim1", lptim_src1), > + KER_CLK(RCC_APB1HENR, 8, RCC_D2CCIP1R, 28, 2, "fdcan", fdcan_src), > + KER_CLK(RCC_APB1HENR, 2, RCC_D2CCIP1R, 31, 1, "swp", swp_src), > + KER_CLK(RCC_APB2ENR, 29, RCC_CFGR, 14, 1, "hrtim", hrtim_src), > + KER_CLK(RCC_APB2ENR, 28, RCC_D2CCIP1R, 24, 1, "dfsdm1", dfsdm1_src), > + KER_CLK(RCC_APB2ENR, 24, RCC_D2CCIP1R, 6, 3, "sai3", sai_src), > + KER_CLK(RCC_APB2ENR, 23, RCC_D2CCIP1R, 6, 3, "sai2", sai_src), > + KER_CLK(RCC_APB2ENR, 22, RCC_D2CCIP1R, 0, 3, "sai1", sai_src), > + KER_CLK(RCC_APB2ENR, 20, RCC_D2CCIP1R, 16, 3, "spi5", spi_src2), > + KER_CLK(RCC_APB2ENR, 13, RCC_D2CCIP1R, 16, 3, "spi4", spi_src2), > + KER_CLK(RCC_APB2ENR, 12, RCC_D2CCIP1R, 16, 3, "spi1", spi_src1), > + KER_CLK(RCC_APB2ENR, 5, RCC_D2CCIP2R, 3, 3, "usart6", usart_src1), > + KER_CLK(RCC_APB2ENR, 4, RCC_D2CCIP2R, 3, 3, "usart1", usart_src1), > + KER_CLK(RCC_APB4ENR, 21, RCC_D3CCIPR, 24, 3, "sai4b", sai_src), > + KER_CLK(RCC_APB4ENR, 21, RCC_D3CCIPR, 21, 3, "sai4a", sai_src), > + KER_CLK(RCC_APB4ENR, 12, RCC_D3CCIPR, 13, 3, "lptim5", lptim_src2), > + KER_CLK(RCC_APB4ENR, 11, RCC_D3CCIPR, 13, 3, "lptim4", lptim_src2), > + KER_CLK(RCC_APB4ENR, 10, RCC_D3CCIPR, 13, 3, "lptim3", lptim_src2), > + KER_CLK(RCC_APB4ENR, 9, RCC_D3CCIPR, 10, 3, "lptim2", lptim_src2), > + KER_CLK(RCC_APB4ENR, 7, RCC_D3CCIPR, 8, 2, "i2c4", i2c_src2), > + KER_CLK(RCC_APB4ENR, 5, RCC_D3CCIPR, 28, 3, "spi6", spi_src3), > + KER_CLK(RCC_APB4ENR, 3, RCC_D3CCIPR, 0, 3, "lpuart1", lpuart1_src), > +}; > + > +static struct composite_clk_gcfg kernel_clk_cfg = { > + M_CFG_MUX(NULL, 0), > + M_CFG_GATE(NULL, 0), > +}; > + > +/* RTC clock */ > +static u8 rtc_mux_get_parent(struct clk_hw *hw) > +{ > + return clk_mux_ops.get_parent(hw); > +} > + > +static int rtc_mux_set_parent(struct clk_hw *hw, u8 index) > +{ > + int dbp_status; > + int err; > + > + dbp_status = is_enable_power_domain_write_protection(); > + > + if (dbp_status) > + disable_power_domain_write_protection(); > + > + err = clk_mux_ops.set_parent(hw, index); > + > + if (dbp_status) > + enable_power_domain_write_protection(); > + > + return err; > +} > + > +static int rtc_mux_determine_rate(struct clk_hw *hw, > + struct clk_rate_request *req) > +{ > + return clk_mux_ops.determine_rate(hw, req); > +} > + > +static const struct clk_ops rtc_mux_ops = { > + .get_parent = rtc_mux_get_parent, > + .set_parent = rtc_mux_set_parent, > + .determine_rate = rtc_mux_determine_rate, > +}; > + > +/* Clock gate with backup domain protection management */ > +static int bd_gate_enable(struct clk_hw *hw) > +{ > + int dbp_status; > + int err; > + > + dbp_status = is_enable_power_domain_write_protection(); > + > + if (dbp_status) > + disable_power_domain_write_protection(); > + > + err = clk_gate_ops.enable(hw); > + > + if (dbp_status) > + enable_power_domain_write_protection(); > + > + return err; > +} > + > +static void bd_gate_disable(struct clk_hw *hw) > +{ > + clk_gate_ops.disable(hw); > +} > + > +static int bd_gate_is_enabled(struct clk_hw *hw) > +{ > + return clk_gate_ops.is_enabled(hw); > +} > + > +static const struct clk_ops bd_gate_ops = { > + .enable = bd_gate_enable, > + .disable = bd_gate_disable, > + .is_enabled = bd_gate_is_enabled, > +}; > + > +static struct composite_clk_gcfg rtc_clk_cfg = { > + M_CFG_MUX(&rtc_mux_ops, 0), > + M_CFG_GATE(&bd_gate_ops, 0), > +}; > + > +static const struct composite_clk_cfg rtc_clk = > + KER_CLK(RCC_BDCR, 15, RCC_BDCR, 8, 2, "rtc_ck", rtc_src); > + > +/* Micro-controller output clock */ > +static struct composite_clk_gcfg mco_clk_cfg = { > + M_CFG_MUX(NULL, 0), > + M_CFG_DIV(NULL, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO), > +}; > + > +#define M_MCO_F(_name, _parents, _mux_offset, _mux_shift, _mux_width,\ > + _rate_offset, _rate_shift, _rate_width,\ > + _flags)\ > +{\ > + .mux = &(struct muxdiv_cfg) {_mux_offset, _mux_shift, _mux_width },\ > + .div = &(struct muxdiv_cfg) {_rate_offset, _rate_shift, _rate_width},\ > + .gate = NULL,\ > + .name = _name,\ > + .parent_name = _parents,\ > + .num_parents = ARRAY_SIZE(_parents),\ > + .flags = _flags,\ > +} > + > +static const struct composite_clk_cfg mco_clk[] = { > + M_MCO_F("mco1", mco_src1, RCC_CFGR, 22, 4, RCC_CFGR, 18, 4, 0), > + M_MCO_F("mco2", mco_src2, RCC_CFGR, 29, 3, RCC_CFGR, 25, 4, 0), > +}; > + > +static void __init stm32h7_rcc_init(struct device_node *np) > +{ > + struct clk_hw_onecell_data *clk_data; > + struct device_node *node; > + struct composite_cfg c_cfg; > + int n; > + const char *hse_clk, *lse_clk, *i2s_clk; > + > + clk_data = kzalloc(sizeof(*clk_data) + > + sizeof(*clk_data->hws) * STM32H7_MAX_CLKS, > + GFP_KERNEL); > + if (!clk_data) > + return; > + > + clk_data->num = STM32H7_MAX_CLKS; > + > + hws = clk_data->hws; > + > + for (n = 0; n < STM32H7_MAX_CLKS; n++) > + hws[n] = ERR_PTR(-ENOENT); > + > + /* get RCC base @ from DT */ > + base = of_iomap(np, 0); > + if (!base) { > + pr_err("%s: unable to map resource", np->name); > + goto err_free_clks; > + } > + > + pdrm = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); > + if (IS_ERR(pdrm)) { > + pdrm = NULL; > + pr_warn("%s: Unable to get syscfg\n", __func__); > + } > + > + /* Put parent names from DT */ > + hse_clk = of_clk_get_parent_name(np, 0); > + lse_clk = of_clk_get_parent_name(np, 1); > + i2s_clk = of_clk_get_parent_name(np, 2); > + > + sai_src[3] = i2s_clk; > + spi_src1[3] = i2s_clk; > + > + /* Register Internal oscillators */ > + clk_hw_register_fixed_rate(NULL, "clk-hsi", NULL, 0, 64000000); > + clk_hw_register_fixed_rate(NULL, "clk-csi", NULL, 0, 4000000); > + clk_hw_register_fixed_rate(NULL, "clk-lsi", NULL, 0, 32000); > + clk_hw_register_fixed_rate(NULL, "clk-rc48", NULL, 0, 48000); > + > + /* This clock is coming from outside. Frequencies unknown */ > + hws[CK_DSI_PHY] = clk_hw_register_fixed_rate(NULL, "ck_dsi_phy", NULL, > + 0, 0); > + > + hws[HSI_DIV] = clk_hw_register_divider(NULL, "hsidiv", "clk-hsi", 0, > + base + RCC_CR, 3, 2, CLK_DIVIDER_POWER_OF_TWO, > + &rlock); > + > + hws[HSE_1M] = clk_hw_register_divider(NULL, "hse_1M", "hse_ck", 0, > + base + RCC_CFGR, 8, 6, CLK_DIVIDER_ONE_BASED | > + CLK_DIVIDER_ALLOW_ZERO, > + &rlock); > + > + /* Mux system clocks */ > + for (n = 0; n < ARRAY_SIZE(stm32_mclk); n++) > + hws[MCLK_BANK + n] = clk_hw_register_mux(NULL, > + stm32_mclk[n].name, > + stm32_mclk[n].parents, > + stm32_mclk[n].num_parents, > + stm32_mclk[n].flags, > + stm32_mclk[n].offset + base, > + stm32_mclk[n].shift, > + stm32_mclk[n].width, > + 0, > + &rlock); > + > + register_core_and_bus_clocks(); > + > + /* Oscillary clocks */ > + for (n = 0; n < ARRAY_SIZE(stm32_oclk); n++) > + hws[OSC_BANK + n] = clk_register_ready_gate(NULL, > + stm32_oclk[n].name, > + stm32_oclk[n].parent, > + stm32_oclk[n].gate_offset + base, > + stm32_oclk[n].bit_idx, > + stm32_oclk[n].bit_rdy, > + 0, > + stm32_oclk[n].flags, > + &rlock); > + > + hws[HSE_CK] = clk_register_ready_gate(NULL, > + "hse_ck", > + hse_clk, > + RCC_CR + base, > + 16, 17, > + 0, > + 0, > + &rlock); > + > + hws[LSE_CK] = clk_register_ready_gate(NULL, > + "lse_ck", > + lse_clk, > + RCC_BDCR + base, > + 0, 1, > + 1, > + 0, > + &rlock); > + > + hws[CSI_KER_DIV122 + n] = clk_hw_register_fixed_factor(NULL, > + "csi_ker_div122", "csi_ker", 0, 1, 122); > + > + /* PLLs */ > + for_each_compatible_node(node, NULL, "stm32,pll") > + stm32_h7_pll_init(node); > + > + /* Peripheral clocks */ > + for (n = 0; n < ARRAY_SIZE(pclk); n++) > + hws[PERIF_BANK + n] = clk_hw_register_gate(NULL, pclk[n].name, > + pclk[n].parent, > + pclk[n].flags, base + pclk[n].gate_offset, > + pclk[n].bit_idx, pclk[n].flags, &rlock); > + > + /* Kernel clocks */ > + for (n = 0; n < ARRAY_SIZE(kclk); n++) { > + get_cfg_composite_div(&kernel_clk_cfg, &kclk[n], &c_cfg, > + &rlock); > + > + hws[KERN_BANK + n] = clk_hw_register_composite(NULL, > + kclk[n].name, > + kclk[n].parent_name, > + kclk[n].num_parents, > + c_cfg.mux_hw, c_cfg.mux_ops, > + c_cfg.div_hw, c_cfg.div_ops, > + c_cfg.gate_hw, c_cfg.gate_ops, > + kclk[n].flags); > + } > + > + /* RTC clock (default state is off) */ > + clk_hw_register_fixed_rate(NULL, "off", NULL, 0, 0); > + > + get_cfg_composite_div(&rtc_clk_cfg, &rtc_clk, &c_cfg, &rlock); > + > + hws[RTC_CK] = clk_hw_register_composite(NULL, > + rtc_clk.name, > + rtc_clk.parent_name, > + rtc_clk.num_parents, > + c_cfg.mux_hw, c_cfg.mux_ops, > + c_cfg.div_hw, c_cfg.div_ops, > + c_cfg.gate_hw, c_cfg.gate_ops, > + rtc_clk.flags); > + > + /* Micro-controller clocks */ > + for (n = 0; n < ARRAY_SIZE(mco_clk); n++) { > + get_cfg_composite_div(&mco_clk_cfg, &mco_clk[n], &c_cfg, > + &rlock); > + > + hws[MCO_BANK + n] = clk_hw_register_composite(NULL, > + mco_clk[n].name, > + mco_clk[n].parent_name, > + mco_clk[n].num_parents, > + c_cfg.mux_hw, c_cfg.mux_ops, > + c_cfg.div_hw, c_cfg.div_ops, > + c_cfg.gate_hw, c_cfg.gate_ops, > + mco_clk[n].flags); > + } > + > + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); > + > + return; > + > +err_free_clks: > + kfree(clk_data); > +} > +CLK_OF_DECLARE_DRIVER(stm32h7_rcc, "st,stm32h743-rcc", stm32h7_rcc_init); > diff --git a/include/dt-bindings/clock/stm32h7-clks.h b/include/dt-bindings/clock/stm32h7-clks.h > new file mode 100644 > index 0000000..6637272 > --- /dev/null > +++ b/include/dt-bindings/clock/stm32h7-clks.h > @@ -0,0 +1,165 @@ > +/* SYS, CORE AND BUS CLOCKS */ > +#define SYS_D1CPRE 0 > +#define HCLK 1 > +#define PCLK1 2 > +#define PCLK2 3 > +#define PCLK3 4 > +#define PCLK4 5 > +#define HSI_DIV 6 > +#define HSE_1M 7 > +#define I2S_CKIN 8 > +#define CK_DSI_PHY 9 > +#define HSE_CK 10 > +#define LSE_CK 11 > +#define CSI_KER_DIV122 12 > +#define RTC_CK 13 > +#define CPU_SYSTICK 14 > + > +/* OSCILLATOR BANK */ > +#define OSC_BANK 18 > +#define HSI_CK 18 > +#define HSI_KER_CK 19 > +#define CSI_CK 20 > +#define CSI_KER_CK 21 > +#define RC48_CK 22 > +#define LSI_CK 23 > + > +/* MCLOCK BANK */ > +#define MCLK_BANK 28 > +#define PER_CK 28 > +#define PLLSRC 29 > +#define SYS_CK 30 > +#define TRACEIN_CK 31 > + > +/* ODF BANK */ > +#define ODF_BANK 32 > +#define PLL1_P 32 > +#define PLL1_Q 33 > +#define PLL1_R 34 > +#define PLL2_P 35 > +#define PLL2_Q 36 > +#define PLL2_R 37 > +#define PLL3_P 38 > +#define PLL3_Q 39 > +#define PLL3_R 40 > + > +/* MCO BANK */ > +#define MCO_BANK 41 > +#define MCO1 41 > +#define MCO2 42 > + > +/* PERIF BANK */ > +#define PERIF_BANK 50 > +#define D1SRAM1_CK 50 > +#define ITCM_CK 51 > +#define DTCM2_CK 52 > +#define DTCM1_CK 53 > +#define FLITF_CK 54 > +#define JPGDEC_CK 55 > +#define DMA2D_CK 56 > +#define MDMA_CK 57 > +#define USB2ULPI_CK 58 > +#define USB1ULPI_CK 59 > +#define ETH1RX_CK 60 > +#define ETH1TX_CK 61 > +#define ETH1MAC_CK 62 > +#define ART_CK 63 > +#define DMA2_CK 64 > +#define DMA1_CK 65 > +#define D2SRAM3_CK 66 > +#define D2SRAM2_CK 67 > +#define D2SRAM1_CK 68 > +#define HASH_CK 69 > +#define CRYPT_CK 70 > +#define CAMITF_CK 71 > +#define BKPRAM_CK 72 > +#define HSEM_CK 73 > +#define BDMA_CK 74 > +#define CRC_CK 75 > +#define GPIOK_CK 76 > +#define GPIOJ_CK 77 > +#define GPIOI_CK 78 > +#define GPIOH_CK 79 > +#define GPIOG_CK 80 > +#define GPIOF_CK 81 > +#define GPIOE_CK 82 > +#define GPIOD_CK 83 > +#define GPIOC_CK 84 > +#define GPIOB_CK 85 > +#define GPIOA_CK 86 > +#define WWDG1_CK 87 > +#define DAC12_CK 88 > +#define WWDG2_CK 89 > +#define TIM14_CK 90 > +#define TIM13_CK 91 > +#define TIM12_CK 92 > +#define TIM7_CK 93 > +#define TIM6_CK 94 > +#define TIM5_CK 95 > +#define TIM4_CK 96 > +#define TIM3_CK 97 > +#define TIM2_CK 98 > +#define MDIOS_CK 99 > +#define OPAMP_CK 100 > +#define CRS_CK 101 > +#define TIM17_CK 102 > +#define TIM16_CK 103 > +#define TIM15_CK 104 > +#define TIM8_CK 105 > +#define TIM1_CK 106 > +#define TMPSENS_CK 107 > +#define RTCAPB_CK 108 > +#define VREF_CK 109 > +#define COMP12_CK 110 > +#define SYSCFG_CK 111 > + > +/* KERNEL BANK */ > +#define KERN_BANK 120 > +#define SDMMC1_CK 120 > +#define QUADSPI_CK 121 > +#define FMC_CK 122 > +#define USB2OTG_CK 123 > +#define USB1OTG_CK 124 > +#define ADC12_CK 125 > +#define SDMMC2_CK 126 > +#define RNG_CK 127 > +#define ADC3_CK 128 > +#define DSI_CK 129 > +#define LTDC_CK 130 > +#define USART8_CK 131 > +#define USART7_CK 132 > +#define HDMICEC_CK 133 > +#define I2C3_CK 134 > +#define I2C2_CK 135 > +#define I2C1_CK 136 > +#define UART5_CK 137 > +#define UART4_CK 138 > +#define USART3_CK 139 > +#define USART2_CK 140 > +#define SPDIFRX_CK 141 > +#define SPI3_CK 142 > +#define SPI2_CK 143 > +#define LPTIM1_CK 144 > +#define FDCAN_CK 145 > +#define SWP_CK 146 > +#define HRTIM_CK 147 > +#define DFSDM1_CK 148 > +#define SAI3_CK 149 > +#define SAI2_CK 150 > +#define SAI1_CK 151 > +#define SPI5_CK 152 > +#define SPI4_CK 153 > +#define SPI1_CK 154 > +#define USART6_CK 155 > +#define USART1_CK 156 > +#define SAI4B_CK 157 > +#define SAI4A_CK 158 > +#define LPTIM5_CK 159 > +#define LPTIM4_CK 160 > +#define LPTIM3_CK 161 > +#define LPTIM2_CK 162 > +#define I2C4_CK 163 > +#define SPI6_CK 164 > +#define LPUART1_CK 165 > + > +#define STM32H7_MAX_CLKS 166 > diff --git a/include/dt-bindings/mfd/stm32h7-rcc.h b/include/dt-bindings/mfd/stm32h7-rcc.h > new file mode 100644 > index 0000000..b96b3c3 > --- /dev/null > +++ b/include/dt-bindings/mfd/stm32h7-rcc.h > @@ -0,0 +1,138 @@ > +/* > + * This header provides constants for the STM32H7 RCC IP > + */ > + > +#ifndef _DT_BINDINGS_MFD_STM32H7_RCC_H > +#define _DT_BINDINGS_MFD_STM32H7_RCC_H > + > +/* AHB3 */ > +#define STM32H7_RCC_AHB3_MDMA 0 > +#define STM32H7_RCC_AHB3_DMA2D 4 > +#define STM32H7_RCC_AHB3_JPGDEC 5 > +#define STM32H7_RCC_AHB3_FMC 12 > +#define STM32H7_RCC_AHB3_QUADSPI 14 > +#define STM32H7_RCC_AHB3_SDMMC1 16 > +#define STM32H7_RCC_AHB3_CPU1 31 > + > +#define STM32H7_AHB3_RESET(bit) (STM32H7_RCC_AHB3_##bit + (0x7C * 8)) > + > +/* AHB1 */ > +#define STM32H7_RCC_AHB1_DMA1 0 > +#define STM32H7_RCC_AHB1_DMA2 1 > +#define STM32H7_RCC_AHB1_ADC12 5 > +#define STM32H7_RCC_AHB1_ART 14 > +#define STM32H7_RCC_AHB1_ETH1MAC 15 > +#define STM32H7_RCC_AHB1_USB1OTG 25 > +#define STM32H7_RCC_AHB1_USB2OTG 27 > +#define STM32H7_RCC_AHB1_CPU2 31 > + > +#define STM32H7_AHB1_RESET(bit) (STM32H7_RCC_AHB1_##bit + (0x80 * 8)) > + > +/* AHB2 */ > +#define STM32H7_RCC_AHB2_CAMITF 0 > +#define STM32H7_RCC_AHB2_CRYPT 4 > +#define STM32H7_RCC_AHB2_HASH 5 > +#define STM32H7_RCC_AHB2_RNG 6 > +#define STM32H7_RCC_AHB2_SDMMC2 9 > + > +#define STM32H7_AHB2_RESET(bit) (STM32H7_RCC_AHB2_##bit + (0x84 * 8)) > + > +/* AHB4 */ > +#define STM32H7_RCC_AHB4_GPIOA 0 > +#define STM32H7_RCC_AHB4_GPIOB 1 > +#define STM32H7_RCC_AHB4_GPIOC 2 > +#define STM32H7_RCC_AHB4_GPIOD 3 > +#define STM32H7_RCC_AHB4_GPIOE 4 > +#define STM32H7_RCC_AHB4_GPIOF 5 > +#define STM32H7_RCC_AHB4_GPIOG 6 > +#define STM32H7_RCC_AHB4_GPIOH 7 > +#define STM32H7_RCC_AHB4_GPIOI 8 > +#define STM32H7_RCC_AHB4_GPIOJ 9 > +#define STM32H7_RCC_AHB4_GPIOK 10 > +#define STM32H7_RCC_AHB4_CRC 19 > +#define STM32H7_RCC_AHB4_BDMA 21 > +#define STM32H7_RCC_AHB4_ADC3 24 > +#define STM32H7_RCC_AHB4_HSEM 25 > + > +#define STM32H7_AHB4_RESET(bit) (STM32H7_RCC_AHB4_##bit + (0x88 * 8)) > + > + > +/* APB3 */ > +#define STM32H7_RCC_APB3_LTDC 3 > +#define STM32H7_RCC_APB3_DSI 4 > + > +#define STM32H7_APB3_RESET(bit) (STM32H7_RCC_APB3_##bit + (0x8C * 8)) > + > +/* APB1L */ > +#define STM32H7_RCC_APB1L_TIM2 0 > +#define STM32H7_RCC_APB1L_TIM3 1 > +#define STM32H7_RCC_APB1L_TIM4 2 > +#define STM32H7_RCC_APB1L_TIM5 3 > +#define STM32H7_RCC_APB1L_TIM6 4 > +#define STM32H7_RCC_APB1L_TIM7 5 > +#define STM32H7_RCC_APB1L_TIM12 6 > +#define STM32H7_RCC_APB1L_TIM13 7 > +#define STM32H7_RCC_APB1L_TIM14 8 > +#define STM32H7_RCC_APB1L_LPTIM1 9 > +#define STM32H7_RCC_APB1L_SPI2 14 > +#define STM32H7_RCC_APB1L_SPI3 15 > +#define STM32H7_RCC_APB1L_SPDIF_RX 16 > +#define STM32H7_RCC_APB1L_USART2 17 > +#define STM32H7_RCC_APB1L_USART3 18 > +#define STM32H7_RCC_APB1L_UART4 19 > +#define STM32H7_RCC_APB1L_UART5 20 > +#define STM32H7_RCC_APB1L_I2C1 21 > +#define STM32H7_RCC_APB1L_I2C2 22 > +#define STM32H7_RCC_APB1L_I2C3 23 > +#define STM32H7_RCC_APB1L_HDMICEC 27 > +#define STM32H7_RCC_APB1L_DAC12 29 > +#define STM32H7_RCC_APB1L_USART7 30 > +#define STM32H7_RCC_APB1L_USART8 31 > + > +#define STM32H7_APB1L_RESET(bit) (STM32H7_RCC_APB1L_##bit + (0x90 * 8)) > + > +/* APB1H */ > +#define STM32H7_RCC_APB1H_CRS 1 > +#define STM32H7_RCC_APB1H_SWP 2 > +#define STM32H7_RCC_APB1H_OPAMP 4 > +#define STM32H7_RCC_APB1H_MDIOS 5 > +#define STM32H7_RCC_APB1H_FDCAN 8 > + > +#define STM32H7_APB1H_RESET(bit) (STM32H7_RCC_APB1H_##bit + (0x94 * 8)) > + > +/* APB2 */ > +#define STM32H7_RCC_APB2_TIM1 0 > +#define STM32H7_RCC_APB2_TIM8 1 > +#define STM32H7_RCC_APB2_USART1 4 > +#define STM32H7_RCC_APB2_USART6 5 > +#define STM32H7_RCC_APB2_SPI1 12 > +#define STM32H7_RCC_APB2_SPI4 13 > +#define STM32H7_RCC_APB2_TIM15 16 > +#define STM32H7_RCC_APB2_TIM16 17 > +#define STM32H7_RCC_APB2_TIM17 18 > +#define STM32H7_RCC_APB2_SPI5 20 > +#define STM32H7_RCC_APB2_SAI1 22 > +#define STM32H7_RCC_APB2_SAI2 23 > +#define STM32H7_RCC_APB2_SAI3 24 > +#define STM32H7_RCC_APB2_DFSDM1 28 > +#define STM32H7_RCC_APB2_HRTIM 29 > + > +#define STM32H7_APB2_RESET(bit) (STM32H7_RCC_APB2_##bit + (0x98 * 8)) > + > +/* APB4 */ > +#define STM32H7_RCC_APB4_SYSCFG 1 > +#define STM32H7_RCC_APB4_LPUART1 3 > +#define STM32H7_RCC_APB4_SPI6 5 > +#define STM32H7_RCC_APB4_I2C4 7 > +#define STM32H7_RCC_APB4_LPTIM2 9 > +#define STM32H7_RCC_APB4_LPTIM3 10 > +#define STM32H7_RCC_APB4_LPTIM4 11 > +#define STM32H7_RCC_APB4_LPTIM5 12 > +#define STM32H7_RCC_APB4_COMP12 14 > +#define STM32H7_RCC_APB4_VREF 15 > +#define STM32H7_RCC_APB4_SAI4 21 > +#define STM32H7_RCC_APB4_TMPSENS 26 > + > +#define STM32H7_APB4_RESET(bit) (STM32H7_RCC_APB4_##bit + (0x9C * 8)) > + > +#endif /* _DT_BINDINGS_MFD_STM32H7_RCC_H */
On Wed, Mar 15, 2017 at 10:23:30AM +0100, gabriel.fernandez@st.com wrote: > From: Gabriel Fernandez <gabriel.fernandez@st.com> > > This patch enables clocks for STM32H743 boards. > > Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com> > --- > .../devicetree/bindings/clock/st,stm32h7-rcc.txt | 152 ++ > drivers/clk/Makefile | 1 + > drivers/clk/clk-stm32h7.c | 1586 ++++++++++++++++++++ > include/dt-bindings/clock/stm32h7-clks.h | 165 ++ > include/dt-bindings/mfd/stm32h7-rcc.h | 138 ++ > 5 files changed, 2042 insertions(+) > create mode 100644 Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt > create mode 100644 drivers/clk/clk-stm32h7.c > create mode 100644 include/dt-bindings/clock/stm32h7-clks.h > create mode 100644 include/dt-bindings/mfd/stm32h7-rcc.h > > diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt > new file mode 100644 > index 0000000..9d4b587 > --- /dev/null > +++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt > @@ -0,0 +1,152 @@ > +STMicroelectronics STM32H7 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,stm32h743-rcc" > + > +- reg: should be register base and length as documented in the > + datasheet > + > +- #reset-cells: 1, see below > + > +- #clock-cells : from common clock binding; shall be set to 1 > + > +- clocks: External oscillator clock phandle > + - high speed external clock signal (HSE) > + - low speed external clock signal (LSE) > + - external I2S clock (I2S_CKIN) > + > +- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain > + write protection (RTC clock). > + > +- pll x node: Allow to register a pll with specific parameters. > + Please see PLL section below. > + > +Example: > + > + rcc: rcc@58024400 { > + #reset-cells = <1>; > + #clock-cells = <2> > + compatible = "st,stm32h743-rcc", "st,stm32-rcc"; > + reg = <0x58024400 0x400>; > + clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>; > + > + st,syscfg = <&pwrcfg>; > + > + #address-cells = <1>; > + #size-cells = <0>; > + > + vco1@58024430 { > + #clock-cells = <0>; > + compatible = "stm32,pll"; > + reg = <0>; The reg addr value and unit address don't match. > + }; > + > + vco2@58024438 { > + #clock-cells = <0>; > + compatible = "stm32,pll"; > + reg = <1>; > + st,clock-div = <2>; > + st,clock-mult = <40>; > + st,frac-status = <0>; > + st,frac = <0>; > + st,vcosel = <1>; > + st,pllrge = <2>; > + }; > + }; > + > + > +STM32H7 PLL > +----------- > + > +The VCO of STM32 PLL could be reprensented like this: > + > + Vref --------- -------- > + ---->| / DIVM |---->| x DIVN | ------> VCO > + --------- -------- > + ^ > + | > + ------- > + | FRACN | > + ------- > + > +When the PLL is configured in integer mode: > +- VCO = ( Vref / DIVM ) * DIVN > + > +When the PLL is configured in fractional mode: > +- VCO = ( Vref / DIVM ) * ( DIVN + FRACN / 2^13) > + > + > +Required properties for pll node: > +- compatible: Should be: > + "stm32,pll" Only 1 single PLL design for all STM32 chips ever? > + > +- #clock-cells: from common clock binding; shall be set to 0 > +- reg: Should be the pll number. > + > +Optional properties: > +- st,clock-div: DIVM division factor : <1..63> > +- st,clock-mult: DIVN multiplication factor : <4..512> > + > +- st,frac-status: > + - 0 Pll is configured in integer mode > + - 1 Pll is configure in fractional mode Isn't this implied by the presence of the next property? > + > +- st,frac: Fractional part of the multiplication factor : <0..8191> > + > +- st,vcosel: VCO selection > + - 0: Wide VCO range:192 to 836 MHz > + - 1: Medium VCO range:150 to 420 MHz > + > +- st,pllrge: PLL input frequency range > + - 0: The PLL input (Vref / DIVM) clock range frequency is between 1 and 2 MHz > + - 1: The PLL input (Vref / DIVM) clock range frequency is between 2 and 4 MHz > + - 2: The PLL input (Vref / DIVM) clock range frequency is between 4 and 8 MHz > + - 3: The PLL input (Vref / DIVM) clock range frequency is between 8 and 16 MHz Seems like you could figure this out based on the input freq and other properties. > + > + > +The peripheral clock consumer should specify the desired clock by > +having the clock ID in its "clocks" phandle cell. > + > +All available clocks are defined as preprocessor macros in > +dt-bindings/clock/stm32h7-clks.h header and can be used in device > +tree sources. > + > +Example: > + > + timer5: timer@40000c00 { > + compatible = "st,stm32-timer"; > + reg = <0x40000c00 0x400>; > + interrupts = <50>; > + clocks = <&rcc TIM5_CK>; > + > + }; > + > +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 = AHB4RSTR_offset / 4 * 32 + CRCRST_bit_offset = 0x88 / 4 * 32 + 19 = 1107 > + > +All available preprocessor macros for reset are defined dt-bindings//mfd/stm32h7-rcc.h > +header and can be used in device tree sources. > + > +example: > + > + timer2 { > + resets = <&rcc STM32H7_APB1L_RESET(TIM2)>; > + }; -- To unsubscribe from this list: send the line "unsubscribe linux-clk" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Rob, Thanks for reviewing On 03/24/2017 03:06 AM, Rob Herring wrote: > On Wed, Mar 15, 2017 at 10:23:30AM +0100, gabriel.fernandez@st.com wrote: >> From: Gabriel Fernandez <gabriel.fernandez@st.com> >> >> This patch enables clocks for STM32H743 boards. >> >> Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com> >> --- >> .../devicetree/bindings/clock/st,stm32h7-rcc.txt | 152 ++ >> drivers/clk/Makefile | 1 + >> drivers/clk/clk-stm32h7.c | 1586 ++++++++++++++++++++ >> include/dt-bindings/clock/stm32h7-clks.h | 165 ++ >> include/dt-bindings/mfd/stm32h7-rcc.h | 138 ++ >> 5 files changed, 2042 insertions(+) >> create mode 100644 Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >> create mode 100644 drivers/clk/clk-stm32h7.c >> create mode 100644 include/dt-bindings/clock/stm32h7-clks.h >> create mode 100644 include/dt-bindings/mfd/stm32h7-rcc.h >> >> diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >> new file mode 100644 >> index 0000000..9d4b587 >> --- /dev/null >> +++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >> @@ -0,0 +1,152 @@ >> +STMicroelectronics STM32H7 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,stm32h743-rcc" >> + >> +- reg: should be register base and length as documented in the >> + datasheet >> + >> +- #reset-cells: 1, see below >> + >> +- #clock-cells : from common clock binding; shall be set to 1 >> + >> +- clocks: External oscillator clock phandle >> + - high speed external clock signal (HSE) >> + - low speed external clock signal (LSE) >> + - external I2S clock (I2S_CKIN) >> + >> +- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain >> + write protection (RTC clock). >> + >> +- pll x node: Allow to register a pll with specific parameters. >> + Please see PLL section below. >> + >> +Example: >> + >> + rcc: rcc@58024400 { >> + #reset-cells = <1>; >> + #clock-cells = <2> >> + compatible = "st,stm32h743-rcc", "st,stm32-rcc"; >> + reg = <0x58024400 0x400>; >> + clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>; >> + >> + st,syscfg = <&pwrcfg>; >> + >> + #address-cells = <1>; >> + #size-cells = <0>; >> + >> + vco1@58024430 { >> + #clock-cells = <0>; >> + compatible = "stm32,pll"; >> + reg = <0>; > The reg addr value and unit address don't match. ok il will change into vco1@0 { reg = <0>; > >> + }; >> + >> + vco2@58024438 { >> + #clock-cells = <0>; >> + compatible = "stm32,pll"; >> + reg = <1>; >> + st,clock-div = <2>; >> + st,clock-mult = <40>; >> + st,frac-status = <0>; >> + st,frac = <0>; >> + st,vcosel = <1>; >> + st,pllrge = <2>; >> + }; >> + }; >> + >> + >> +STM32H7 PLL >> +----------- >> + >> +The VCO of STM32 PLL could be reprensented like this: >> + >> + Vref --------- -------- >> + ---->| / DIVM |---->| x DIVN | ------> VCO >> + --------- -------- >> + ^ >> + | >> + ------- >> + | FRACN | >> + ------- >> + >> +When the PLL is configured in integer mode: >> +- VCO = ( Vref / DIVM ) * DIVN >> + >> +When the PLL is configured in fractional mode: >> +- VCO = ( Vref / DIVM ) * ( DIVN + FRACN / 2^13) >> + >> + >> +Required properties for pll node: >> +- compatible: Should be: >> + "stm32,pll" > Only 1 single PLL design for all STM32 chips ever? no, i can change into "stm32h7,pll" >> + >> +- #clock-cells: from common clock binding; shall be set to 0 >> +- reg: Should be the pll number. >> + >> +Optional properties: >> +- st,clock-div: DIVM division factor : <1..63> >> +- st,clock-mult: DIVN multiplication factor : <4..512> >> + >> +- st,frac-status: >> + - 0 Pll is configured in integer mode >> + - 1 Pll is configure in fractional mode > Isn't this implied by the presence of the next property? do you prefer this ? - st,frac : 0 : pll is configured in integer mode 1..8191 : Fractional part of the multiplication factor and pll is configured in fractional mode >> + >> +- st,frac: Fractional part of the multiplication factor : <0..8191> >> + >> +- st,vcosel: VCO selection >> + - 0: Wide VCO range:192 to 836 MHz >> + - 1: Medium VCO range:150 to 420 MHz >> + >> +- st,pllrge: PLL input frequency range >> + - 0: The PLL input (Vref / DIVM) clock range frequency is between 1 and 2 MHz >> + - 1: The PLL input (Vref / DIVM) clock range frequency is between 2 and 4 MHz >> + - 2: The PLL input (Vref / DIVM) clock range frequency is between 4 and 8 MHz >> + - 3: The PLL input (Vref / DIVM) clock range frequency is between 8 and 16 MHz > Seems like you could figure this out based on the input freq and other > properties. ok i will try to integrate pllrge configuration in the code. > >> + >> + >> +The peripheral clock consumer should specify the desired clock by >> +having the clock ID in its "clocks" phandle cell. >> + >> +All available clocks are defined as preprocessor macros in >> +dt-bindings/clock/stm32h7-clks.h header and can be used in device >> +tree sources. >> + >> +Example: >> + >> + timer5: timer@40000c00 { >> + compatible = "st,stm32-timer"; >> + reg = <0x40000c00 0x400>; >> + interrupts = <50>; >> + clocks = <&rcc TIM5_CK>; >> + >> + }; >> + >> +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 = AHB4RSTR_offset / 4 * 32 + CRCRST_bit_offset = 0x88 / 4 * 32 + 19 = 1107 >> + >> +All available preprocessor macros for reset are defined dt-bindings//mfd/stm32h7-rcc.h >> +header and can be used in device tree sources. >> + >> +example: >> + >> + timer2 { >> + resets = <&rcc STM32H7_APB1L_RESET(TIM2)>; >> + }; -- To unsubscribe from this list: send the line "unsubscribe linux-clk" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Fri, Mar 24, 2017 at 4:41 AM, Gabriel Fernandez <gabriel.fernandez@st.com> wrote: > Hi Rob, > > Thanks for reviewing > > > > On 03/24/2017 03:06 AM, Rob Herring wrote: >> >> On Wed, Mar 15, 2017 at 10:23:30AM +0100, gabriel.fernandez@st.com wrote: >>> >>> From: Gabriel Fernandez <gabriel.fernandez@st.com> >>> >>> This patch enables clocks for STM32H743 boards. >>> >>> Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com> >>> --- >>> .../devicetree/bindings/clock/st,stm32h7-rcc.txt | 152 ++ >>> drivers/clk/Makefile | 1 + >>> drivers/clk/clk-stm32h7.c | 1586 >>> ++++++++++++++++++++ >>> include/dt-bindings/clock/stm32h7-clks.h | 165 ++ >>> include/dt-bindings/mfd/stm32h7-rcc.h | 138 ++ >>> 5 files changed, 2042 insertions(+) >>> create mode 100644 >>> Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>> create mode 100644 drivers/clk/clk-stm32h7.c >>> create mode 100644 include/dt-bindings/clock/stm32h7-clks.h >>> create mode 100644 include/dt-bindings/mfd/stm32h7-rcc.h >>> >>> diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>> b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>> new file mode 100644 >>> index 0000000..9d4b587 >>> --- /dev/null >>> +++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>> @@ -0,0 +1,152 @@ >>> +STMicroelectronics STM32H7 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,stm32h743-rcc" >>> + >>> +- reg: should be register base and length as documented in the >>> + datasheet >>> + >>> +- #reset-cells: 1, see below >>> + >>> +- #clock-cells : from common clock binding; shall be set to 1 >>> + >>> +- clocks: External oscillator clock phandle >>> + - high speed external clock signal (HSE) >>> + - low speed external clock signal (LSE) >>> + - external I2S clock (I2S_CKIN) >>> + >>> +- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup >>> domain >>> + write protection (RTC clock). >>> + >>> +- pll x node: Allow to register a pll with specific parameters. >>> + Please see PLL section below. >>> + >>> +Example: >>> + >>> + rcc: rcc@58024400 { >>> + #reset-cells = <1>; >>> + #clock-cells = <2> >>> + compatible = "st,stm32h743-rcc", "st,stm32-rcc"; >>> + reg = <0x58024400 0x400>; >>> + clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>; >>> + >>> + st,syscfg = <&pwrcfg>; >>> + >>> + #address-cells = <1>; >>> + #size-cells = <0>; >>> + >>> + vco1@58024430 { >>> + #clock-cells = <0>; >>> + compatible = "stm32,pll"; >>> + reg = <0>; >> >> The reg addr value and unit address don't match. > > ok il will change into > > vco1@0 { > reg = <0>; > > >> >>> + }; >>> + >>> + vco2@58024438 { >>> + #clock-cells = <0>; >>> + compatible = "stm32,pll"; >>> + reg = <1>; >>> + st,clock-div = <2>; >>> + st,clock-mult = <40>; >>> + st,frac-status = <0>; >>> + st,frac = <0>; >>> + st,vcosel = <1>; >>> + st,pllrge = <2>; >>> + }; >>> + }; >>> + >>> + >>> +STM32H7 PLL >>> +----------- >>> + >>> +The VCO of STM32 PLL could be reprensented like this: >>> + >>> + Vref --------- -------- >>> + ---->| / DIVM |---->| x DIVN | ------> VCO >>> + --------- -------- >>> + ^ >>> + | >>> + ------- >>> + | FRACN | >>> + ------- >>> + >>> +When the PLL is configured in integer mode: >>> +- VCO = ( Vref / DIVM ) * DIVN >>> + >>> +When the PLL is configured in fractional mode: >>> +- VCO = ( Vref / DIVM ) * ( DIVN + FRACN / 2^13) >>> + >>> + >>> +Required properties for pll node: >>> +- compatible: Should be: >>> + "stm32,pll" >> >> Only 1 single PLL design for all STM32 chips ever? > > no, i can change into "stm32h7,pll" Actually, should be "st,stm32h7-pll". > >>> + >>> +- #clock-cells: from common clock binding; shall be set to 0 >>> +- reg: Should be the pll number. >>> + >>> +Optional properties: >>> +- st,clock-div: DIVM division factor : <1..63> >>> +- st,clock-mult: DIVN multiplication factor : <4..512> >>> + >>> +- st,frac-status: >>> + - 0 Pll is configured in integer mode >>> + - 1 Pll is configure in fractional mode >> >> Isn't this implied by the presence of the next property? > > do you prefer this ? > > - st,frac : > 0 : pll is configured in integer mode > 1..8191 : Fractional part of the multiplication factor and pll is configured > in fractional mode Why not no st,frac property means integer mode? Rob -- To unsubscribe from this list: send the line "unsubscribe linux-clk" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 03/27/2017 09:04 PM, Rob Herring wrote: > On Fri, Mar 24, 2017 at 4:41 AM, Gabriel Fernandez > <gabriel.fernandez@st.com> wrote: >> Hi Rob, >> >> Thanks for reviewing >> >> >> >> On 03/24/2017 03:06 AM, Rob Herring wrote: >>> On Wed, Mar 15, 2017 at 10:23:30AM +0100, gabriel.fernandez@st.com wrote: >>>> From: Gabriel Fernandez <gabriel.fernandez@st.com> >>>> >>>> This patch enables clocks for STM32H743 boards. >>>> >>>> Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com> >>>> --- >>>> .../devicetree/bindings/clock/st,stm32h7-rcc.txt | 152 ++ >>>> drivers/clk/Makefile | 1 + >>>> drivers/clk/clk-stm32h7.c | 1586 >>>> ++++++++++++++++++++ >>>> include/dt-bindings/clock/stm32h7-clks.h | 165 ++ >>>> include/dt-bindings/mfd/stm32h7-rcc.h | 138 ++ >>>> 5 files changed, 2042 insertions(+) >>>> create mode 100644 >>>> Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>>> create mode 100644 drivers/clk/clk-stm32h7.c >>>> create mode 100644 include/dt-bindings/clock/stm32h7-clks.h >>>> create mode 100644 include/dt-bindings/mfd/stm32h7-rcc.h >>>> >>>> diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>>> b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>>> new file mode 100644 >>>> index 0000000..9d4b587 >>>> --- /dev/null >>>> +++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>>> @@ -0,0 +1,152 @@ >>>> +STMicroelectronics STM32H7 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,stm32h743-rcc" >>>> + >>>> +- reg: should be register base and length as documented in the >>>> + datasheet >>>> + >>>> +- #reset-cells: 1, see below >>>> + >>>> +- #clock-cells : from common clock binding; shall be set to 1 >>>> + >>>> +- clocks: External oscillator clock phandle >>>> + - high speed external clock signal (HSE) >>>> + - low speed external clock signal (LSE) >>>> + - external I2S clock (I2S_CKIN) >>>> + >>>> +- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup >>>> domain >>>> + write protection (RTC clock). >>>> + >>>> +- pll x node: Allow to register a pll with specific parameters. >>>> + Please see PLL section below. >>>> + >>>> +Example: >>>> + >>>> + rcc: rcc@58024400 { >>>> + #reset-cells = <1>; >>>> + #clock-cells = <2> >>>> + compatible = "st,stm32h743-rcc", "st,stm32-rcc"; >>>> + reg = <0x58024400 0x400>; >>>> + clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>; >>>> + >>>> + st,syscfg = <&pwrcfg>; >>>> + >>>> + #address-cells = <1>; >>>> + #size-cells = <0>; >>>> + >>>> + vco1@58024430 { >>>> + #clock-cells = <0>; >>>> + compatible = "stm32,pll"; >>>> + reg = <0>; >>> The reg addr value and unit address don't match. >> ok il will change into >> >> vco1@0 { >> reg = <0>; >> >> >>>> + }; >>>> + >>>> + vco2@58024438 { >>>> + #clock-cells = <0>; >>>> + compatible = "stm32,pll"; >>>> + reg = <1>; >>>> + st,clock-div = <2>; >>>> + st,clock-mult = <40>; >>>> + st,frac-status = <0>; >>>> + st,frac = <0>; >>>> + st,vcosel = <1>; >>>> + st,pllrge = <2>; >>>> + }; >>>> + }; >>>> + >>>> + >>>> +STM32H7 PLL >>>> +----------- >>>> + >>>> +The VCO of STM32 PLL could be reprensented like this: >>>> + >>>> + Vref --------- -------- >>>> + ---->| / DIVM |---->| x DIVN | ------> VCO >>>> + --------- -------- >>>> + ^ >>>> + | >>>> + ------- >>>> + | FRACN | >>>> + ------- >>>> + >>>> +When the PLL is configured in integer mode: >>>> +- VCO = ( Vref / DIVM ) * DIVN >>>> + >>>> +When the PLL is configured in fractional mode: >>>> +- VCO = ( Vref / DIVM ) * ( DIVN + FRACN / 2^13) >>>> + >>>> + >>>> +Required properties for pll node: >>>> +- compatible: Should be: >>>> + "stm32,pll" >>> Only 1 single PLL design for all STM32 chips ever? >> no, i can change into "stm32h7,pll" > Actually, should be "st,stm32h7-pll". ok >>>> + >>>> +- #clock-cells: from common clock binding; shall be set to 0 >>>> +- reg: Should be the pll number. >>>> + >>>> +Optional properties: >>>> +- st,clock-div: DIVM division factor : <1..63> >>>> +- st,clock-mult: DIVN multiplication factor : <4..512> >>>> + >>>> +- st,frac-status: >>>> + - 0 Pll is configured in integer mode >>>> + - 1 Pll is configure in fractional mode >>> Isn't this implied by the presence of the next property? >> do you prefer this ? >> >> - st,frac : >> 0 : pll is configured in integer mode >> 1..8191 : Fractional part of the multiplication factor and pll is configured >> in fractional mode > Why not no st,frac property means integer mode? No no st,frac property means no change (keep default values from bootloader) Best Regards Gabriel > Rob -- To unsubscribe from this list: send the line "unsubscribe linux-clk" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, Mar 28, 2017 at 1:20 AM, Gabriel Fernandez <gabriel.fernandez@st.com> wrote: > > > On 03/27/2017 09:04 PM, Rob Herring wrote: >> >> On Fri, Mar 24, 2017 at 4:41 AM, Gabriel Fernandez >> <gabriel.fernandez@st.com> wrote: >>> >>> Hi Rob, >>> >>> Thanks for reviewing >>> >>> >>> >>> On 03/24/2017 03:06 AM, Rob Herring wrote: >>>> >>>> On Wed, Mar 15, 2017 at 10:23:30AM +0100, gabriel.fernandez@st.com >>>> wrote: >>>>> >>>>> From: Gabriel Fernandez <gabriel.fernandez@st.com> >>>>> >>>>> This patch enables clocks for STM32H743 boards. >>>>> >>>>> Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com> >>>>> --- >>>>> .../devicetree/bindings/clock/st,stm32h7-rcc.txt | 152 ++ >>>>> drivers/clk/Makefile | 1 + >>>>> drivers/clk/clk-stm32h7.c | 1586 >>>>> ++++++++++++++++++++ >>>>> include/dt-bindings/clock/stm32h7-clks.h | 165 ++ >>>>> include/dt-bindings/mfd/stm32h7-rcc.h | 138 ++ >>>>> 5 files changed, 2042 insertions(+) >>>>> create mode 100644 >>>>> Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>>>> create mode 100644 drivers/clk/clk-stm32h7.c >>>>> create mode 100644 include/dt-bindings/clock/stm32h7-clks.h >>>>> create mode 100644 include/dt-bindings/mfd/stm32h7-rcc.h >>>>> >>>>> diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>>>> b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>>>> new file mode 100644 >>>>> index 0000000..9d4b587 >>>>> --- /dev/null >>>>> +++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>>>> @@ -0,0 +1,152 @@ >>>>> +STMicroelectronics STM32H7 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,stm32h743-rcc" >>>>> + >>>>> +- reg: should be register base and length as documented in the >>>>> + datasheet >>>>> + >>>>> +- #reset-cells: 1, see below >>>>> + >>>>> +- #clock-cells : from common clock binding; shall be set to 1 >>>>> + >>>>> +- clocks: External oscillator clock phandle >>>>> + - high speed external clock signal (HSE) >>>>> + - low speed external clock signal (LSE) >>>>> + - external I2S clock (I2S_CKIN) >>>>> + >>>>> +- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup >>>>> domain >>>>> + write protection (RTC clock). >>>>> + >>>>> +- pll x node: Allow to register a pll with specific parameters. >>>>> + Please see PLL section below. >>>>> + >>>>> +Example: >>>>> + >>>>> + rcc: rcc@58024400 { >>>>> + #reset-cells = <1>; >>>>> + #clock-cells = <2> >>>>> + compatible = "st,stm32h743-rcc", "st,stm32-rcc"; >>>>> + reg = <0x58024400 0x400>; >>>>> + clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>; >>>>> + >>>>> + st,syscfg = <&pwrcfg>; >>>>> + >>>>> + #address-cells = <1>; >>>>> + #size-cells = <0>; >>>>> + >>>>> + vco1@58024430 { >>>>> + #clock-cells = <0>; >>>>> + compatible = "stm32,pll"; >>>>> + reg = <0>; >>>> >>>> The reg addr value and unit address don't match. >>> >>> ok il will change into >>> >>> vco1@0 { >>> reg = <0>; >>> >>> >>>>> + }; >>>>> + >>>>> + vco2@58024438 { >>>>> + #clock-cells = <0>; >>>>> + compatible = "stm32,pll"; >>>>> + reg = <1>; >>>>> + st,clock-div = <2>; >>>>> + st,clock-mult = <40>; >>>>> + st,frac-status = <0>; >>>>> + st,frac = <0>; >>>>> + st,vcosel = <1>; >>>>> + st,pllrge = <2>; >>>>> + }; >>>>> + }; >>>>> + >>>>> + >>>>> +STM32H7 PLL >>>>> +----------- >>>>> + >>>>> +The VCO of STM32 PLL could be reprensented like this: >>>>> + >>>>> + Vref --------- -------- >>>>> + ---->| / DIVM |---->| x DIVN | ------> VCO >>>>> + --------- -------- >>>>> + ^ >>>>> + | >>>>> + ------- >>>>> + | FRACN | >>>>> + ------- >>>>> + >>>>> +When the PLL is configured in integer mode: >>>>> +- VCO = ( Vref / DIVM ) * DIVN >>>>> + >>>>> +When the PLL is configured in fractional mode: >>>>> +- VCO = ( Vref / DIVM ) * ( DIVN + FRACN / 2^13) >>>>> + >>>>> + >>>>> +Required properties for pll node: >>>>> +- compatible: Should be: >>>>> + "stm32,pll" >>>> >>>> Only 1 single PLL design for all STM32 chips ever? >>> >>> no, i can change into "stm32h7,pll" >> >> Actually, should be "st,stm32h7-pll". > > ok > >>>>> + >>>>> +- #clock-cells: from common clock binding; shall be set to 0 >>>>> +- reg: Should be the pll number. >>>>> + >>>>> +Optional properties: >>>>> +- st,clock-div: DIVM division factor : <1..63> >>>>> +- st,clock-mult: DIVN multiplication factor : <4..512> >>>>> + >>>>> +- st,frac-status: >>>>> + - 0 Pll is configured in integer mode >>>>> + - 1 Pll is configure in fractional mode >>>> >>>> Isn't this implied by the presence of the next property? >>> >>> do you prefer this ? >>> >>> - st,frac : >>> 0 : pll is configured in integer mode >>> 1..8191 : Fractional part of the multiplication factor and pll is >>> configured >>> in fractional mode >> >> Why not no st,frac property means integer mode? > > No > > no st,frac property means no change (keep default values from bootloader) Okay, then 0 for integer mode is fine. Also state what the default means. Rob -- To unsubscribe from this list: send the line "unsubscribe linux-clk" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 03/15, gabriel.fernandez@st.com wrote: > From: Gabriel Fernandez <gabriel.fernandez@st.com> > > This patch enables clocks for STM32H743 boards. Like what clocks exactly? All of them? > diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt > new file mode 100644 > index 0000000..9d4b587 > --- /dev/null > +++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt > @@ -0,0 +1,152 @@ > +STMicroelectronics STM32H7 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,stm32h743-rcc" > + > +- reg: should be register base and length as documented in the > + datasheet > + > +- #reset-cells: 1, see below > + > +- #clock-cells : from common clock binding; shall be set to 1 > + > +- clocks: External oscillator clock phandle > + - high speed external clock signal (HSE) > + - low speed external clock signal (LSE) > + - external I2S clock (I2S_CKIN) > + > +- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain > + write protection (RTC clock). > + > +- pll x node: Allow to register a pll with specific parameters. > + Please see PLL section below. > + > +Example: > + > + rcc: rcc@58024400 { > + #reset-cells = <1>; > + #clock-cells = <2> > + compatible = "st,stm32h743-rcc", "st,stm32-rcc"; > + reg = <0x58024400 0x400>; > + clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>; > + > + st,syscfg = <&pwrcfg>; > + > + #address-cells = <1>; > + #size-cells = <0>; > + > + vco1@58024430 { > + #clock-cells = <0>; > + compatible = "stm32,pll"; > + reg = <0>; reg is super confusing and doesn't match unit address. > + }; Why? Shouldn't we know this from the compatible string how many PLLs there are and where they're located? Export the PLLs through rcc node's clock-cells? > + > + vco2@58024438 { > + #clock-cells = <0>; > + compatible = "stm32,pll"; > + reg = <1>; reg is super confusing and doesn't match unit address. > + st,clock-div = <2>; > + st,clock-mult = <40>; > + st,frac-status = <0>; > + st,frac = <0>; > + st,vcosel = <1>; > + st,pllrge = <2>; Does this stuff change on a per-board basis? I hope none of these properties need to be in DT. > + }; > + }; > + > + > +STM32H7 PLL > +----------- > + [...] > + > +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 = AHB4RSTR_offset / 4 * 32 + CRCRST_bit_offset = 0x88 / 4 * 32 + 19 = 1107 > + > +All available preprocessor macros for reset are defined dt-bindings//mfd/stm32h7-rcc.h One too many slashes? > +header and can be used in device tree sources. > + > +example: > + > + timer2 { > + resets = <&rcc STM32H7_APB1L_RESET(TIM2)>; > + }; > diff --git a/drivers/clk/clk-stm32h7.c b/drivers/clk/clk-stm32h7.c > new file mode 100644 > index 0000000..c8eb729 > --- /dev/null > +++ b/drivers/clk/clk-stm32h7.c > @@ -0,0 +1,1586 @@ > +/* > + * Copyright (C) Gabriel Fernandez 2017 > + * Author: Gabriel Fernandez <gabriel.fernandez@st.com> > + * > + * License terms: GPL V2.0. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope it will be useful, but WITHOUT > + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or > + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for > + * more details. > + * > + * You should have received a copy of the GNU General Public License along with > + * this program. If not, see <http://www.gnu.org/licenses/>. > + */ > + > +#include <linux/clk.h> Is this used? > +#include <linux/clk-provider.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/mfd/syscon.h> > +#include <linux/of.h> > +#include <linux/of_address.h> > +#include <linux/slab.h> > +#include <linux/spinlock.h> > +#include <linux/regmap.h> > + > +#include <dt-bindings/clock/stm32h7-clks.h> > + > +/* Reset Clock Control Registers */ > +#define RCC_CR 0x00 > +#define RCC_CFGR 0x10 > +#define RCC_D1CFGR 0x18 > +#define RCC_D2CFGR 0x1C > +#define RCC_D3CFGR 0x20 > +#define RCC_PLLCKSELR 0x28 > +#define RCC_PLLCFGR 0x2C > +#define RCC_PLL1DIVR 0x30 > +#define RCC_PLL1FRACR 0x34 > +#define RCC_PLL2DIVR 0x38 > +#define RCC_PLL2FRACR 0x3C > +#define RCC_PLL3DIVR 0x40 > +#define RCC_PLL3FRACR 0x44 > +#define RCC_D1CCIPR 0x4C > +#define RCC_D2CCIP1R 0x50 > +#define RCC_D2CCIP2R 0x54 > +#define RCC_D3CCIPR 0x58 > +#define RCC_BDCR 0x70 > +#define RCC_CSR 0x74 > +#define RCC_AHB3ENR 0xD4 > +#define RCC_AHB1ENR 0xD8 > +#define RCC_AHB2ENR 0xDC > +#define RCC_AHB4ENR 0xE0 > +#define RCC_APB3ENR 0xE4 > +#define RCC_APB1LENR 0xE8 > +#define RCC_APB1HENR 0xEC > +#define RCC_APB2ENR 0xF0 > +#define RCC_APB4ENR 0xF4 > + > +static DEFINE_SPINLOCK(rlock); This is super generic and will make lockdep debugging sad. Perhaps stm32rcc_lock? > + > +static void __iomem *base; > +static struct regmap *pdrm; > +static struct clk_hw **hws; > + > +/* System clock parent */ > +static const char * const sys_src[] = { > + "hsi_ck", "csi_ck", "hse_ck", "pll1_p" }; > + > +static const char * const tracein_src[] = { > + "hsi_ck", "csi_ck", "hse_ck", "pll1_r" }; [...] > + > +static unsigned long pll_fd_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + struct stm32_pll_obj *clk_elem = to_pll(hw); > + struct stm32_fractional_divider *fd = &clk_elem->div; > + unsigned long m, n; > + u32 val, mask; > + u64 rate, rate1 = 0; > + > + val = clk_readl(fd->mreg); Please don't use clk_readl() unless you need it for some reason. > + mask = (GENMASK(fd->mwidth - 1, 0) << fd->mshift); > + m = (val & mask) >> fd->mshift; > + > + val = clk_readl(fd->nreg); > + mask = (GENMASK(fd->nwidth - 1, 0) << fd->nshift); Useless parentheses. And isn't GENMASK supposed to take the actual bit positions? Then we avoid overflow issues? > + n = ((val & mask) >> fd->nshift) + 1; > + > + if (!n || !m) > + return parent_rate; > + > + rate = (u64)parent_rate * n; > + do_div(rate, m); > + > + if (pll_frac_is_enabled(hw)) { > + val = pll_read_frac(hw); > + rate1 = (u64) parent_rate * (u64) val; > + do_div(rate1, (m * 8191)); > + } > + > + return rate + rate1; > +} > + [...] > + > + /* Micro-controller clocks */ > + for (n = 0; n < ARRAY_SIZE(mco_clk); n++) { > + get_cfg_composite_div(&mco_clk_cfg, &mco_clk[n], &c_cfg, > + &rlock); > + > + hws[MCO_BANK + n] = clk_hw_register_composite(NULL, > + mco_clk[n].name, > + mco_clk[n].parent_name, > + mco_clk[n].num_parents, > + c_cfg.mux_hw, c_cfg.mux_ops, > + c_cfg.div_hw, c_cfg.div_ops, > + c_cfg.gate_hw, c_cfg.gate_ops, > + mco_clk[n].flags); > + } > + > + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); > + > + return; > + > +err_free_clks: > + kfree(clk_data); > +} > +CLK_OF_DECLARE_DRIVER(stm32h7_rcc, "st,stm32h743-rcc", stm32h7_rcc_init); Is there another driver that uses the same register space? Nothing showing up in -next right now. Perhaps a comment should be added to indicate the other driver.
Hi Stephen, On 04/06/2017 12:32 AM, Stephen Boyd wrote: > On 03/15, gabriel.fernandez@st.com wrote: >> From: Gabriel Fernandez <gabriel.fernandez@st.com> >> >> This patch enables clocks for STM32H743 boards. > Like what clocks exactly? All of them? > Yes all of them, it's new IP >> diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >> new file mode 100644 >> index 0000000..9d4b587 >> --- /dev/null >> +++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >> @@ -0,0 +1,152 @@ >> +STMicroelectronics STM32H7 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,stm32h743-rcc" >> + >> +- reg: should be register base and length as documented in the >> + datasheet >> + >> +- #reset-cells: 1, see below >> + >> +- #clock-cells : from common clock binding; shall be set to 1 >> + >> +- clocks: External oscillator clock phandle >> + - high speed external clock signal (HSE) >> + - low speed external clock signal (LSE) >> + - external I2S clock (I2S_CKIN) >> + >> +- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain >> + write protection (RTC clock). >> + >> +- pll x node: Allow to register a pll with specific parameters. >> + Please see PLL section below. >> + >> +Example: >> + >> + rcc: rcc@58024400 { >> + #reset-cells = <1>; >> + #clock-cells = <2> >> + compatible = "st,stm32h743-rcc", "st,stm32-rcc"; >> + reg = <0x58024400 0x400>; >> + clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>; >> + >> + st,syscfg = <&pwrcfg>; >> + >> + #address-cells = <1>; >> + #size-cells = <0>; >> + >> + vco1@58024430 { >> + #clock-cells = <0>; >> + compatible = "stm32,pll"; >> + reg = <0>; > reg is super confusing and doesn't match unit address. ok i fixed it in the v2 > >> + }; > Why? Shouldn't we know this from the compatible string how many > PLLs there are and where they're located? Export the PLLs through > rcc node's clock-cells? > Because i need to offer the possibility to change the PLL VCO frequencies at the start-up of this driver clock. The VCO algorithm needs a division factor, a multiplication factor and a fractional factor. Lot's of solution are possible for one frequency and it's nightmare to satisfy the 3 output dividers of the PLL. >> + >> + vco2@58024438 { >> + #clock-cells = <0>; >> + compatible = "stm32,pll"; >> + reg = <1>; > reg is super confusing and doesn't match unit address. > >> + st,clock-div = <2>; >> + st,clock-mult = <40>; >> + st,frac-status = <0>; >> + st,frac = <0>; >> + st,vcosel = <1>; >> + st,pllrge = <2>; > Does this stuff change on a per-board basis? I hope none of these > properties need to be in DT. These properties are optionals. I absolute need it to custumize VCO frequencies of a pll without the boot loader.. i suppressed "st,frac-status" and "st,pllrge" in the v2 > >> + }; >> + }; >> + >> + >> +STM32H7 PLL >> +----------- >> + > [...] >> + >> +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 = AHB4RSTR_offset / 4 * 32 + CRCRST_bit_offset = 0x88 / 4 * 32 + 19 = 1107 >> + >> +All available preprocessor macros for reset are defined dt-bindings//mfd/stm32h7-rcc.h > One too many slashes? ok i will fix it > >> +header and can be used in device tree sources. >> + >> +example: >> + >> + timer2 { >> + resets = <&rcc STM32H7_APB1L_RESET(TIM2)>; >> + }; >> diff --git a/drivers/clk/clk-stm32h7.c b/drivers/clk/clk-stm32h7.c >> new file mode 100644 >> index 0000000..c8eb729 >> --- /dev/null >> +++ b/drivers/clk/clk-stm32h7.c >> @@ -0,0 +1,1586 @@ >> +/* >> + * Copyright (C) Gabriel Fernandez 2017 >> + * Author: Gabriel Fernandez <gabriel.fernandez@st.com> >> + * >> + * License terms: GPL V2.0. >> + * >> + * This program is free software; you can redistribute it and/or modify it >> + * under the terms and conditions of the GNU General Public License, >> + * version 2, as published by the Free Software Foundation. >> + * >> + * This program is distributed in the hope it will be useful, but WITHOUT >> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or >> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for >> + * more details. >> + * >> + * You should have received a copy of the GNU General Public License along with >> + * this program. If not, see <http://www.gnu.org/licenses/>. >> + */ >> + >> +#include <linux/clk.h> > Is this used? No i will suppress it > >> +#include <linux/clk-provider.h> >> +#include <linux/err.h> >> +#include <linux/io.h> >> +#include <linux/mfd/syscon.h> >> +#include <linux/of.h> >> +#include <linux/of_address.h> >> +#include <linux/slab.h> >> +#include <linux/spinlock.h> >> +#include <linux/regmap.h> >> + >> +#include <dt-bindings/clock/stm32h7-clks.h> >> + >> +/* Reset Clock Control Registers */ >> +#define RCC_CR 0x00 >> +#define RCC_CFGR 0x10 >> +#define RCC_D1CFGR 0x18 >> +#define RCC_D2CFGR 0x1C >> +#define RCC_D3CFGR 0x20 >> +#define RCC_PLLCKSELR 0x28 >> +#define RCC_PLLCFGR 0x2C >> +#define RCC_PLL1DIVR 0x30 >> +#define RCC_PLL1FRACR 0x34 >> +#define RCC_PLL2DIVR 0x38 >> +#define RCC_PLL2FRACR 0x3C >> +#define RCC_PLL3DIVR 0x40 >> +#define RCC_PLL3FRACR 0x44 >> +#define RCC_D1CCIPR 0x4C >> +#define RCC_D2CCIP1R 0x50 >> +#define RCC_D2CCIP2R 0x54 >> +#define RCC_D3CCIPR 0x58 >> +#define RCC_BDCR 0x70 >> +#define RCC_CSR 0x74 >> +#define RCC_AHB3ENR 0xD4 >> +#define RCC_AHB1ENR 0xD8 >> +#define RCC_AHB2ENR 0xDC >> +#define RCC_AHB4ENR 0xE0 >> +#define RCC_APB3ENR 0xE4 >> +#define RCC_APB1LENR 0xE8 >> +#define RCC_APB1HENR 0xEC >> +#define RCC_APB2ENR 0xF0 >> +#define RCC_APB4ENR 0xF4 >> + >> +static DEFINE_SPINLOCK(rlock); > This is super generic and will make lockdep debugging sad. > Perhaps stm32rcc_lock? ok > >> + >> +static void __iomem *base; >> +static struct regmap *pdrm; >> +static struct clk_hw **hws; >> + >> +/* System clock parent */ >> +static const char * const sys_src[] = { >> + "hsi_ck", "csi_ck", "hse_ck", "pll1_p" }; >> + >> +static const char * const tracein_src[] = { >> + "hsi_ck", "csi_ck", "hse_ck", "pll1_r" }; > [...] >> + >> +static unsigned long pll_fd_recalc_rate(struct clk_hw *hw, >> + unsigned long parent_rate) >> +{ >> + struct stm32_pll_obj *clk_elem = to_pll(hw); >> + struct stm32_fractional_divider *fd = &clk_elem->div; >> + unsigned long m, n; >> + u32 val, mask; >> + u64 rate, rate1 = 0; >> + >> + val = clk_readl(fd->mreg); > Please don't use clk_readl() unless you need it for some reason. ok > >> + mask = (GENMASK(fd->mwidth - 1, 0) << fd->mshift); >> + m = (val & mask) >> fd->mshift; >> + >> + val = clk_readl(fd->nreg); >> + mask = (GENMASK(fd->nwidth - 1, 0) << fd->nshift); > Useless parentheses. And isn't GENMASK supposed to take the > actual bit positions? Then we avoid overflow issues? ok il will fix it. >> + n = ((val & mask) >> fd->nshift) + 1; >> + >> + if (!n || !m) >> + return parent_rate; >> + >> + rate = (u64)parent_rate * n; >> + do_div(rate, m); >> + >> + if (pll_frac_is_enabled(hw)) { >> + val = pll_read_frac(hw); >> + rate1 = (u64) parent_rate * (u64) val; >> + do_div(rate1, (m * 8191)); >> + } >> + >> + return rate + rate1; >> +} >> + > [...] >> + >> + /* Micro-controller clocks */ >> + for (n = 0; n < ARRAY_SIZE(mco_clk); n++) { >> + get_cfg_composite_div(&mco_clk_cfg, &mco_clk[n], &c_cfg, >> + &rlock); >> + >> + hws[MCO_BANK + n] = clk_hw_register_composite(NULL, >> + mco_clk[n].name, >> + mco_clk[n].parent_name, >> + mco_clk[n].num_parents, >> + c_cfg.mux_hw, c_cfg.mux_ops, >> + c_cfg.div_hw, c_cfg.div_ops, >> + c_cfg.gate_hw, c_cfg.gate_ops, >> + mco_clk[n].flags); >> + } >> + >> + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); >> + >> + return; >> + >> +err_free_clks: >> + kfree(clk_data); >> +} >> +CLK_OF_DECLARE_DRIVER(stm32h7_rcc, "st,stm32h743-rcc", stm32h7_rcc_init); > Is there another driver that uses the same register space? > Nothing showing up in -next right now. Perhaps a comment should > be added to indicate the other driver. Yes the reset controler. ok il will add a comment. BR Gabriel -- To unsubscribe from this list: send the line "unsubscribe linux-clk" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 04/06, Gabriel Fernandez wrote: > On 04/06/2017 12:32 AM, Stephen Boyd wrote: > >On 03/15, gabriel.fernandez@st.com wrote: > >>diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt > >>new file mode 100644 > >>index 0000000..9d4b587 > >>--- /dev/null > >>+++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt > >>@@ -0,0 +1,152 @@ > >>+ > >>+ rcc: rcc@58024400 { > >>+ #reset-cells = <1>; > >>+ #clock-cells = <2> > >>+ compatible = "st,stm32h743-rcc", "st,stm32-rcc"; > >>+ reg = <0x58024400 0x400>; > >>+ clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>; > >>+ > >>+ st,syscfg = <&pwrcfg>; > >>+ > >>+ #address-cells = <1>; > >>+ #size-cells = <0>; > >>+ > >>+ vco1@58024430 { > >>+ #clock-cells = <0>; > >>+ compatible = "stm32,pll"; > >>+ reg = <0>; > >reg is super confusing and doesn't match unit address. > ok i fixed it in the v2 > > > > >>+ }; > >Why? Shouldn't we know this from the compatible string how many > >PLLs there are and where they're located? Export the PLLs through > >rcc node's clock-cells? > > > Because i need to offer the possibility to change the PLL VCO > frequencies at the start-up of this driver clock. > The VCO algorithm needs a division factor, a multiplication factor > and a fractional factor. > Lot's of solution are possible for one frequency and it's nightmare > to satisfy the 3 output dividers of the PLL. Sure, but do we need to configure that on a per-board basis or a per-SoC basis? If it's just some configuration, I wonder why we don't put that into the driver and base it off some compatible string that includes the SoC the device is for.
Hi Stephen Sorry for delay i was on sick live On 04/07/2017 09:51 PM, Stephen Boyd wrote: > On 04/06, Gabriel Fernandez wrote: >> On 04/06/2017 12:32 AM, Stephen Boyd wrote: >>> On 03/15, gabriel.fernandez@st.com wrote: >>>> diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>>> new file mode 100644 >>>> index 0000000..9d4b587 >>>> --- /dev/null >>>> +++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt >>>> @@ -0,0 +1,152 @@ >>>> + >>>> + rcc: rcc@58024400 { >>>> + #reset-cells = <1>; >>>> + #clock-cells = <2> >>>> + compatible = "st,stm32h743-rcc", "st,stm32-rcc"; >>>> + reg = <0x58024400 0x400>; >>>> + clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>; >>>> + >>>> + st,syscfg = <&pwrcfg>; >>>> + >>>> + #address-cells = <1>; >>>> + #size-cells = <0>; >>>> + >>>> + vco1@58024430 { >>>> + #clock-cells = <0>; >>>> + compatible = "stm32,pll"; >>>> + reg = <0>; >>> reg is super confusing and doesn't match unit address. >> ok i fixed it in the v2 >> >>>> + }; >>> Why? Shouldn't we know this from the compatible string how many >>> PLLs there are and where they're located? Export the PLLs through >>> rcc node's clock-cells? >>> >> Because i need to offer the possibility to change the PLL VCO >> frequencies at the start-up of this driver clock. >> The VCO algorithm needs a division factor, a multiplication factor >> and a fractional factor. >> Lot's of solution are possible for one frequency and it's nightmare >> to satisfy the 3 output dividers of the PLL. > Sure, but do we need to configure that on a per-board basis or a > per-SoC basis? If it's just some configuration, I wonder why we > don't put that into the driver and base it off some compatible > string that includes the SoC the device is for. > I prefer to let in first, the responsibility of the boot loader to change VCO parameters. Then i propose a new version without DT configuration of PLL's are you ok for that ? best regards Gabriel To simply -- To unsubscribe from this list: send the line "unsubscribe linux-clk" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt new file mode 100644 index 0000000..9d4b587 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/st,stm32h7-rcc.txt @@ -0,0 +1,152 @@ +STMicroelectronics STM32H7 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,stm32h743-rcc" + +- reg: should be register base and length as documented in the + datasheet + +- #reset-cells: 1, see below + +- #clock-cells : from common clock binding; shall be set to 1 + +- clocks: External oscillator clock phandle + - high speed external clock signal (HSE) + - low speed external clock signal (LSE) + - external I2S clock (I2S_CKIN) + +- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain + write protection (RTC clock). + +- pll x node: Allow to register a pll with specific parameters. + Please see PLL section below. + +Example: + + rcc: rcc@58024400 { + #reset-cells = <1>; + #clock-cells = <2> + compatible = "st,stm32h743-rcc", "st,stm32-rcc"; + reg = <0x58024400 0x400>; + clocks = <&clk_hse>, <&clk_lse>, <&clk_i2s_ckin>; + + st,syscfg = <&pwrcfg>; + + #address-cells = <1>; + #size-cells = <0>; + + vco1@58024430 { + #clock-cells = <0>; + compatible = "stm32,pll"; + reg = <0>; + }; + + vco2@58024438 { + #clock-cells = <0>; + compatible = "stm32,pll"; + reg = <1>; + st,clock-div = <2>; + st,clock-mult = <40>; + st,frac-status = <0>; + st,frac = <0>; + st,vcosel = <1>; + st,pllrge = <2>; + }; + }; + + +STM32H7 PLL +----------- + +The VCO of STM32 PLL could be reprensented like this: + + Vref --------- -------- + ---->| / DIVM |---->| x DIVN | ------> VCO + --------- -------- + ^ + | + ------- + | FRACN | + ------- + +When the PLL is configured in integer mode: +- VCO = ( Vref / DIVM ) * DIVN + +When the PLL is configured in fractional mode: +- VCO = ( Vref / DIVM ) * ( DIVN + FRACN / 2^13) + + +Required properties for pll node: +- compatible: Should be: + "stm32,pll" + +- #clock-cells: from common clock binding; shall be set to 0 +- reg: Should be the pll number. + +Optional properties: +- st,clock-div: DIVM division factor : <1..63> +- st,clock-mult: DIVN multiplication factor : <4..512> + +- st,frac-status: + - 0 Pll is configured in integer mode + - 1 Pll is configure in fractional mode + +- st,frac: Fractional part of the multiplication factor : <0..8191> + +- st,vcosel: VCO selection + - 0: Wide VCO range:192 to 836 MHz + - 1: Medium VCO range:150 to 420 MHz + +- st,pllrge: PLL input frequency range + - 0: The PLL input (Vref / DIVM) clock range frequency is between 1 and 2 MHz + - 1: The PLL input (Vref / DIVM) clock range frequency is between 2 and 4 MHz + - 2: The PLL input (Vref / DIVM) clock range frequency is between 4 and 8 MHz + - 3: The PLL input (Vref / DIVM) clock range frequency is between 8 and 16 MHz + + +The peripheral clock consumer should specify the desired clock by +having the clock ID in its "clocks" phandle cell. + +All available clocks are defined as preprocessor macros in +dt-bindings/clock/stm32h7-clks.h header and can be used in device +tree sources. + +Example: + + timer5: timer@40000c00 { + compatible = "st,stm32-timer"; + reg = <0x40000c00 0x400>; + interrupts = <50>; + clocks = <&rcc TIM5_CK>; + + }; + +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 = AHB4RSTR_offset / 4 * 32 + CRCRST_bit_offset = 0x88 / 4 * 32 + 19 = 1107 + +All available preprocessor macros for reset are defined dt-bindings//mfd/stm32h7-rcc.h +header and can be used in device tree sources. + +example: + + timer2 { + resets = <&rcc STM32H7_APB1L_RESET(TIM2)>; + }; diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 92c12b8..734aa02 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o obj-$(CONFIG_ARCH_STM32) += clk-stm32f4.o +obj-$(CONFIG_ARCH_STM32) += clk-stm32h7.o obj-$(CONFIG_ARCH_TANGO) += clk-tango4.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o obj-$(CONFIG_ARCH_U300) += clk-u300.o diff --git a/drivers/clk/clk-stm32h7.c b/drivers/clk/clk-stm32h7.c new file mode 100644 index 0000000..c8eb729 --- /dev/null +++ b/drivers/clk/clk-stm32h7.c @@ -0,0 +1,1586 @@ +/* + * Copyright (C) Gabriel Fernandez 2017 + * Author: Gabriel Fernandez <gabriel.fernandez@st.com> + * + * License terms: GPL V2.0. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/stm32h7-clks.h> + +/* Reset Clock Control Registers */ +#define RCC_CR 0x00 +#define RCC_CFGR 0x10 +#define RCC_D1CFGR 0x18 +#define RCC_D2CFGR 0x1C +#define RCC_D3CFGR 0x20 +#define RCC_PLLCKSELR 0x28 +#define RCC_PLLCFGR 0x2C +#define RCC_PLL1DIVR 0x30 +#define RCC_PLL1FRACR 0x34 +#define RCC_PLL2DIVR 0x38 +#define RCC_PLL2FRACR 0x3C +#define RCC_PLL3DIVR 0x40 +#define RCC_PLL3FRACR 0x44 +#define RCC_D1CCIPR 0x4C +#define RCC_D2CCIP1R 0x50 +#define RCC_D2CCIP2R 0x54 +#define RCC_D3CCIPR 0x58 +#define RCC_BDCR 0x70 +#define RCC_CSR 0x74 +#define RCC_AHB3ENR 0xD4 +#define RCC_AHB1ENR 0xD8 +#define RCC_AHB2ENR 0xDC +#define RCC_AHB4ENR 0xE0 +#define RCC_APB3ENR 0xE4 +#define RCC_APB1LENR 0xE8 +#define RCC_APB1HENR 0xEC +#define RCC_APB2ENR 0xF0 +#define RCC_APB4ENR 0xF4 + +static DEFINE_SPINLOCK(rlock); + +static void __iomem *base; +static struct regmap *pdrm; +static struct clk_hw **hws; + +/* System clock parent */ +static const char * const sys_src[] = { + "hsi_ck", "csi_ck", "hse_ck", "pll1_p" }; + +static const char * const tracein_src[] = { + "hsi_ck", "csi_ck", "hse_ck", "pll1_r" }; + +static const char * const per_src[] = { + "hsi_ker", "csi_ker", "hse_ck", "disabled" }; + +static const char * const pll_src[] = { + "hsi_ck", "csi_ck", "hse_ck", "no clock" }; + +static const char * const sdmmc_src[] = { "pll1_q", "pll2_r" }; + +static const char * const dsi_src[] = { "ck_dsi_phy", "pll2_q" }; + +static const char * const qspi_src[] = { + "hclk", "pll1_q", "pll2_r", "per_ck" }; + +static const char * const fmc_src[] = { + "hclk", "pll1_q", "pll2_r", "per_ck" }; + +/* Kernel clock parent */ +static const char * const swp_src[] = { "pclk1", "hsi_ker" }; + +static const char * const fdcan_src[] = { "hse_ck", "pll1_q", "pll2_q" }; + +static const char * const dfsdm1_src[] = { "pclk2", "sys_ck" }; + +static const char * const spdifrx_src[] = { + "pll1_q", "pll2_r", "pll3_r", "hsi_ker" }; + +static const char *spi_src1[5] = { + "pll1_q", "pll2_p", "pll3_p", NULL, "per_ck" }; + +static const char * const spi_src2[] = { + "pclk2", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "hse_ck" }; + +static const char * const spi_src3[] = { + "pclk4", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "hse_ck" }; + +static const char * const lptim_src1[] = { + "pclk1", "pll2_p", "pll3_r", "lse_ck", "lsi_ck", "per_ck" }; + +static const char * const lptim_src2[] = { + "pclk4", "pll2_p", "pll3_r", "lse_ck", "lsi_ck", "per_ck" }; + +static const char * const cec_src[] = {"lse_ck", "lsi_ck", "csi_ker_div122" }; + +static const char * const usbotg_src[] = {"pll1_q", "pll3_q", "rc48_ck" }; + +/* i2c 1,2,3 src */ +static const char * const i2c_src1[] = { + "pclk1", "pll3_r", "hsi_ker", "csi_ker" }; + +static const char * const i2c_src2[] = { + "pclk4", "pll3_r", "hsi_ker", "csi_ker" }; + +static const char * const rng_src[] = { + "rc48_ck", "pll1_q", "lse_ck", "lsi_ck" }; + +/* usart 1,6 src */ +static const char * const usart_src1[] = { + "pclk2", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "lse_ck" }; + +/* usart 2,3,4,5,7,8 src */ +static const char * const usart_src2[] = { + "pclk1", "pll2_q", "pll3_q", "hsi_ker", "csi_ker", "lse_ck" }; + +static const char *sai_src[5] = { + "pll1_q", "pll2_p", "pll3_p", NULL, "per_ck" }; + +static const char * const adc_src[] = { "pll2_p", "pll3_r", "per_ck" }; + +/* lptim 2,3,4,5 src */ +static const char * const lpuart1_src[] = { + "pclk3", "pll2_q", "pll3_q", "csi_ker", "lse_ck" }; + +static const char * const hrtim_src[] = { "tim2_ker", "d1cpre" }; + +/* RTC clock parent */ +static const char * const rtc_src[] = { "off", "lse_ck", "lsi_ck", "hse_1M" }; + +/* Micro-controller output clock parent */ +static const char * const mco_src1[] = { + "hsi_ck", "lse_ck", "hse_ck", "pll1_q", "rc48_ck" }; + +static const char * const mco_src2[] = { + "sys_ck", "pll2_p", "hse_ck", "pll1_p", "csi", "lsi_ck" }; + +/* LCD clock */ +static const char * const ltdc_src[] = {"pll3_r"}; + +/* Power domain helper */ +static inline void disable_power_domain_write_protection(void) +{ + if (pdrm) + regmap_update_bits(pdrm, 0x00, (1 << 8), (1 << 8)); +} + +static inline void enable_power_domain_write_protection(void) +{ + if (pdrm) + regmap_update_bits(pdrm, 0x00, (1 << 8), (0 << 8)); +} + +static inline int is_enable_power_domain_write_protection(void) +{ + if (pdrm) { + u32 val; + + regmap_read(pdrm, 0x00, &val); + + return !(val & 0x100); + } + return -1; +} + +/* Gate clock with ready bit and backup domain management */ +struct stm32_ready_gate { + struct clk_gate gate; + u8 bit_rdy; + u8 backup_domain; +}; + +#define to_ready_gate_clk(_rgate) container_of(_rgate, struct stm32_ready_gate,\ + gate) + +#define RGATE_TIMEOUT 10000 + +static int ready_gate_clk_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static int ready_gate_clk_enable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct stm32_ready_gate *rgate = to_ready_gate_clk(gate); + int dbp_status; + int bit_status; + unsigned int timeout = RGATE_TIMEOUT; + + if (clk_gate_ops.is_enabled(hw)) + return 0; + + dbp_status = is_enable_power_domain_write_protection(); + + if (rgate->backup_domain && dbp_status) + disable_power_domain_write_protection(); + + clk_gate_ops.enable(hw); + + do { + bit_status = !(readl(gate->reg) & BIT(rgate->bit_rdy)); + + if (bit_status) + udelay(100); + + } while (bit_status && --timeout); + + if (rgate->backup_domain && dbp_status) + enable_power_domain_write_protection(); + + return bit_status; +} + +static void ready_gate_clk_disable(struct clk_hw *hw) +{ + clk_gate_ops.disable(hw); +} + +static const struct clk_ops ready_gate_clk_ops = { + .enable = ready_gate_clk_enable, + .disable = ready_gate_clk_disable, + .is_enabled = ready_gate_clk_is_enabled, +}; + +static struct clk_hw *clk_register_ready_gate(struct device *dev, + const char *name, const char *parent_name, + void __iomem *reg, u8 bit_idx, u8 bit_rdy, + u8 backup_domain, unsigned long flags, spinlock_t *lock) +{ + struct stm32_ready_gate *rgate; + struct clk_init_data init = { NULL }; + struct clk_hw *hw; + int ret; + + rgate = kzalloc(sizeof(*rgate), GFP_KERNEL); + if (!rgate) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &ready_gate_clk_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + rgate->bit_rdy = bit_rdy; + rgate->backup_domain = backup_domain; + + rgate->gate.lock = lock; + rgate->gate.reg = reg; + rgate->gate.bit_idx = bit_idx; + rgate->gate.hw.init = &init; + + hw = &rgate->gate.hw; + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(rgate); + hw = ERR_PTR(ret); + } + + return hw; +} + +struct gate_cfg { + u32 offset; + u8 bit_idx; +}; + +struct muxdiv_cfg { + u32 offset; + u8 shift; + u8 width; +}; + +struct composite_clk_cfg { + struct gate_cfg *gate; + struct muxdiv_cfg *mux; + struct muxdiv_cfg *div; + const char *name; + const char * const *parent_name; + int num_parents; + u32 flags; +}; + +struct composite_clk_gcfg_t { + u8 flags; + const struct clk_ops *ops; +}; + +/* + * General config definition of a composite clock (only clock diviser for rate) + */ +struct composite_clk_gcfg { + struct composite_clk_gcfg_t *mux; + struct composite_clk_gcfg_t *div; + struct composite_clk_gcfg_t *gate; +}; + +#define M_CFG_MUX(_mux_ops, _mux_flags)\ + .mux = &(struct composite_clk_gcfg_t) { _mux_flags, _mux_ops} + +#define M_CFG_DIV(_rate_ops, _rate_flags)\ + .div = &(struct composite_clk_gcfg_t) {_rate_flags, _rate_ops} + +#define M_CFG_GATE(_gate_ops, _gate_flags)\ + .gate = &(struct composite_clk_gcfg_t) { _gate_flags, _gate_ops} + +static struct clk_mux *_get_cmux(void __iomem *reg, u8 shift, u8 width, + u32 flags, spinlock_t *lock) +{ + struct clk_mux *mux; + + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = reg; + mux->shift = shift; + mux->mask = (1 << width) - 1; + mux->flags = flags; + mux->lock = lock; + + return mux; +} + +static struct clk_divider *_get_cdiv(void __iomem *reg, u8 shift, u8 width, + u32 flags, spinlock_t *lock) +{ + struct clk_divider *div; + + div = kzalloc(sizeof(*div), GFP_KERNEL); + + if (!div) + return ERR_PTR(-ENOMEM); + + div->reg = reg; + div->shift = shift; + div->width = width; + div->flags = flags; + div->lock = lock; + + return div; +} + +static struct clk_gate *_get_cgate(void __iomem *reg, u8 bit_idx, u32 flags, + spinlock_t *lock) +{ + struct clk_gate *gate; + + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) + return ERR_PTR(-ENOMEM); + + gate->reg = reg; + gate->bit_idx = bit_idx; + gate->flags = flags; + gate->lock = lock; + + return gate; +} + +struct composite_cfg { + struct clk_hw *mux_hw; + struct clk_hw *div_hw; + struct clk_hw *gate_hw; + + const struct clk_ops *mux_ops; + const struct clk_ops *div_ops; + const struct clk_ops *gate_ops; +}; + +static void get_cfg_composite_div(const struct composite_clk_gcfg *gcfg, + const struct composite_clk_cfg *cfg, + struct composite_cfg *composite, spinlock_t *lock) +{ + struct clk_mux *mux = NULL; + struct clk_divider *div = NULL; + struct clk_gate *gate = NULL; + const struct clk_ops *mux_ops, *div_ops, *gate_ops; + struct clk_hw *mux_hw; + struct clk_hw *div_hw; + struct clk_hw *gate_hw; + + mux_ops = div_ops = gate_ops = NULL; + mux_hw = div_hw = gate_hw = NULL; + + if (gcfg->mux) { + mux = _get_cmux(base + cfg->mux->offset, + cfg->mux->shift, + cfg->mux->width, + gcfg->mux->flags, lock); + + if (!IS_ERR(mux)) { + mux_hw = &mux->hw; + mux_ops = gcfg->mux->ops ? + gcfg->mux->ops : &clk_mux_ops; + } + } + + if (gcfg->div) { + div = _get_cdiv(base + cfg->div->offset, + cfg->div->shift, + cfg->div->width, + gcfg->div->flags, lock); + + if (!IS_ERR(div)) { + div_hw = &div->hw; + div_ops = gcfg->div->ops ? + gcfg->div->ops : &clk_divider_ops; + } + + } + + if (gcfg->gate) { + gate = _get_cgate(base + cfg->gate->offset, + cfg->gate->bit_idx, + gcfg->gate->flags, lock); + + if (!IS_ERR(gate)) { + gate_hw = &gate->hw; + gate_ops = gcfg->gate->ops ? + gcfg->gate->ops : &clk_gate_ops; + } + + } + + composite->mux_hw = mux_hw; + composite->mux_ops = mux_ops; + + composite->div_hw = div_hw; + composite->div_ops = div_ops; + + composite->gate_hw = gate_hw; + composite->gate_ops = gate_ops; +} + +/* Kernel Timer */ +struct timer_ker { + u8 dppre_shift; + struct clk_hw hw; + spinlock_t *lock; +}; + +#define to_timer_ker(_hw) container_of(_hw, struct timer_ker, hw) + +static unsigned long timer_ker_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct timer_ker *clk_elem = to_timer_ker(hw); + u32 timpre; + u32 dppre_shift = clk_elem->dppre_shift; + u32 prescaler; + u32 mul; + + timpre = (readl(base + RCC_CFGR) >> 15) & 0x01; + + prescaler = (readl(base + RCC_D2CFGR) >> dppre_shift) & 0x03; + + mul = 2; + + if (prescaler < 4) + mul = 1; + + else if (timpre && prescaler > 4) + mul = 4; + + return parent_rate * mul; +} + +static const struct clk_ops timer_ker_ops = { + .recalc_rate = timer_ker_recalc_rate, +}; + +static struct clk_hw *clk_register_stm32_timer_ker(struct device *dev, + const char *name, const char *parent_name, + unsigned long flags, + u8 dppre_shift, + spinlock_t lock) +{ + struct timer_ker *element; + struct clk_init_data init; + struct clk_hw *hw; + int err; + + element = kzalloc(sizeof(*element), GFP_KERNEL); + if (!element) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &timer_ker_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + element->hw.init = &init; + element->lock = &lock; + element->dppre_shift = dppre_shift; + + hw = &element->hw; + err = clk_hw_register(dev, hw); + + if (err) { + kfree(element); + return ERR_PTR(err); + } + + return hw; +} + +static const struct clk_div_table d1cpre_div_table[] = { + { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1}, + { 4, 1 }, { 5, 1 }, { 6, 1 }, { 7, 1}, + { 8, 2 }, { 9, 4 }, { 10, 8 }, { 11, 16 }, + { 12, 64 }, { 13, 128 }, { 14, 256 }, + { 15, 512 }, + { 0 }, +}; + +static const struct clk_div_table ppre_div_table[] = { + { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1}, + { 4, 2 }, { 5, 4 }, { 6, 8 }, { 7, 16 }, + { 0 }, +}; + +static void register_core_and_bus_clocks(void) +{ + /* CORE AND BUS */ + hws[SYS_D1CPRE] = clk_hw_register_divider_table(NULL, "d1cpre", + "sys_ck", CLK_IGNORE_UNUSED, base + RCC_D1CFGR, 8, 4, 0, + d1cpre_div_table, &rlock); + + hws[HCLK] = clk_hw_register_divider_table(NULL, "hclk", "d1cpre", + CLK_IGNORE_UNUSED, base + RCC_D1CFGR, 0, 4, 0, + d1cpre_div_table, &rlock); + + /* D1 DOMAIN */ + /* * CPU Systick */ + hws[CPU_SYSTICK] = clk_hw_register_fixed_factor(NULL, "systick", + "d1cpre", 0, 1, 8); + + /* * APB3 peripheral */ + hws[PCLK3] = clk_hw_register_divider_table(NULL, "pclk3", "hclk", 0, + base + RCC_D1CFGR, 4, 3, 0, + ppre_div_table, &rlock); + + /* D2 DOMAIN */ + /* * APB1 peripheral */ + hws[PCLK1] = clk_hw_register_divider_table(NULL, "pclk1", "hclk", 0, + base + RCC_D2CFGR, 4, 3, 0, + ppre_div_table, &rlock); + + /* Timers prescaler clocks */ + clk_register_stm32_timer_ker(NULL, "tim1_ker", "pclk1", 0, + 4, rlock); + + /* * APB2 peripheral */ + hws[PCLK2] = clk_hw_register_divider_table(NULL, "pclk2", "hclk", 0, + base + RCC_D2CFGR, 8, 3, 0, ppre_div_table, &rlock); + + clk_register_stm32_timer_ker(NULL, "tim2_ker", "pclk2", 0, 8, rlock); + + /* D3 DOMAIN */ + /* * APB4 peripheral */ + hws[PCLK4] = clk_hw_register_divider_table(NULL, "pclk4", "hclk", 0, + base + RCC_D3CFGR, 4, 3, 0, + ppre_div_table, &rlock); +} + +/* MUX clock configuration */ +struct stm32_mux_clk { + const char *name; + const char * const *parents; + u8 num_parents; + u32 offset; + u8 shift; + u8 width; + u32 flags; +}; + +#define M_MCLOCF(_name, _parents, _mux_offset, _mux_shift, _mux_width, _flags)\ +{\ + .name = _name,\ + .parents = _parents,\ + .num_parents = ARRAY_SIZE(_parents),\ + .offset = _mux_offset,\ + .shift = _mux_shift,\ + .width = _mux_width,\ + .flags = _flags,\ +} +#define M_MCLOC(_name, _parents, _mux_offset, _mux_shift, _mux_width)\ + M_MCLOCF(_name, _parents, _mux_offset, _mux_shift, _mux_width, 0)\ + +static const struct stm32_mux_clk stm32_mclk[] __initconst = { + M_MCLOC("per_ck", per_src, RCC_D1CCIPR, 28, 3), + M_MCLOC("pllsrc", pll_src, RCC_PLLCKSELR, 0, 3), + M_MCLOC("sys_ck", sys_src, RCC_CFGR, 0, 3), + M_MCLOC("tracein_ck", tracein_src, RCC_CFGR, 0, 3), +}; + +/* Oscillary clock configuration */ +struct stm32_osc_clk { + const char *name; + const char *parent; + u32 gate_offset; + u8 bit_idx; + u8 bit_rdy; + u32 flags; +}; + +#define OSC_CLKF(_name, _parent, _gate_offset, _bit_idx, _bit_rdy, _flags)\ +{\ + .name = _name,\ + .parent = _parent,\ + .gate_offset = _gate_offset,\ + .bit_idx = _bit_idx,\ + .bit_rdy = _bit_rdy,\ + .flags = _flags,\ +} +#define OSC_CLK(_name, _parent, _gate_offset, _bit_idx, _bit_rdy)\ + OSC_CLKF(_name, _parent, _gate_offset, _bit_idx, _bit_rdy, 0) + +static const struct stm32_osc_clk stm32_oclk[] __initconst = { + OSC_CLK("hsi_ck", "hsidiv", RCC_CR, 0, 2), + OSC_CLK("hsi_ker", "hsidiv", RCC_CR, 1, 2), + OSC_CLK("csi_ck", "clk-csi", RCC_CR, 7, 8), + OSC_CLK("csi_ker", "clk-csi", RCC_CR, 9, 8), + OSC_CLK("rc48_ck", "clk-rc48", RCC_CR, 12, 13), + OSC_CLK("lsi_ck", "clk-lsi", RCC_CSR, 0, 1), +}; + +/* PLL configuration */ +struct st32h7_pll_cfg { + u8 bit_idx; + u32 offset_divr; + u8 bit_frac_en; + u32 offset_frac; + u8 divm; +}; + +struct stm32_pll_data { + const char *name; + const char *parent_name; + unsigned long flags; + const struct st32h7_pll_cfg *cfg; +}; + +static const struct st32h7_pll_cfg stm32h7_pll1 = { + .bit_idx = 24, + .offset_divr = RCC_PLL1DIVR, + .bit_frac_en = 0, + .offset_frac = RCC_PLL1FRACR, + .divm = 4, +}; + +static const struct st32h7_pll_cfg stm32h7_pll2 = { + .bit_idx = 26, + .offset_divr = RCC_PLL2DIVR, + .bit_frac_en = 4, + .offset_frac = RCC_PLL2FRACR, + .divm = 12, +}; + +static const struct st32h7_pll_cfg stm32h7_pll3 = { + .bit_idx = 28, + .offset_divr = RCC_PLL3DIVR, + .bit_frac_en = 8, + .offset_frac = RCC_PLL3FRACR, + .divm = 20, +}; + +static const struct stm32_pll_data stm32_pll[] = { + { "vco1", "pllsrc", 0, &stm32h7_pll1 }, + { "vco2", "pllsrc", 0, &stm32h7_pll2 }, + { "vco3", "pllsrc", 0, &stm32h7_pll3 }, +}; + +struct stm32_fractional_divider { + void __iomem *mreg; + u8 mshift; + u8 mwidth; + u32 mmask; + + void __iomem *nreg; + u8 nshift; + u8 nwidth; + + void __iomem *freg_status; + u8 freg_bit; + void __iomem *freg_value; + u8 fshift; + u8 fwidth; + + u8 flags; + struct clk_hw hw; + spinlock_t *lock; +}; + +struct stm32_pll_obj { + spinlock_t *lock; + struct stm32_fractional_divider div; + struct stm32_ready_gate rgate; + struct clk_hw hw; +}; + +#define to_pll(_hw) container_of(_hw, struct stm32_pll_obj, hw) + +static int pll_is_enabled(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct clk_hw *_hw = &clk_elem->rgate.gate.hw; + + __clk_hw_set_clk(_hw, hw); + + return ready_gate_clk_ops.is_enabled(_hw); +} + +static int pll_enable(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct clk_hw *_hw = &clk_elem->rgate.gate.hw; + + __clk_hw_set_clk(_hw, hw); + + return ready_gate_clk_ops.enable(_hw); +} + +static void pll_disable(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct clk_hw *_hw = &clk_elem->rgate.gate.hw; + + __clk_hw_set_clk(_hw, hw); + + ready_gate_clk_ops.disable(_hw); +} + +static int pll_frac_is_enabled(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct stm32_fractional_divider *fd = &clk_elem->div; + + return (readl(fd->freg_status) >> fd->freg_bit) & 0x01; +} + +static unsigned long pll_read_frac(struct clk_hw *hw) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct stm32_fractional_divider *fd = &clk_elem->div; + + return (readl(fd->freg_value) >> fd->fshift) & + GENMASK(fd->fwidth - 1, 0); +} + +static unsigned long pll_fd_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct stm32_fractional_divider *fd = &clk_elem->div; + unsigned long m, n; + u32 val, mask; + u64 rate, rate1 = 0; + + val = clk_readl(fd->mreg); + mask = (GENMASK(fd->mwidth - 1, 0) << fd->mshift); + m = (val & mask) >> fd->mshift; + + val = clk_readl(fd->nreg); + mask = (GENMASK(fd->nwidth - 1, 0) << fd->nshift); + n = ((val & mask) >> fd->nshift) + 1; + + if (!n || !m) + return parent_rate; + + rate = (u64)parent_rate * n; + do_div(rate, m); + + if (pll_frac_is_enabled(hw)) { + val = pll_read_frac(hw); + rate1 = (u64) parent_rate * (u64) val; + do_div(rate1, (m * 8191)); + } + + return rate + rate1; +} + +static const struct clk_ops pll_ops = { + .enable = pll_enable, + .disable = pll_disable, + .is_enabled = pll_is_enabled, + .recalc_rate = pll_fd_recalc_rate, +}; + +static struct clk_hw *clk_register_stm32_pll(struct device *dev, + const char *name, + const char *parent, + unsigned long flags, + const struct st32h7_pll_cfg *cfg, + spinlock_t *lock) +{ + + struct stm32_pll_obj *pll; + struct clk_init_data init = { NULL }; + struct clk_hw *hw; + int ret; + struct stm32_fractional_divider *div = NULL; + struct stm32_ready_gate *rgate; + + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &pll_ops; + init.flags = flags; + init.parent_names = &parent; + init.num_parents = 1; + pll->hw.init = &init; + + hw = &pll->hw; + rgate = &pll->rgate; + + rgate->bit_rdy = cfg->bit_idx + 1; + rgate->gate.lock = lock; + rgate->gate.reg = base + RCC_CR; + rgate->gate.bit_idx = cfg->bit_idx; + + div = &pll->div; + div->flags = 0; + div->mreg = base + RCC_PLLCKSELR; + div->mshift = cfg->divm; + div->mwidth = 6; + div->nreg = base + cfg->offset_divr; + div->nshift = 0; + div->nwidth = 9; + + div->freg_status = base + RCC_PLLCFGR; + div->freg_bit = cfg->bit_frac_en; + div->freg_value = base + cfg->offset_frac; + div->fshift = 3; + div->fwidth = 13; + + div->lock = lock; + + ret = clk_hw_register(dev, hw); + if (ret) { + kfree(pll); + hw = ERR_PTR(ret); + } + + return hw; +} + +/* ODF CLOCKS */ +static unsigned long odf_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return clk_divider_ops.recalc_rate(hw, parent_rate); +} + +static long odf_divider_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + return clk_divider_ops.round_rate(hw, rate, prate); +} + +static int odf_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_hw *hwp; + int pll_status; + int ret; + + hwp = clk_hw_get_parent(hw); + + pll_status = pll_is_enabled(hwp); + + if (pll_status) + pll_disable(hwp); + + ret = clk_divider_ops.set_rate(hw, rate, parent_rate); + + if (pll_status) + pll_enable(hwp); + + return ret; +} + +static const struct clk_ops odf_divider_ops = { + .recalc_rate = odf_divider_recalc_rate, + .round_rate = odf_divider_round_rate, + .set_rate = odf_divider_set_rate, +}; + +static int odf_gate_enable(struct clk_hw *hw) +{ + struct clk_hw *hwp; + int pll_status; + int ret; + + hwp = clk_hw_get_parent(hw); + + pll_status = pll_is_enabled(hwp); + + if (pll_status) + pll_disable(hwp); + + ret = clk_gate_ops.enable(hw); + + if (pll_status) + pll_enable(hwp); + + return ret; +} + +static void odf_gate_disable(struct clk_hw *hw) +{ + clk_gate_ops.disable(hw); +} + +static int odf_gate_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static const struct clk_ops odf_gate_ops = { + .enable = odf_gate_enable, + .disable = odf_gate_disable, + .is_enabled = odf_gate_is_enabled, +}; + +static struct composite_clk_gcfg odf_clk_gcfg = { + M_CFG_DIV(&odf_divider_ops, 0), + M_CFG_GATE(&odf_gate_ops, 0), +}; + +#define M_ODF_F(_name, _parent, _gate_offset, _bit_idx, _rate_offset,\ + _rate_shift, _rate_width, _flags)\ +{\ + .mux = NULL,\ + .div = &(struct muxdiv_cfg) {_rate_offset, _rate_shift, _rate_width},\ + .gate = &(struct gate_cfg) {_gate_offset, _bit_idx },\ + .name = _name,\ + .parent_name = &(const char *) {_parent},\ + .num_parents = 1,\ + .flags = _flags,\ +} + +#define M_ODF(_name, _parent, _gate_offset, _bit_idx, _rate_offset,\ + _rate_shift, _rate_width)\ +M_ODF_F(_name, _parent, _gate_offset, _bit_idx, _rate_offset,\ + _rate_shift, _rate_width, 0)\ + +static const struct composite_clk_cfg stm32_odf[3][3] = { + { + M_ODF("pll1_p", "vco1", RCC_PLLCFGR, 16, RCC_PLL1DIVR, 9, 7), + M_ODF("pll1_q", "vco1", RCC_PLLCFGR, 17, RCC_PLL1DIVR, 16, 7), + M_ODF("pll1_r", "vco1", RCC_PLLCFGR, 18, RCC_PLL1DIVR, 24, 7), + }, + + { + M_ODF("pll2_p", "vco2", RCC_PLLCFGR, 19, RCC_PLL2DIVR, 9, 7), + M_ODF("pll2_q", "vco2", RCC_PLLCFGR, 20, RCC_PLL2DIVR, 16, 7), + M_ODF("pll2_r", "vco2", RCC_PLLCFGR, 21, RCC_PLL2DIVR, 24, 7), + }, + { + M_ODF("pll3_p", "vco3", RCC_PLLCFGR, 22, RCC_PLL3DIVR, 9, 7), + M_ODF("pll3_q", "vco3", RCC_PLLCFGR, 23, RCC_PLL3DIVR, 16, 7), + M_ODF("pll3_r", "vco3", RCC_PLLCFGR, 24, RCC_PLL3DIVR, 24, 7), + } +}; + +/* PLL config structure from DT */ +struct pll_param { + u32 index; + u32 mult; + u32 div; + u32 frac_en; + u32 frac; + u32 vcosel; + u32 pllrge; +}; + +static int of_get_stm32_pll(struct device_node *np, struct pll_param *param) +{ + if (of_property_read_u32(np, "reg", ¶m->index) || + param->index >= ARRAY_SIZE(stm32_pll)) + return -EINVAL; + + of_property_read_u32(np, "st,clock-div", ¶m->div); + + of_property_read_u32(np, "st,clock-mult", ¶m->mult); + + param->frac_en = ~0; + of_property_read_u32(np, "st,frac-status", ¶m->frac_en); + + of_property_read_u32(np, "st,frac", ¶m->frac); + + param->vcosel = ~0; + of_property_read_u32(np, "st,vcosel", ¶m->vcosel); + + param->pllrge = ~0; + of_property_read_u32(np, "st,pllrge", ¶m->pllrge); + + return 0; +} + +static void stm32_pll_save_param(struct clk_hw *hw, + struct pll_param *pll_dt_cfg) +{ + struct stm32_pll_obj *clk_elem = to_pll(hw); + struct stm32_fractional_divider *fd = &clk_elem->div; + unsigned long m, n; + u32 val; + int pll_status; + + /* Save PLL parameters from DT if needed */ + pll_status = pll_ops.is_enabled(hw); + + if (pll_status) + pll_ops.disable(hw); + + m = pll_dt_cfg->div; + if (m) { + val = clk_readl(fd->mreg); + val &= ~(GENMASK(fd->mwidth - 1, 0) << fd->mshift); + val |= (m << fd->mshift); + writel(val, fd->mreg); + } + + n = pll_dt_cfg->mult; + if (n) { + val = clk_readl(fd->nreg); + val &= ~((GENMASK(fd->nwidth - 1, 0) << fd->nshift)); + val |= ((n - 1) << fd->nshift); + writel(val, fd->nreg); + } + + if (pll_dt_cfg->frac_en != ~0) { + int status; + + status = !!pll_dt_cfg->frac_en; + + /* write frac status */ + val = readl(fd->freg_status); + + val &= ~BIT(fd->freg_bit); + val |= status << fd->freg_bit; + writel(val, fd->freg_status); + + /* write frac value */ + val = clk_readl(fd->freg_value); + val &= ~((GENMASK(fd->fshift - 1, 0) << fd->fshift)); + + /* if enable, set to '0' before */ + if (status) + writel(val, fd->freg_value); + + val |= ((pll_dt_cfg->frac) << fd->fshift); + writel(val, fd->freg_value); + } + + if (pll_dt_cfg->vcosel != ~0) { + val = readl(fd->freg_status); + val &= ~BIT(fd->freg_bit + 1); + val |= (pll_dt_cfg->vcosel & 0x01) << (fd->freg_bit + 1); + writel(val, fd->freg_status); + + } + + if (pll_dt_cfg->pllrge != ~0) { + val = readl(fd->freg_status); + val &= ~(0x3 << (fd->freg_bit + 2)); + val |= (pll_dt_cfg->pllrge & 0x3) << (fd->freg_bit + 2); + writel(val, fd->freg_status); + } + + if (pll_status) + pll_ops.enable(hw); +} + +static void stm32_h7_pll_init(struct device_node *np) +{ + struct pll_param pll_dt_cfg = { }; + struct clk_hw *hw; + int idx, n; + + if (of_get_stm32_pll(np, &pll_dt_cfg)) + return; + + idx = pll_dt_cfg.index; + + /* Register the VCO */ + hw = clk_register_stm32_pll(NULL, stm32_pll[idx].name, + stm32_pll[idx].parent_name, stm32_pll[idx].flags, + stm32_pll[idx].cfg, + &rlock); + + stm32_pll_save_param(hw, &pll_dt_cfg); + + /* Register the 3 output dividers */ + for (n = 0; n < 3; n++) { + struct composite_cfg c_cfg; + + get_cfg_composite_div(&odf_clk_gcfg, &stm32_odf[idx][n], + &c_cfg, &rlock); + + hws[ODF_BANK + (idx * 3) + n] = clk_hw_register_composite(NULL, + stm32_odf[idx][n].name, + stm32_odf[idx][n].parent_name, + stm32_odf[idx][n].num_parents, + c_cfg.mux_hw, c_cfg.mux_ops, + c_cfg.div_hw, c_cfg.div_ops, + c_cfg.gate_hw, c_cfg.gate_ops, + stm32_odf[idx][n].flags); + } +} + +/* PERIF CLOCKS */ +struct pclk_t { + u32 gate_offset; + u8 bit_idx; + const char *name; + const char *parent; + u32 flags; +}; + +#define PER_CLKF(_gate_offset, _bit_idx, _name, _parent, _flags)\ +{\ + .gate_offset = _gate_offset,\ + .bit_idx = _bit_idx,\ + .name = _name,\ + .parent = _parent,\ + .flags = _flags,\ +} +#define PER_CLK(_gate_offset, _bit_idx, _name, _parent)\ + PER_CLKF(_gate_offset, _bit_idx, _name, _parent, 0) + +static const struct pclk_t pclk[] = { + PER_CLK(RCC_AHB3ENR, 31, "d1sram1", "hclk"), + PER_CLK(RCC_AHB3ENR, 30, "itcm", "hclk"), + PER_CLK(RCC_AHB3ENR, 29, "dtcm2", "hclk"), + PER_CLK(RCC_AHB3ENR, 28, "dtcm1", "hclk"), + PER_CLK(RCC_AHB3ENR, 8, "flitf", "hclk"), + PER_CLK(RCC_AHB3ENR, 5, "jpgdec", "hclk"), + PER_CLK(RCC_AHB3ENR, 4, "dma2d", "hclk"), + PER_CLK(RCC_AHB3ENR, 0, "mdma", "hclk"), + PER_CLK(RCC_AHB1ENR, 28, "usb2ulpi", "hclk"), + PER_CLK(RCC_AHB1ENR, 26, "usb1ulpi", "hclk"), + PER_CLK(RCC_AHB1ENR, 17, "eth1rx", "hclk"), + PER_CLK(RCC_AHB1ENR, 16, "eth1tx", "hclk"), + PER_CLK(RCC_AHB1ENR, 15, "eth1mac", "hclk"), + PER_CLK(RCC_AHB1ENR, 14, "art", "hclk"), + PER_CLK(RCC_AHB1ENR, 1, "dma2", "hclk"), + PER_CLK(RCC_AHB1ENR, 0, "dma1", "hclk"), + PER_CLK(RCC_AHB2ENR, 31, "d2sram3", "hclk"), + PER_CLK(RCC_AHB2ENR, 30, "d2sram2", "hclk"), + PER_CLK(RCC_AHB2ENR, 29, "d2sram1", "hclk"), + PER_CLK(RCC_AHB2ENR, 5, "hash", "hclk"), + PER_CLK(RCC_AHB2ENR, 4, "crypt", "hclk"), + PER_CLK(RCC_AHB2ENR, 0, "camitf", "hclk"), + PER_CLK(RCC_AHB4ENR, 28, "bkpram", "hclk"), + PER_CLK(RCC_AHB4ENR, 25, "hsem", "hclk"), + PER_CLK(RCC_AHB4ENR, 21, "bdma", "hclk"), + PER_CLK(RCC_AHB4ENR, 19, "crc", "hclk"), + PER_CLK(RCC_AHB4ENR, 10, "gpiok", "hclk"), + PER_CLK(RCC_AHB4ENR, 9, "gpioj", "hclk"), + PER_CLK(RCC_AHB4ENR, 8, "gpioi", "hclk"), + PER_CLK(RCC_AHB4ENR, 7, "gpioh", "hclk"), + PER_CLK(RCC_AHB4ENR, 6, "gpiog", "hclk"), + PER_CLK(RCC_AHB4ENR, 5, "gpiof", "hclk"), + PER_CLK(RCC_AHB4ENR, 4, "gpioe", "hclk"), + PER_CLK(RCC_AHB4ENR, 3, "gpiod", "hclk"), + PER_CLK(RCC_AHB4ENR, 2, "gpioc", "hclk"), + PER_CLK(RCC_AHB4ENR, 1, "gpiob", "hclk"), + PER_CLK(RCC_AHB4ENR, 0, "gpioa", "hclk"), + PER_CLK(RCC_APB3ENR, 6, "wwdg1", "pclk3"), + PER_CLK(RCC_APB1LENR, 29, "dac12", "pclk1"), + PER_CLK(RCC_APB1LENR, 11, "wwdg2", "pclk1"), + PER_CLK(RCC_APB1LENR, 8, "tim14", "pclk1"), + PER_CLK(RCC_APB1LENR, 7, "tim13", "pclk1"), + PER_CLK(RCC_APB1LENR, 6, "tim12", "pclk1"), + PER_CLK(RCC_APB1LENR, 5, "tim7", "pclk1"), + PER_CLK(RCC_APB1LENR, 4, "tim6", "pclk1"), + PER_CLK(RCC_APB1LENR, 3, "tim5", "pclk1"), + PER_CLK(RCC_APB1LENR, 2, "tim4", "pclk1"), + PER_CLK(RCC_APB1LENR, 1, "tim3", "pclk1"), + PER_CLK(RCC_APB1LENR, 0, "tim2", "pclk1"), + PER_CLK(RCC_APB1HENR, 5, "mdios", "pclk1"), + PER_CLK(RCC_APB1HENR, 4, "opamp", "pclk1"), + PER_CLK(RCC_APB1HENR, 1, "crs", "pclk1"), + PER_CLK(RCC_APB2ENR, 18, "tim17", "pclk2"), + PER_CLK(RCC_APB2ENR, 17, "tim16", "pclk2"), + PER_CLK(RCC_APB2ENR, 16, "tim15", "pclk2"), + PER_CLK(RCC_APB2ENR, 1, "tim8", "pclk2"), + PER_CLK(RCC_APB2ENR, 0, "tim1", "pclk2"), + PER_CLK(RCC_APB4ENR, 26, "tmpsens", "pclk4"), + PER_CLK(RCC_APB4ENR, 16, "rtcapb", "pclk4"), + PER_CLK(RCC_APB4ENR, 15, "vref", "pclk4"), + PER_CLK(RCC_APB4ENR, 14, "comp12", "pclk4"), + PER_CLK(RCC_APB4ENR, 1, "syscfg", "pclk4"), +}; + +/* KERNEL CLOCKS */ +#define KER_CLKF(_gate_offset, _bit_idx,\ + _mux_offset, _mux_shift, _mux_width,\ + _name, _parent_name,\ + _flags) \ +{ \ + .gate = &(struct gate_cfg) {_gate_offset, _bit_idx},\ + .mux = &(struct muxdiv_cfg) {_mux_offset, _mux_shift, _mux_width },\ + .name = _name, \ + .parent_name = _parent_name, \ + .num_parents = ARRAY_SIZE(_parent_name),\ + .flags = _flags,\ +} +#define KER_CLK(_gate_offset, _bit_idx, _mux_offset, _mux_shift, _mux_width,\ + _name, _parent_name) \ +KER_CLKF(_gate_offset, _bit_idx, _mux_offset, _mux_shift, _mux_width,\ + _name, _parent_name, 0)\ + +#define KER_CLKF_NOMUX(_gate_offset, _bit_idx,\ + _name, _parent_name,\ + _flags) \ +{ \ + .gate = &(struct gate_cfg) {_gate_offset, _bit_idx},\ + .mux = NULL,\ + .name = _name, \ + .parent_name = _parent_name, \ + .num_parents = 1,\ + .flags = _flags,\ +} + +static const struct composite_clk_cfg kclk[] = { + KER_CLK(RCC_AHB3ENR, 16, RCC_D1CCIPR, 16, 1, "sdmmc1", sdmmc_src), + KER_CLKF(RCC_AHB3ENR, 14, RCC_D1CCIPR, 4, 2, "quadspi", qspi_src, + CLK_IGNORE_UNUSED), + KER_CLKF(RCC_AHB3ENR, 12, RCC_D1CCIPR, 0, 2, "fmc", fmc_src, + CLK_IGNORE_UNUSED), + KER_CLK(RCC_AHB1ENR, 27, RCC_D2CCIP2R, 20, 2, "usb2otg", usbotg_src), + KER_CLK(RCC_AHB1ENR, 25, RCC_D2CCIP2R, 20, 2, "usb1otg", usbotg_src), + KER_CLK(RCC_AHB1ENR, 5, RCC_D3CCIPR, 16, 2, "adc12", adc_src), + KER_CLK(RCC_AHB2ENR, 9, RCC_D1CCIPR, 16, 1, "sdmmc2", sdmmc_src), + KER_CLK(RCC_AHB2ENR, 6, RCC_D2CCIP2R, 8, 2, "rng", rng_src), + KER_CLK(RCC_AHB4ENR, 24, RCC_D3CCIPR, 16, 2, "adc3", adc_src), + KER_CLK(RCC_APB3ENR, 4, RCC_D1CCIPR, 8, 1, "dsi", dsi_src), + KER_CLKF_NOMUX(RCC_APB3ENR, 3, "ltdc", ltdc_src, 0), + KER_CLK(RCC_APB1LENR, 31, RCC_D2CCIP2R, 0, 3, "usart8", usart_src2), + KER_CLK(RCC_APB1LENR, 30, RCC_D2CCIP2R, 0, 3, "usart7", usart_src2), + KER_CLK(RCC_APB1LENR, 27, RCC_D2CCIP2R, 22, 2, "hdmicec", cec_src), + KER_CLK(RCC_APB1LENR, 23, RCC_D2CCIP2R, 12, 2, "i2c3", i2c_src1), + KER_CLK(RCC_APB1LENR, 22, RCC_D2CCIP2R, 12, 2, "i2c2", i2c_src1), + KER_CLK(RCC_APB1LENR, 21, RCC_D2CCIP2R, 12, 2, "i2c1", i2c_src1), + KER_CLK(RCC_APB1LENR, 20, RCC_D2CCIP2R, 0, 3, "uart5", usart_src2), + KER_CLK(RCC_APB1LENR, 19, RCC_D2CCIP2R, 0, 3, "uart4", usart_src2), + KER_CLK(RCC_APB1LENR, 18, RCC_D2CCIP2R, 0, 3, "usart3", usart_src2), + KER_CLK(RCC_APB1LENR, 17, RCC_D2CCIP2R, 0, 3, "usart2", usart_src2), + KER_CLK(RCC_APB1LENR, 16, RCC_D2CCIP1R, 20, 2, "spdifrx", spdifrx_src), + KER_CLK(RCC_APB1LENR, 15, RCC_D2CCIP1R, 16, 3, "spi3", spi_src1), + KER_CLK(RCC_APB1LENR, 14, RCC_D2CCIP1R, 16, 3, "spi2", spi_src1), + KER_CLK(RCC_APB1LENR, 9, RCC_D2CCIP2R, 28, 3, "lptim1", lptim_src1), + KER_CLK(RCC_APB1HENR, 8, RCC_D2CCIP1R, 28, 2, "fdcan", fdcan_src), + KER_CLK(RCC_APB1HENR, 2, RCC_D2CCIP1R, 31, 1, "swp", swp_src), + KER_CLK(RCC_APB2ENR, 29, RCC_CFGR, 14, 1, "hrtim", hrtim_src), + KER_CLK(RCC_APB2ENR, 28, RCC_D2CCIP1R, 24, 1, "dfsdm1", dfsdm1_src), + KER_CLK(RCC_APB2ENR, 24, RCC_D2CCIP1R, 6, 3, "sai3", sai_src), + KER_CLK(RCC_APB2ENR, 23, RCC_D2CCIP1R, 6, 3, "sai2", sai_src), + KER_CLK(RCC_APB2ENR, 22, RCC_D2CCIP1R, 0, 3, "sai1", sai_src), + KER_CLK(RCC_APB2ENR, 20, RCC_D2CCIP1R, 16, 3, "spi5", spi_src2), + KER_CLK(RCC_APB2ENR, 13, RCC_D2CCIP1R, 16, 3, "spi4", spi_src2), + KER_CLK(RCC_APB2ENR, 12, RCC_D2CCIP1R, 16, 3, "spi1", spi_src1), + KER_CLK(RCC_APB2ENR, 5, RCC_D2CCIP2R, 3, 3, "usart6", usart_src1), + KER_CLK(RCC_APB2ENR, 4, RCC_D2CCIP2R, 3, 3, "usart1", usart_src1), + KER_CLK(RCC_APB4ENR, 21, RCC_D3CCIPR, 24, 3, "sai4b", sai_src), + KER_CLK(RCC_APB4ENR, 21, RCC_D3CCIPR, 21, 3, "sai4a", sai_src), + KER_CLK(RCC_APB4ENR, 12, RCC_D3CCIPR, 13, 3, "lptim5", lptim_src2), + KER_CLK(RCC_APB4ENR, 11, RCC_D3CCIPR, 13, 3, "lptim4", lptim_src2), + KER_CLK(RCC_APB4ENR, 10, RCC_D3CCIPR, 13, 3, "lptim3", lptim_src2), + KER_CLK(RCC_APB4ENR, 9, RCC_D3CCIPR, 10, 3, "lptim2", lptim_src2), + KER_CLK(RCC_APB4ENR, 7, RCC_D3CCIPR, 8, 2, "i2c4", i2c_src2), + KER_CLK(RCC_APB4ENR, 5, RCC_D3CCIPR, 28, 3, "spi6", spi_src3), + KER_CLK(RCC_APB4ENR, 3, RCC_D3CCIPR, 0, 3, "lpuart1", lpuart1_src), +}; + +static struct composite_clk_gcfg kernel_clk_cfg = { + M_CFG_MUX(NULL, 0), + M_CFG_GATE(NULL, 0), +}; + +/* RTC clock */ +static u8 rtc_mux_get_parent(struct clk_hw *hw) +{ + return clk_mux_ops.get_parent(hw); +} + +static int rtc_mux_set_parent(struct clk_hw *hw, u8 index) +{ + int dbp_status; + int err; + + dbp_status = is_enable_power_domain_write_protection(); + + if (dbp_status) + disable_power_domain_write_protection(); + + err = clk_mux_ops.set_parent(hw, index); + + if (dbp_status) + enable_power_domain_write_protection(); + + return err; +} + +static int rtc_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return clk_mux_ops.determine_rate(hw, req); +} + +static const struct clk_ops rtc_mux_ops = { + .get_parent = rtc_mux_get_parent, + .set_parent = rtc_mux_set_parent, + .determine_rate = rtc_mux_determine_rate, +}; + +/* Clock gate with backup domain protection management */ +static int bd_gate_enable(struct clk_hw *hw) +{ + int dbp_status; + int err; + + dbp_status = is_enable_power_domain_write_protection(); + + if (dbp_status) + disable_power_domain_write_protection(); + + err = clk_gate_ops.enable(hw); + + if (dbp_status) + enable_power_domain_write_protection(); + + return err; +} + +static void bd_gate_disable(struct clk_hw *hw) +{ + clk_gate_ops.disable(hw); +} + +static int bd_gate_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static const struct clk_ops bd_gate_ops = { + .enable = bd_gate_enable, + .disable = bd_gate_disable, + .is_enabled = bd_gate_is_enabled, +}; + +static struct composite_clk_gcfg rtc_clk_cfg = { + M_CFG_MUX(&rtc_mux_ops, 0), + M_CFG_GATE(&bd_gate_ops, 0), +}; + +static const struct composite_clk_cfg rtc_clk = + KER_CLK(RCC_BDCR, 15, RCC_BDCR, 8, 2, "rtc_ck", rtc_src); + +/* Micro-controller output clock */ +static struct composite_clk_gcfg mco_clk_cfg = { + M_CFG_MUX(NULL, 0), + M_CFG_DIV(NULL, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO), +}; + +#define M_MCO_F(_name, _parents, _mux_offset, _mux_shift, _mux_width,\ + _rate_offset, _rate_shift, _rate_width,\ + _flags)\ +{\ + .mux = &(struct muxdiv_cfg) {_mux_offset, _mux_shift, _mux_width },\ + .div = &(struct muxdiv_cfg) {_rate_offset, _rate_shift, _rate_width},\ + .gate = NULL,\ + .name = _name,\ + .parent_name = _parents,\ + .num_parents = ARRAY_SIZE(_parents),\ + .flags = _flags,\ +} + +static const struct composite_clk_cfg mco_clk[] = { + M_MCO_F("mco1", mco_src1, RCC_CFGR, 22, 4, RCC_CFGR, 18, 4, 0), + M_MCO_F("mco2", mco_src2, RCC_CFGR, 29, 3, RCC_CFGR, 25, 4, 0), +}; + +static void __init stm32h7_rcc_init(struct device_node *np) +{ + struct clk_hw_onecell_data *clk_data; + struct device_node *node; + struct composite_cfg c_cfg; + int n; + const char *hse_clk, *lse_clk, *i2s_clk; + + clk_data = kzalloc(sizeof(*clk_data) + + sizeof(*clk_data->hws) * STM32H7_MAX_CLKS, + GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = STM32H7_MAX_CLKS; + + hws = clk_data->hws; + + for (n = 0; n < STM32H7_MAX_CLKS; n++) + hws[n] = ERR_PTR(-ENOENT); + + /* get RCC base @ from DT */ + base = of_iomap(np, 0); + if (!base) { + pr_err("%s: unable to map resource", np->name); + goto err_free_clks; + } + + pdrm = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(pdrm)) { + pdrm = NULL; + pr_warn("%s: Unable to get syscfg\n", __func__); + } + + /* Put parent names from DT */ + hse_clk = of_clk_get_parent_name(np, 0); + lse_clk = of_clk_get_parent_name(np, 1); + i2s_clk = of_clk_get_parent_name(np, 2); + + sai_src[3] = i2s_clk; + spi_src1[3] = i2s_clk; + + /* Register Internal oscillators */ + clk_hw_register_fixed_rate(NULL, "clk-hsi", NULL, 0, 64000000); + clk_hw_register_fixed_rate(NULL, "clk-csi", NULL, 0, 4000000); + clk_hw_register_fixed_rate(NULL, "clk-lsi", NULL, 0, 32000); + clk_hw_register_fixed_rate(NULL, "clk-rc48", NULL, 0, 48000); + + /* This clock is coming from outside. Frequencies unknown */ + hws[CK_DSI_PHY] = clk_hw_register_fixed_rate(NULL, "ck_dsi_phy", NULL, + 0, 0); + + hws[HSI_DIV] = clk_hw_register_divider(NULL, "hsidiv", "clk-hsi", 0, + base + RCC_CR, 3, 2, CLK_DIVIDER_POWER_OF_TWO, + &rlock); + + hws[HSE_1M] = clk_hw_register_divider(NULL, "hse_1M", "hse_ck", 0, + base + RCC_CFGR, 8, 6, CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO, + &rlock); + + /* Mux system clocks */ + for (n = 0; n < ARRAY_SIZE(stm32_mclk); n++) + hws[MCLK_BANK + n] = clk_hw_register_mux(NULL, + stm32_mclk[n].name, + stm32_mclk[n].parents, + stm32_mclk[n].num_parents, + stm32_mclk[n].flags, + stm32_mclk[n].offset + base, + stm32_mclk[n].shift, + stm32_mclk[n].width, + 0, + &rlock); + + register_core_and_bus_clocks(); + + /* Oscillary clocks */ + for (n = 0; n < ARRAY_SIZE(stm32_oclk); n++) + hws[OSC_BANK + n] = clk_register_ready_gate(NULL, + stm32_oclk[n].name, + stm32_oclk[n].parent, + stm32_oclk[n].gate_offset + base, + stm32_oclk[n].bit_idx, + stm32_oclk[n].bit_rdy, + 0, + stm32_oclk[n].flags, + &rlock); + + hws[HSE_CK] = clk_register_ready_gate(NULL, + "hse_ck", + hse_clk, + RCC_CR + base, + 16, 17, + 0, + 0, + &rlock); + + hws[LSE_CK] = clk_register_ready_gate(NULL, + "lse_ck", + lse_clk, + RCC_BDCR + base, + 0, 1, + 1, + 0, + &rlock); + + hws[CSI_KER_DIV122 + n] = clk_hw_register_fixed_factor(NULL, + "csi_ker_div122", "csi_ker", 0, 1, 122); + + /* PLLs */ + for_each_compatible_node(node, NULL, "stm32,pll") + stm32_h7_pll_init(node); + + /* Peripheral clocks */ + for (n = 0; n < ARRAY_SIZE(pclk); n++) + hws[PERIF_BANK + n] = clk_hw_register_gate(NULL, pclk[n].name, + pclk[n].parent, + pclk[n].flags, base + pclk[n].gate_offset, + pclk[n].bit_idx, pclk[n].flags, &rlock); + + /* Kernel clocks */ + for (n = 0; n < ARRAY_SIZE(kclk); n++) { + get_cfg_composite_div(&kernel_clk_cfg, &kclk[n], &c_cfg, + &rlock); + + hws[KERN_BANK + n] = clk_hw_register_composite(NULL, + kclk[n].name, + kclk[n].parent_name, + kclk[n].num_parents, + c_cfg.mux_hw, c_cfg.mux_ops, + c_cfg.div_hw, c_cfg.div_ops, + c_cfg.gate_hw, c_cfg.gate_ops, + kclk[n].flags); + } + + /* RTC clock (default state is off) */ + clk_hw_register_fixed_rate(NULL, "off", NULL, 0, 0); + + get_cfg_composite_div(&rtc_clk_cfg, &rtc_clk, &c_cfg, &rlock); + + hws[RTC_CK] = clk_hw_register_composite(NULL, + rtc_clk.name, + rtc_clk.parent_name, + rtc_clk.num_parents, + c_cfg.mux_hw, c_cfg.mux_ops, + c_cfg.div_hw, c_cfg.div_ops, + c_cfg.gate_hw, c_cfg.gate_ops, + rtc_clk.flags); + + /* Micro-controller clocks */ + for (n = 0; n < ARRAY_SIZE(mco_clk); n++) { + get_cfg_composite_div(&mco_clk_cfg, &mco_clk[n], &c_cfg, + &rlock); + + hws[MCO_BANK + n] = clk_hw_register_composite(NULL, + mco_clk[n].name, + mco_clk[n].parent_name, + mco_clk[n].num_parents, + c_cfg.mux_hw, c_cfg.mux_ops, + c_cfg.div_hw, c_cfg.div_ops, + c_cfg.gate_hw, c_cfg.gate_ops, + mco_clk[n].flags); + } + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); + + return; + +err_free_clks: + kfree(clk_data); +} +CLK_OF_DECLARE_DRIVER(stm32h7_rcc, "st,stm32h743-rcc", stm32h7_rcc_init); diff --git a/include/dt-bindings/clock/stm32h7-clks.h b/include/dt-bindings/clock/stm32h7-clks.h new file mode 100644 index 0000000..6637272 --- /dev/null +++ b/include/dt-bindings/clock/stm32h7-clks.h @@ -0,0 +1,165 @@ +/* SYS, CORE AND BUS CLOCKS */ +#define SYS_D1CPRE 0 +#define HCLK 1 +#define PCLK1 2 +#define PCLK2 3 +#define PCLK3 4 +#define PCLK4 5 +#define HSI_DIV 6 +#define HSE_1M 7 +#define I2S_CKIN 8 +#define CK_DSI_PHY 9 +#define HSE_CK 10 +#define LSE_CK 11 +#define CSI_KER_DIV122 12 +#define RTC_CK 13 +#define CPU_SYSTICK 14 + +/* OSCILLATOR BANK */ +#define OSC_BANK 18 +#define HSI_CK 18 +#define HSI_KER_CK 19 +#define CSI_CK 20 +#define CSI_KER_CK 21 +#define RC48_CK 22 +#define LSI_CK 23 + +/* MCLOCK BANK */ +#define MCLK_BANK 28 +#define PER_CK 28 +#define PLLSRC 29 +#define SYS_CK 30 +#define TRACEIN_CK 31 + +/* ODF BANK */ +#define ODF_BANK 32 +#define PLL1_P 32 +#define PLL1_Q 33 +#define PLL1_R 34 +#define PLL2_P 35 +#define PLL2_Q 36 +#define PLL2_R 37 +#define PLL3_P 38 +#define PLL3_Q 39 +#define PLL3_R 40 + +/* MCO BANK */ +#define MCO_BANK 41 +#define MCO1 41 +#define MCO2 42 + +/* PERIF BANK */ +#define PERIF_BANK 50 +#define D1SRAM1_CK 50 +#define ITCM_CK 51 +#define DTCM2_CK 52 +#define DTCM1_CK 53 +#define FLITF_CK 54 +#define JPGDEC_CK 55 +#define DMA2D_CK 56 +#define MDMA_CK 57 +#define USB2ULPI_CK 58 +#define USB1ULPI_CK 59 +#define ETH1RX_CK 60 +#define ETH1TX_CK 61 +#define ETH1MAC_CK 62 +#define ART_CK 63 +#define DMA2_CK 64 +#define DMA1_CK 65 +#define D2SRAM3_CK 66 +#define D2SRAM2_CK 67 +#define D2SRAM1_CK 68 +#define HASH_CK 69 +#define CRYPT_CK 70 +#define CAMITF_CK 71 +#define BKPRAM_CK 72 +#define HSEM_CK 73 +#define BDMA_CK 74 +#define CRC_CK 75 +#define GPIOK_CK 76 +#define GPIOJ_CK 77 +#define GPIOI_CK 78 +#define GPIOH_CK 79 +#define GPIOG_CK 80 +#define GPIOF_CK 81 +#define GPIOE_CK 82 +#define GPIOD_CK 83 +#define GPIOC_CK 84 +#define GPIOB_CK 85 +#define GPIOA_CK 86 +#define WWDG1_CK 87 +#define DAC12_CK 88 +#define WWDG2_CK 89 +#define TIM14_CK 90 +#define TIM13_CK 91 +#define TIM12_CK 92 +#define TIM7_CK 93 +#define TIM6_CK 94 +#define TIM5_CK 95 +#define TIM4_CK 96 +#define TIM3_CK 97 +#define TIM2_CK 98 +#define MDIOS_CK 99 +#define OPAMP_CK 100 +#define CRS_CK 101 +#define TIM17_CK 102 +#define TIM16_CK 103 +#define TIM15_CK 104 +#define TIM8_CK 105 +#define TIM1_CK 106 +#define TMPSENS_CK 107 +#define RTCAPB_CK 108 +#define VREF_CK 109 +#define COMP12_CK 110 +#define SYSCFG_CK 111 + +/* KERNEL BANK */ +#define KERN_BANK 120 +#define SDMMC1_CK 120 +#define QUADSPI_CK 121 +#define FMC_CK 122 +#define USB2OTG_CK 123 +#define USB1OTG_CK 124 +#define ADC12_CK 125 +#define SDMMC2_CK 126 +#define RNG_CK 127 +#define ADC3_CK 128 +#define DSI_CK 129 +#define LTDC_CK 130 +#define USART8_CK 131 +#define USART7_CK 132 +#define HDMICEC_CK 133 +#define I2C3_CK 134 +#define I2C2_CK 135 +#define I2C1_CK 136 +#define UART5_CK 137 +#define UART4_CK 138 +#define USART3_CK 139 +#define USART2_CK 140 +#define SPDIFRX_CK 141 +#define SPI3_CK 142 +#define SPI2_CK 143 +#define LPTIM1_CK 144 +#define FDCAN_CK 145 +#define SWP_CK 146 +#define HRTIM_CK 147 +#define DFSDM1_CK 148 +#define SAI3_CK 149 +#define SAI2_CK 150 +#define SAI1_CK 151 +#define SPI5_CK 152 +#define SPI4_CK 153 +#define SPI1_CK 154 +#define USART6_CK 155 +#define USART1_CK 156 +#define SAI4B_CK 157 +#define SAI4A_CK 158 +#define LPTIM5_CK 159 +#define LPTIM4_CK 160 +#define LPTIM3_CK 161 +#define LPTIM2_CK 162 +#define I2C4_CK 163 +#define SPI6_CK 164 +#define LPUART1_CK 165 + +#define STM32H7_MAX_CLKS 166 diff --git a/include/dt-bindings/mfd/stm32h7-rcc.h b/include/dt-bindings/mfd/stm32h7-rcc.h new file mode 100644 index 0000000..b96b3c3 --- /dev/null +++ b/include/dt-bindings/mfd/stm32h7-rcc.h @@ -0,0 +1,138 @@ +/* + * This header provides constants for the STM32H7 RCC IP + */ + +#ifndef _DT_BINDINGS_MFD_STM32H7_RCC_H +#define _DT_BINDINGS_MFD_STM32H7_RCC_H + +/* AHB3 */ +#define STM32H7_RCC_AHB3_MDMA 0 +#define STM32H7_RCC_AHB3_DMA2D 4 +#define STM32H7_RCC_AHB3_JPGDEC 5 +#define STM32H7_RCC_AHB3_FMC 12 +#define STM32H7_RCC_AHB3_QUADSPI 14 +#define STM32H7_RCC_AHB3_SDMMC1 16 +#define STM32H7_RCC_AHB3_CPU1 31 + +#define STM32H7_AHB3_RESET(bit) (STM32H7_RCC_AHB3_##bit + (0x7C * 8)) + +/* AHB1 */ +#define STM32H7_RCC_AHB1_DMA1 0 +#define STM32H7_RCC_AHB1_DMA2 1 +#define STM32H7_RCC_AHB1_ADC12 5 +#define STM32H7_RCC_AHB1_ART 14 +#define STM32H7_RCC_AHB1_ETH1MAC 15 +#define STM32H7_RCC_AHB1_USB1OTG 25 +#define STM32H7_RCC_AHB1_USB2OTG 27 +#define STM32H7_RCC_AHB1_CPU2 31 + +#define STM32H7_AHB1_RESET(bit) (STM32H7_RCC_AHB1_##bit + (0x80 * 8)) + +/* AHB2 */ +#define STM32H7_RCC_AHB2_CAMITF 0 +#define STM32H7_RCC_AHB2_CRYPT 4 +#define STM32H7_RCC_AHB2_HASH 5 +#define STM32H7_RCC_AHB2_RNG 6 +#define STM32H7_RCC_AHB2_SDMMC2 9 + +#define STM32H7_AHB2_RESET(bit) (STM32H7_RCC_AHB2_##bit + (0x84 * 8)) + +/* AHB4 */ +#define STM32H7_RCC_AHB4_GPIOA 0 +#define STM32H7_RCC_AHB4_GPIOB 1 +#define STM32H7_RCC_AHB4_GPIOC 2 +#define STM32H7_RCC_AHB4_GPIOD 3 +#define STM32H7_RCC_AHB4_GPIOE 4 +#define STM32H7_RCC_AHB4_GPIOF 5 +#define STM32H7_RCC_AHB4_GPIOG 6 +#define STM32H7_RCC_AHB4_GPIOH 7 +#define STM32H7_RCC_AHB4_GPIOI 8 +#define STM32H7_RCC_AHB4_GPIOJ 9 +#define STM32H7_RCC_AHB4_GPIOK 10 +#define STM32H7_RCC_AHB4_CRC 19 +#define STM32H7_RCC_AHB4_BDMA 21 +#define STM32H7_RCC_AHB4_ADC3 24 +#define STM32H7_RCC_AHB4_HSEM 25 + +#define STM32H7_AHB4_RESET(bit) (STM32H7_RCC_AHB4_##bit + (0x88 * 8)) + + +/* APB3 */ +#define STM32H7_RCC_APB3_LTDC 3 +#define STM32H7_RCC_APB3_DSI 4 + +#define STM32H7_APB3_RESET(bit) (STM32H7_RCC_APB3_##bit + (0x8C * 8)) + +/* APB1L */ +#define STM32H7_RCC_APB1L_TIM2 0 +#define STM32H7_RCC_APB1L_TIM3 1 +#define STM32H7_RCC_APB1L_TIM4 2 +#define STM32H7_RCC_APB1L_TIM5 3 +#define STM32H7_RCC_APB1L_TIM6 4 +#define STM32H7_RCC_APB1L_TIM7 5 +#define STM32H7_RCC_APB1L_TIM12 6 +#define STM32H7_RCC_APB1L_TIM13 7 +#define STM32H7_RCC_APB1L_TIM14 8 +#define STM32H7_RCC_APB1L_LPTIM1 9 +#define STM32H7_RCC_APB1L_SPI2 14 +#define STM32H7_RCC_APB1L_SPI3 15 +#define STM32H7_RCC_APB1L_SPDIF_RX 16 +#define STM32H7_RCC_APB1L_USART2 17 +#define STM32H7_RCC_APB1L_USART3 18 +#define STM32H7_RCC_APB1L_UART4 19 +#define STM32H7_RCC_APB1L_UART5 20 +#define STM32H7_RCC_APB1L_I2C1 21 +#define STM32H7_RCC_APB1L_I2C2 22 +#define STM32H7_RCC_APB1L_I2C3 23 +#define STM32H7_RCC_APB1L_HDMICEC 27 +#define STM32H7_RCC_APB1L_DAC12 29 +#define STM32H7_RCC_APB1L_USART7 30 +#define STM32H7_RCC_APB1L_USART8 31 + +#define STM32H7_APB1L_RESET(bit) (STM32H7_RCC_APB1L_##bit + (0x90 * 8)) + +/* APB1H */ +#define STM32H7_RCC_APB1H_CRS 1 +#define STM32H7_RCC_APB1H_SWP 2 +#define STM32H7_RCC_APB1H_OPAMP 4 +#define STM32H7_RCC_APB1H_MDIOS 5 +#define STM32H7_RCC_APB1H_FDCAN 8 + +#define STM32H7_APB1H_RESET(bit) (STM32H7_RCC_APB1H_##bit + (0x94 * 8)) + +/* APB2 */ +#define STM32H7_RCC_APB2_TIM1 0 +#define STM32H7_RCC_APB2_TIM8 1 +#define STM32H7_RCC_APB2_USART1 4 +#define STM32H7_RCC_APB2_USART6 5 +#define STM32H7_RCC_APB2_SPI1 12 +#define STM32H7_RCC_APB2_SPI4 13 +#define STM32H7_RCC_APB2_TIM15 16 +#define STM32H7_RCC_APB2_TIM16 17 +#define STM32H7_RCC_APB2_TIM17 18 +#define STM32H7_RCC_APB2_SPI5 20 +#define STM32H7_RCC_APB2_SAI1 22 +#define STM32H7_RCC_APB2_SAI2 23 +#define STM32H7_RCC_APB2_SAI3 24 +#define STM32H7_RCC_APB2_DFSDM1 28 +#define STM32H7_RCC_APB2_HRTIM 29 + +#define STM32H7_APB2_RESET(bit) (STM32H7_RCC_APB2_##bit + (0x98 * 8)) + +/* APB4 */ +#define STM32H7_RCC_APB4_SYSCFG 1 +#define STM32H7_RCC_APB4_LPUART1 3 +#define STM32H7_RCC_APB4_SPI6 5 +#define STM32H7_RCC_APB4_I2C4 7 +#define STM32H7_RCC_APB4_LPTIM2 9 +#define STM32H7_RCC_APB4_LPTIM3 10 +#define STM32H7_RCC_APB4_LPTIM4 11 +#define STM32H7_RCC_APB4_LPTIM5 12 +#define STM32H7_RCC_APB4_COMP12 14 +#define STM32H7_RCC_APB4_VREF 15 +#define STM32H7_RCC_APB4_SAI4 21 +#define STM32H7_RCC_APB4_TMPSENS 26 + +#define STM32H7_APB4_RESET(bit) (STM32H7_RCC_APB4_##bit + (0x9C * 8)) + +#endif /* _DT_BINDINGS_MFD_STM32H7_RCC_H */