162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Freescale Semiconductor, Inc. 462306a36Sopenharmony_ci * Copyright 2017~2018 NXP 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/bits.h> 962306a36Sopenharmony_ci#include <linux/clk-provider.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "../clk-fractional-divider.h" 1562306a36Sopenharmony_ci#include "clk.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define PCG_PCS_SHIFT 24 1862306a36Sopenharmony_ci#define PCG_PCS_MASK 0x7 1962306a36Sopenharmony_ci#define PCG_CGC_SHIFT 30 2062306a36Sopenharmony_ci#define PCG_FRAC_SHIFT 3 2162306a36Sopenharmony_ci#define PCG_FRAC_WIDTH 1 2262306a36Sopenharmony_ci#define PCG_PCD_SHIFT 0 2362306a36Sopenharmony_ci#define PCG_PCD_WIDTH 3 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define SW_RST BIT(28) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int pcc_gate_enable(struct clk_hw *hw) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct clk_gate *gate = to_clk_gate(hw); 3062306a36Sopenharmony_ci unsigned long flags; 3162306a36Sopenharmony_ci u32 val; 3262306a36Sopenharmony_ci int ret; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci ret = clk_gate_ops.enable(hw); 3562306a36Sopenharmony_ci if (ret) 3662306a36Sopenharmony_ci return ret; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 3962306a36Sopenharmony_ci /* 4062306a36Sopenharmony_ci * release the sw reset for peripherals associated with 4162306a36Sopenharmony_ci * with this pcc clock. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ci val = readl(gate->reg); 4462306a36Sopenharmony_ci val |= SW_RST; 4562306a36Sopenharmony_ci writel(val, gate->reg); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic void pcc_gate_disable(struct clk_hw *hw) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci clk_gate_ops.disable(hw); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int pcc_gate_is_enabled(struct clk_hw *hw) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci return clk_gate_ops.is_enabled(hw); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic const struct clk_ops pcc_gate_ops = { 6362306a36Sopenharmony_ci .enable = pcc_gate_enable, 6462306a36Sopenharmony_ci .disable = pcc_gate_disable, 6562306a36Sopenharmony_ci .is_enabled = pcc_gate_is_enabled, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic struct clk_hw *imx_ulp_clk_hw_composite(const char *name, 6962306a36Sopenharmony_ci const char * const *parent_names, 7062306a36Sopenharmony_ci int num_parents, bool mux_present, 7162306a36Sopenharmony_ci bool rate_present, bool gate_present, 7262306a36Sopenharmony_ci void __iomem *reg, bool has_swrst) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct clk_hw *mux_hw = NULL, *fd_hw = NULL, *gate_hw = NULL; 7562306a36Sopenharmony_ci struct clk_fractional_divider *fd = NULL; 7662306a36Sopenharmony_ci struct clk_gate *gate = NULL; 7762306a36Sopenharmony_ci struct clk_mux *mux = NULL; 7862306a36Sopenharmony_ci struct clk_hw *hw; 7962306a36Sopenharmony_ci u32 val; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (mux_present) { 8262306a36Sopenharmony_ci mux = kzalloc(sizeof(*mux), GFP_KERNEL); 8362306a36Sopenharmony_ci if (!mux) 8462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 8562306a36Sopenharmony_ci mux_hw = &mux->hw; 8662306a36Sopenharmony_ci mux->reg = reg; 8762306a36Sopenharmony_ci mux->shift = PCG_PCS_SHIFT; 8862306a36Sopenharmony_ci mux->mask = PCG_PCS_MASK; 8962306a36Sopenharmony_ci if (has_swrst) 9062306a36Sopenharmony_ci mux->lock = &imx_ccm_lock; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (rate_present) { 9462306a36Sopenharmony_ci fd = kzalloc(sizeof(*fd), GFP_KERNEL); 9562306a36Sopenharmony_ci if (!fd) { 9662306a36Sopenharmony_ci kfree(mux); 9762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci fd_hw = &fd->hw; 10062306a36Sopenharmony_ci fd->reg = reg; 10162306a36Sopenharmony_ci fd->mshift = PCG_FRAC_SHIFT; 10262306a36Sopenharmony_ci fd->mwidth = PCG_FRAC_WIDTH; 10362306a36Sopenharmony_ci fd->nshift = PCG_PCD_SHIFT; 10462306a36Sopenharmony_ci fd->nwidth = PCG_PCD_WIDTH; 10562306a36Sopenharmony_ci fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED; 10662306a36Sopenharmony_ci if (has_swrst) 10762306a36Sopenharmony_ci fd->lock = &imx_ccm_lock; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (gate_present) { 11162306a36Sopenharmony_ci gate = kzalloc(sizeof(*gate), GFP_KERNEL); 11262306a36Sopenharmony_ci if (!gate) { 11362306a36Sopenharmony_ci kfree(mux); 11462306a36Sopenharmony_ci kfree(fd); 11562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci gate_hw = &gate->hw; 11862306a36Sopenharmony_ci gate->reg = reg; 11962306a36Sopenharmony_ci gate->bit_idx = PCG_CGC_SHIFT; 12062306a36Sopenharmony_ci if (has_swrst) 12162306a36Sopenharmony_ci gate->lock = &imx_ccm_lock; 12262306a36Sopenharmony_ci /* 12362306a36Sopenharmony_ci * make sure clock is gated during clock tree initialization, 12462306a36Sopenharmony_ci * the HW ONLY allow clock parent/rate changed with clock gated, 12562306a36Sopenharmony_ci * during clock tree initialization, clocks could be enabled 12662306a36Sopenharmony_ci * by bootloader, so the HW status will mismatch with clock tree 12762306a36Sopenharmony_ci * prepare count, then clock core driver will allow parent/rate 12862306a36Sopenharmony_ci * change since the prepare count is zero, but HW actually 12962306a36Sopenharmony_ci * prevent the parent/rate change due to the clock is enabled. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci val = readl_relaxed(reg); 13262306a36Sopenharmony_ci val &= ~(1 << PCG_CGC_SHIFT); 13362306a36Sopenharmony_ci writel_relaxed(val, reg); 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 13762306a36Sopenharmony_ci mux_hw, &clk_mux_ops, fd_hw, 13862306a36Sopenharmony_ci &clk_fractional_divider_ops, gate_hw, 13962306a36Sopenharmony_ci has_swrst ? &pcc_gate_ops : &clk_gate_ops, CLK_SET_RATE_GATE | 14062306a36Sopenharmony_ci CLK_SET_PARENT_GATE | CLK_SET_RATE_NO_REPARENT); 14162306a36Sopenharmony_ci if (IS_ERR(hw)) { 14262306a36Sopenharmony_ci kfree(mux); 14362306a36Sopenharmony_ci kfree(fd); 14462306a36Sopenharmony_ci kfree(gate); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return hw; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistruct clk_hw *imx7ulp_clk_hw_composite(const char *name, const char * const *parent_names, 15162306a36Sopenharmony_ci int num_parents, bool mux_present, bool rate_present, 15262306a36Sopenharmony_ci bool gate_present, void __iomem *reg) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present, 15562306a36Sopenharmony_ci gate_present, reg, false); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistruct clk_hw *imx8ulp_clk_hw_composite(const char *name, const char * const *parent_names, 15962306a36Sopenharmony_ci int num_parents, bool mux_present, bool rate_present, 16062306a36Sopenharmony_ci bool gate_present, void __iomem *reg, bool has_swrst) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present, 16362306a36Sopenharmony_ci gate_present, reg, has_swrst); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx8ulp_clk_hw_composite); 166