196 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright 2012 Freescale Semiconductor, Inc.4 * Copyright 2012 Linaro Ltd.5 */6 7#include <linux/bits.h>8#include <linux/clk.h>9#include <linux/clk-provider.h>10#include <linux/io.h>11#include <linux/slab.h>12#include <linux/jiffies.h>13#include <linux/err.h>14#include "clk.h"15 16static int clk_busy_wait(void __iomem *reg, u8 shift)17{18 unsigned long timeout = jiffies + msecs_to_jiffies(10);19 20 while (readl_relaxed(reg) & (1 << shift))21 if (time_after(jiffies, timeout))22 return -ETIMEDOUT;23 24 return 0;25}26 27struct clk_busy_divider {28 struct clk_divider div;29 const struct clk_ops *div_ops;30 void __iomem *reg;31 u8 shift;32};33 34static inline struct clk_busy_divider *to_clk_busy_divider(struct clk_hw *hw)35{36 struct clk_divider *div = to_clk_divider(hw);37 38 return container_of(div, struct clk_busy_divider, div);39}40 41static unsigned long clk_busy_divider_recalc_rate(struct clk_hw *hw,42 unsigned long parent_rate)43{44 struct clk_busy_divider *busy = to_clk_busy_divider(hw);45 46 return busy->div_ops->recalc_rate(&busy->div.hw, parent_rate);47}48 49static long clk_busy_divider_round_rate(struct clk_hw *hw, unsigned long rate,50 unsigned long *prate)51{52 struct clk_busy_divider *busy = to_clk_busy_divider(hw);53 54 return busy->div_ops->round_rate(&busy->div.hw, rate, prate);55}56 57static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate,58 unsigned long parent_rate)59{60 struct clk_busy_divider *busy = to_clk_busy_divider(hw);61 int ret;62 63 ret = busy->div_ops->set_rate(&busy->div.hw, rate, parent_rate);64 if (!ret)65 ret = clk_busy_wait(busy->reg, busy->shift);66 67 return ret;68}69 70static const struct clk_ops clk_busy_divider_ops = {71 .recalc_rate = clk_busy_divider_recalc_rate,72 .round_rate = clk_busy_divider_round_rate,73 .set_rate = clk_busy_divider_set_rate,74};75 76struct clk_hw *imx_clk_hw_busy_divider(const char *name, const char *parent_name,77 void __iomem *reg, u8 shift, u8 width,78 void __iomem *busy_reg, u8 busy_shift)79{80 struct clk_busy_divider *busy;81 struct clk_hw *hw;82 struct clk_init_data init;83 int ret;84 85 busy = kzalloc(sizeof(*busy), GFP_KERNEL);86 if (!busy)87 return ERR_PTR(-ENOMEM);88 89 busy->reg = busy_reg;90 busy->shift = busy_shift;91 92 busy->div.reg = reg;93 busy->div.shift = shift;94 busy->div.width = width;95 busy->div.lock = &imx_ccm_lock;96 busy->div_ops = &clk_divider_ops;97 98 init.name = name;99 init.ops = &clk_busy_divider_ops;100 init.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL;101 init.parent_names = &parent_name;102 init.num_parents = 1;103 104 busy->div.hw.init = &init;105 106 hw = &busy->div.hw;107 108 ret = clk_hw_register(NULL, hw);109 if (ret) {110 kfree(busy);111 return ERR_PTR(ret);112 }113 114 return hw;115}116 117struct clk_busy_mux {118 struct clk_mux mux;119 const struct clk_ops *mux_ops;120 void __iomem *reg;121 u8 shift;122};123 124static inline struct clk_busy_mux *to_clk_busy_mux(struct clk_hw *hw)125{126 struct clk_mux *mux = to_clk_mux(hw);127 128 return container_of(mux, struct clk_busy_mux, mux);129}130 131static u8 clk_busy_mux_get_parent(struct clk_hw *hw)132{133 struct clk_busy_mux *busy = to_clk_busy_mux(hw);134 135 return busy->mux_ops->get_parent(&busy->mux.hw);136}137 138static int clk_busy_mux_set_parent(struct clk_hw *hw, u8 index)139{140 struct clk_busy_mux *busy = to_clk_busy_mux(hw);141 int ret;142 143 ret = busy->mux_ops->set_parent(&busy->mux.hw, index);144 if (!ret)145 ret = clk_busy_wait(busy->reg, busy->shift);146 147 return ret;148}149 150static const struct clk_ops clk_busy_mux_ops = {151 .determine_rate = clk_hw_determine_rate_no_reparent,152 .get_parent = clk_busy_mux_get_parent,153 .set_parent = clk_busy_mux_set_parent,154};155 156struct clk_hw *imx_clk_hw_busy_mux(const char *name, void __iomem *reg, u8 shift,157 u8 width, void __iomem *busy_reg, u8 busy_shift,158 const char * const *parent_names, int num_parents)159{160 struct clk_busy_mux *busy;161 struct clk_hw *hw;162 struct clk_init_data init;163 int ret;164 165 busy = kzalloc(sizeof(*busy), GFP_KERNEL);166 if (!busy)167 return ERR_PTR(-ENOMEM);168 169 busy->reg = busy_reg;170 busy->shift = busy_shift;171 172 busy->mux.reg = reg;173 busy->mux.shift = shift;174 busy->mux.mask = BIT(width) - 1;175 busy->mux.lock = &imx_ccm_lock;176 busy->mux_ops = &clk_mux_ops;177 178 init.name = name;179 init.ops = &clk_busy_mux_ops;180 init.flags = CLK_IS_CRITICAL;181 init.parent_names = parent_names;182 init.num_parents = num_parents;183 184 busy->mux.hw.init = &init;185 186 hw = &busy->mux.hw;187 188 ret = clk_hw_register(NULL, hw);189 if (ret) {190 kfree(busy);191 return ERR_PTR(ret);192 }193 194 return hw;195}196