1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2012 Freescale Semiconductor, Inc. 4 */ 5 6 #include <linux/clk-provider.h> 7 #include <linux/err.h> 8 #include <linux/slab.h> 9 #include "clk.h" 10 11 /** 12 * struct clk_div - mxs integer divider clock 13 * @divider: the parent class 14 * @ops: pointer to clk_ops of parent class 15 * @reg: register address 16 * @busy: busy bit shift 17 * 18 * The mxs divider clock is a subclass of basic clk_divider with an 19 * addtional busy bit. 20 */ 21 struct clk_div { 22 struct clk_divider divider; 23 const struct clk_ops *ops; 24 void __iomem *reg; 25 u8 busy; 26 }; 27 28 static inline struct clk_div *to_clk_div(struct clk_hw *hw) 29 { 30 struct clk_divider *divider = to_clk_divider(hw); 31 32 return container_of(divider, struct clk_div, divider); 33 } 34 35 static unsigned long clk_div_recalc_rate(struct clk_hw *hw, 36 unsigned long parent_rate) 37 { 38 struct clk_div *div = to_clk_div(hw); 39 40 return div->ops->recalc_rate(&div->divider.hw, parent_rate); 41 } 42 43 static long clk_div_round_rate(struct clk_hw *hw, unsigned long rate, 44 unsigned long *prate) 45 { 46 struct clk_div *div = to_clk_div(hw); 47 48 return div->ops->round_rate(&div->divider.hw, rate, prate); 49 } 50 51 static int clk_div_set_rate(struct clk_hw *hw, unsigned long rate, 52 unsigned long parent_rate) 53 { 54 struct clk_div *div = to_clk_div(hw); 55 int ret; 56 57 ret = div->ops->set_rate(&div->divider.hw, rate, parent_rate); 58 if (!ret) 59 ret = mxs_clk_wait(div->reg, div->busy); 60 61 return ret; 62 } 63 64 static const struct clk_ops clk_div_ops = { 65 .recalc_rate = clk_div_recalc_rate, 66 .round_rate = clk_div_round_rate, 67 .set_rate = clk_div_set_rate, 68 }; 69 70 struct clk *mxs_clk_div(const char *name, const char *parent_name, 71 void __iomem *reg, u8 shift, u8 width, u8 busy) 72 { 73 struct clk_div *div; 74 struct clk *clk; 75 struct clk_init_data init; 76 77 div = kzalloc(sizeof(*div), GFP_KERNEL); 78 if (!div) 79 return ERR_PTR(-ENOMEM); 80 81 init.name = name; 82 init.ops = &clk_div_ops; 83 init.flags = CLK_SET_RATE_PARENT; 84 init.parent_names = (parent_name ? &parent_name: NULL); 85 init.num_parents = (parent_name ? 1 : 0); 86 87 div->reg = reg; 88 div->busy = busy; 89 90 div->divider.reg = reg; 91 div->divider.shift = shift; 92 div->divider.width = width; 93 div->divider.flags = CLK_DIVIDER_ONE_BASED; 94 div->divider.lock = &mxs_lock; 95 div->divider.hw.init = &init; 96 div->ops = &clk_divider_ops; 97 98 clk = clk_register(NULL, &div->divider.hw); 99 if (IS_ERR(clk)) 100 kfree(div); 101 102 return clk; 103 } 104