1d3ff9728SAbel Vesa // SPDX-License-Identifier: GPL-2.0 2d3ff9728SAbel Vesa /* 3d3ff9728SAbel Vesa * Copyright 2018 NXP 4d3ff9728SAbel Vesa */ 5d3ff9728SAbel Vesa 6d3ff9728SAbel Vesa #include <linux/clk-provider.h> 762e59c4eSStephen Boyd #include <linux/errno.h> 862e59c4eSStephen Boyd #include <linux/io.h> 962e59c4eSStephen Boyd #include <linux/slab.h> 10d3ff9728SAbel Vesa 11d3ff9728SAbel Vesa #include "clk.h" 12d3ff9728SAbel Vesa 13d3ff9728SAbel Vesa #define PCG_PREDIV_SHIFT 16 14d3ff9728SAbel Vesa #define PCG_PREDIV_WIDTH 3 15d3ff9728SAbel Vesa #define PCG_PREDIV_MAX 8 16d3ff9728SAbel Vesa 17d3ff9728SAbel Vesa #define PCG_DIV_SHIFT 0 18d3ff9728SAbel Vesa #define PCG_DIV_WIDTH 6 19d3ff9728SAbel Vesa #define PCG_DIV_MAX 64 20d3ff9728SAbel Vesa 21d3ff9728SAbel Vesa #define PCG_PCS_SHIFT 24 22d3ff9728SAbel Vesa #define PCG_PCS_MASK 0x7 23d3ff9728SAbel Vesa 24d3ff9728SAbel Vesa #define PCG_CGC_SHIFT 28 25d3ff9728SAbel Vesa 26d3ff9728SAbel Vesa static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk_hw *hw, 27d3ff9728SAbel Vesa unsigned long parent_rate) 28d3ff9728SAbel Vesa { 29d3ff9728SAbel Vesa struct clk_divider *divider = to_clk_divider(hw); 30d3ff9728SAbel Vesa unsigned long prediv_rate; 31d3ff9728SAbel Vesa unsigned int prediv_value; 32d3ff9728SAbel Vesa unsigned int div_value; 33d3ff9728SAbel Vesa 34d3ff9728SAbel Vesa prediv_value = readl(divider->reg) >> divider->shift; 35d3ff9728SAbel Vesa prediv_value &= clk_div_mask(divider->width); 36d3ff9728SAbel Vesa 37d3ff9728SAbel Vesa prediv_rate = divider_recalc_rate(hw, parent_rate, prediv_value, 38d3ff9728SAbel Vesa NULL, divider->flags, 39d3ff9728SAbel Vesa divider->width); 40d3ff9728SAbel Vesa 41d3ff9728SAbel Vesa div_value = readl(divider->reg) >> PCG_DIV_SHIFT; 42d3ff9728SAbel Vesa div_value &= clk_div_mask(PCG_DIV_WIDTH); 43d3ff9728SAbel Vesa 44d3ff9728SAbel Vesa return divider_recalc_rate(hw, prediv_rate, div_value, NULL, 45d3ff9728SAbel Vesa divider->flags, PCG_DIV_WIDTH); 46d3ff9728SAbel Vesa } 47d3ff9728SAbel Vesa 48d3ff9728SAbel Vesa static int imx8m_clk_composite_compute_dividers(unsigned long rate, 49d3ff9728SAbel Vesa unsigned long parent_rate, 50d3ff9728SAbel Vesa int *prediv, int *postdiv) 51d3ff9728SAbel Vesa { 52d3ff9728SAbel Vesa int div1, div2; 53d3ff9728SAbel Vesa int error = INT_MAX; 54d3ff9728SAbel Vesa int ret = -EINVAL; 55d3ff9728SAbel Vesa 56d3ff9728SAbel Vesa *prediv = 1; 57d3ff9728SAbel Vesa *postdiv = 1; 58d3ff9728SAbel Vesa 59d3ff9728SAbel Vesa for (div1 = 1; div1 <= PCG_PREDIV_MAX; div1++) { 60d3ff9728SAbel Vesa for (div2 = 1; div2 <= PCG_DIV_MAX; div2++) { 61d3ff9728SAbel Vesa int new_error = ((parent_rate / div1) / div2) - rate; 62d3ff9728SAbel Vesa 63d3ff9728SAbel Vesa if (abs(new_error) < abs(error)) { 64d3ff9728SAbel Vesa *prediv = div1; 65d3ff9728SAbel Vesa *postdiv = div2; 66d3ff9728SAbel Vesa error = new_error; 67d3ff9728SAbel Vesa ret = 0; 68d3ff9728SAbel Vesa } 69d3ff9728SAbel Vesa } 70d3ff9728SAbel Vesa } 71d3ff9728SAbel Vesa return ret; 72d3ff9728SAbel Vesa } 73d3ff9728SAbel Vesa 74d3ff9728SAbel Vesa static long imx8m_clk_composite_divider_round_rate(struct clk_hw *hw, 75d3ff9728SAbel Vesa unsigned long rate, 76d3ff9728SAbel Vesa unsigned long *prate) 77d3ff9728SAbel Vesa { 78d3ff9728SAbel Vesa int prediv_value; 79d3ff9728SAbel Vesa int div_value; 80d3ff9728SAbel Vesa 81d3ff9728SAbel Vesa imx8m_clk_composite_compute_dividers(rate, *prate, 82d3ff9728SAbel Vesa &prediv_value, &div_value); 83d3ff9728SAbel Vesa rate = DIV_ROUND_UP(*prate, prediv_value); 84d3ff9728SAbel Vesa 85d3ff9728SAbel Vesa return DIV_ROUND_UP(rate, div_value); 86d3ff9728SAbel Vesa 87d3ff9728SAbel Vesa } 88d3ff9728SAbel Vesa 89d3ff9728SAbel Vesa static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw, 90d3ff9728SAbel Vesa unsigned long rate, 91d3ff9728SAbel Vesa unsigned long parent_rate) 92d3ff9728SAbel Vesa { 93d3ff9728SAbel Vesa struct clk_divider *divider = to_clk_divider(hw); 94d3ff9728SAbel Vesa unsigned long flags = 0; 95d3ff9728SAbel Vesa int prediv_value; 96d3ff9728SAbel Vesa int div_value; 9733e7a842SColin Ian King int ret; 98d3ff9728SAbel Vesa u32 val; 99d3ff9728SAbel Vesa 100d3ff9728SAbel Vesa ret = imx8m_clk_composite_compute_dividers(rate, parent_rate, 101d3ff9728SAbel Vesa &prediv_value, &div_value); 102d3ff9728SAbel Vesa if (ret) 103d3ff9728SAbel Vesa return -EINVAL; 104d3ff9728SAbel Vesa 105d3ff9728SAbel Vesa spin_lock_irqsave(divider->lock, flags); 106d3ff9728SAbel Vesa 107d3ff9728SAbel Vesa val = readl(divider->reg); 108d3ff9728SAbel Vesa val &= ~((clk_div_mask(divider->width) << divider->shift) | 109d3ff9728SAbel Vesa (clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT)); 110d3ff9728SAbel Vesa 111d3ff9728SAbel Vesa val |= (u32)(prediv_value - 1) << divider->shift; 112d3ff9728SAbel Vesa val |= (u32)(div_value - 1) << PCG_DIV_SHIFT; 113d3ff9728SAbel Vesa writel(val, divider->reg); 114d3ff9728SAbel Vesa 115d3ff9728SAbel Vesa spin_unlock_irqrestore(divider->lock, flags); 116d3ff9728SAbel Vesa 117d3ff9728SAbel Vesa return ret; 118d3ff9728SAbel Vesa } 119d3ff9728SAbel Vesa 120d3ff9728SAbel Vesa static const struct clk_ops imx8m_clk_composite_divider_ops = { 121d3ff9728SAbel Vesa .recalc_rate = imx8m_clk_composite_divider_recalc_rate, 122d3ff9728SAbel Vesa .round_rate = imx8m_clk_composite_divider_round_rate, 123d3ff9728SAbel Vesa .set_rate = imx8m_clk_composite_divider_set_rate, 124d3ff9728SAbel Vesa }; 125d3ff9728SAbel Vesa 126d3ff9728SAbel Vesa struct clk *imx8m_clk_composite_flags(const char *name, 12765a6b7c5SAbel Vesa const char * const *parent_names, 128d3ff9728SAbel Vesa int num_parents, void __iomem *reg, 129d3ff9728SAbel Vesa unsigned long flags) 130d3ff9728SAbel Vesa { 131d3ff9728SAbel Vesa struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw; 132d3ff9728SAbel Vesa struct clk_hw *div_hw, *gate_hw; 133d3ff9728SAbel Vesa struct clk_divider *div = NULL; 134d3ff9728SAbel Vesa struct clk_gate *gate = NULL; 135d3ff9728SAbel Vesa struct clk_mux *mux = NULL; 136d3ff9728SAbel Vesa 137d3ff9728SAbel Vesa mux = kzalloc(sizeof(*mux), GFP_KERNEL); 138d3ff9728SAbel Vesa if (!mux) 139d3ff9728SAbel Vesa goto fail; 140d3ff9728SAbel Vesa 141d3ff9728SAbel Vesa mux_hw = &mux->hw; 142d3ff9728SAbel Vesa mux->reg = reg; 143d3ff9728SAbel Vesa mux->shift = PCG_PCS_SHIFT; 144d3ff9728SAbel Vesa mux->mask = PCG_PCS_MASK; 145d3ff9728SAbel Vesa 146d3ff9728SAbel Vesa div = kzalloc(sizeof(*div), GFP_KERNEL); 147d3ff9728SAbel Vesa if (!div) 148d3ff9728SAbel Vesa goto fail; 149d3ff9728SAbel Vesa 150d3ff9728SAbel Vesa div_hw = &div->hw; 151d3ff9728SAbel Vesa div->reg = reg; 152d3ff9728SAbel Vesa div->shift = PCG_PREDIV_SHIFT; 153d3ff9728SAbel Vesa div->width = PCG_PREDIV_WIDTH; 154d3ff9728SAbel Vesa div->lock = &imx_ccm_lock; 155d3ff9728SAbel Vesa div->flags = CLK_DIVIDER_ROUND_CLOSEST; 156d3ff9728SAbel Vesa 157d3ff9728SAbel Vesa gate = kzalloc(sizeof(*gate), GFP_KERNEL); 158d3ff9728SAbel Vesa if (!gate) 159d3ff9728SAbel Vesa goto fail; 160d3ff9728SAbel Vesa 161d3ff9728SAbel Vesa gate_hw = &gate->hw; 162d3ff9728SAbel Vesa gate->reg = reg; 163d3ff9728SAbel Vesa gate->bit_idx = PCG_CGC_SHIFT; 164d3ff9728SAbel Vesa 165d3ff9728SAbel Vesa hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 166d3ff9728SAbel Vesa mux_hw, &clk_mux_ops, div_hw, 167d3ff9728SAbel Vesa &imx8m_clk_composite_divider_ops, 168d3ff9728SAbel Vesa gate_hw, &clk_gate_ops, flags); 169d3ff9728SAbel Vesa if (IS_ERR(hw)) 170d3ff9728SAbel Vesa goto fail; 171d3ff9728SAbel Vesa 172d3ff9728SAbel Vesa return hw->clk; 173d3ff9728SAbel Vesa 174d3ff9728SAbel Vesa fail: 175d3ff9728SAbel Vesa kfree(gate); 176d3ff9728SAbel Vesa kfree(div); 177d3ff9728SAbel Vesa kfree(mux); 178d3ff9728SAbel Vesa return ERR_CAST(hw); 179d3ff9728SAbel Vesa } 180