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