111f68120SShawn Guo /* 211f68120SShawn Guo * Copyright 2012 Freescale Semiconductor, Inc. 311f68120SShawn Guo * Copyright 2012 Linaro Ltd. 411f68120SShawn Guo * 511f68120SShawn Guo * The code contained herein is licensed under the GNU General Public 611f68120SShawn Guo * License. You may obtain a copy of the GNU General Public License 711f68120SShawn Guo * Version 2 or later at the following locations: 811f68120SShawn Guo * 911f68120SShawn Guo * http://www.opensource.org/licenses/gpl-license.html 1011f68120SShawn Guo * http://www.gnu.org/copyleft/gpl.html 1111f68120SShawn Guo */ 1211f68120SShawn Guo 1311f68120SShawn Guo #include <linux/clk.h> 1411f68120SShawn Guo #include <linux/clk-provider.h> 1511f68120SShawn Guo #include <linux/io.h> 1611f68120SShawn Guo #include <linux/slab.h> 1711f68120SShawn Guo #include <linux/jiffies.h> 1811f68120SShawn Guo #include <linux/err.h> 1911f68120SShawn Guo #include "clk.h" 2011f68120SShawn Guo 2111f68120SShawn Guo static int clk_busy_wait(void __iomem *reg, u8 shift) 2211f68120SShawn Guo { 2311f68120SShawn Guo unsigned long timeout = jiffies + msecs_to_jiffies(10); 2411f68120SShawn Guo 2511f68120SShawn Guo while (readl_relaxed(reg) & (1 << shift)) 2611f68120SShawn Guo if (time_after(jiffies, timeout)) 2711f68120SShawn Guo return -ETIMEDOUT; 2811f68120SShawn Guo 2911f68120SShawn Guo return 0; 3011f68120SShawn Guo } 3111f68120SShawn Guo 3211f68120SShawn Guo struct clk_busy_divider { 3311f68120SShawn Guo struct clk_divider div; 3411f68120SShawn Guo const struct clk_ops *div_ops; 3511f68120SShawn Guo void __iomem *reg; 3611f68120SShawn Guo u8 shift; 3711f68120SShawn Guo }; 3811f68120SShawn Guo 3911f68120SShawn Guo static inline struct clk_busy_divider *to_clk_busy_divider(struct clk_hw *hw) 4011f68120SShawn Guo { 4111f68120SShawn Guo struct clk_divider *div = container_of(hw, struct clk_divider, hw); 4211f68120SShawn Guo 4311f68120SShawn Guo return container_of(div, struct clk_busy_divider, div); 4411f68120SShawn Guo } 4511f68120SShawn Guo 4611f68120SShawn Guo static unsigned long clk_busy_divider_recalc_rate(struct clk_hw *hw, 4711f68120SShawn Guo unsigned long parent_rate) 4811f68120SShawn Guo { 4911f68120SShawn Guo struct clk_busy_divider *busy = to_clk_busy_divider(hw); 5011f68120SShawn Guo 5111f68120SShawn Guo return busy->div_ops->recalc_rate(&busy->div.hw, parent_rate); 5211f68120SShawn Guo } 5311f68120SShawn Guo 5411f68120SShawn Guo static long clk_busy_divider_round_rate(struct clk_hw *hw, unsigned long rate, 5511f68120SShawn Guo unsigned long *prate) 5611f68120SShawn Guo { 5711f68120SShawn Guo struct clk_busy_divider *busy = to_clk_busy_divider(hw); 5811f68120SShawn Guo 5911f68120SShawn Guo return busy->div_ops->round_rate(&busy->div.hw, rate, prate); 6011f68120SShawn Guo } 6111f68120SShawn Guo 6211f68120SShawn Guo static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate, 6311f68120SShawn Guo unsigned long parent_rate) 6411f68120SShawn Guo { 6511f68120SShawn Guo struct clk_busy_divider *busy = to_clk_busy_divider(hw); 6611f68120SShawn Guo int ret; 6711f68120SShawn Guo 6811f68120SShawn Guo ret = busy->div_ops->set_rate(&busy->div.hw, rate, parent_rate); 6911f68120SShawn Guo if (!ret) 7011f68120SShawn Guo ret = clk_busy_wait(busy->reg, busy->shift); 7111f68120SShawn Guo 7211f68120SShawn Guo return ret; 7311f68120SShawn Guo } 7411f68120SShawn Guo 7511f68120SShawn Guo static struct clk_ops clk_busy_divider_ops = { 7611f68120SShawn Guo .recalc_rate = clk_busy_divider_recalc_rate, 7711f68120SShawn Guo .round_rate = clk_busy_divider_round_rate, 7811f68120SShawn Guo .set_rate = clk_busy_divider_set_rate, 7911f68120SShawn Guo }; 8011f68120SShawn Guo 8111f68120SShawn Guo struct clk *imx_clk_busy_divider(const char *name, const char *parent_name, 8211f68120SShawn Guo void __iomem *reg, u8 shift, u8 width, 8311f68120SShawn Guo void __iomem *busy_reg, u8 busy_shift) 8411f68120SShawn Guo { 8511f68120SShawn Guo struct clk_busy_divider *busy; 8611f68120SShawn Guo struct clk *clk; 8711f68120SShawn Guo struct clk_init_data init; 8811f68120SShawn Guo 8911f68120SShawn Guo busy = kzalloc(sizeof(*busy), GFP_KERNEL); 9011f68120SShawn Guo if (!busy) 9111f68120SShawn Guo return ERR_PTR(-ENOMEM); 9211f68120SShawn Guo 9311f68120SShawn Guo busy->reg = busy_reg; 9411f68120SShawn Guo busy->shift = busy_shift; 9511f68120SShawn Guo 9611f68120SShawn Guo busy->div.reg = reg; 9711f68120SShawn Guo busy->div.shift = shift; 9811f68120SShawn Guo busy->div.width = width; 9911f68120SShawn Guo busy->div.lock = &imx_ccm_lock; 10011f68120SShawn Guo busy->div_ops = &clk_divider_ops; 10111f68120SShawn Guo 10211f68120SShawn Guo init.name = name; 10311f68120SShawn Guo init.ops = &clk_busy_divider_ops; 10411f68120SShawn Guo init.flags = CLK_SET_RATE_PARENT; 10511f68120SShawn Guo init.parent_names = &parent_name; 10611f68120SShawn Guo init.num_parents = 1; 10711f68120SShawn Guo 10811f68120SShawn Guo busy->div.hw.init = &init; 10911f68120SShawn Guo 11011f68120SShawn Guo clk = clk_register(NULL, &busy->div.hw); 11111f68120SShawn Guo if (IS_ERR(clk)) 11211f68120SShawn Guo kfree(busy); 11311f68120SShawn Guo 11411f68120SShawn Guo return clk; 11511f68120SShawn Guo } 11611f68120SShawn Guo 11711f68120SShawn Guo struct clk_busy_mux { 11811f68120SShawn Guo struct clk_mux mux; 11911f68120SShawn Guo const struct clk_ops *mux_ops; 12011f68120SShawn Guo void __iomem *reg; 12111f68120SShawn Guo u8 shift; 12211f68120SShawn Guo }; 12311f68120SShawn Guo 12411f68120SShawn Guo static inline struct clk_busy_mux *to_clk_busy_mux(struct clk_hw *hw) 12511f68120SShawn Guo { 12611f68120SShawn Guo struct clk_mux *mux = container_of(hw, struct clk_mux, hw); 12711f68120SShawn Guo 12811f68120SShawn Guo return container_of(mux, struct clk_busy_mux, mux); 12911f68120SShawn Guo } 13011f68120SShawn Guo 13111f68120SShawn Guo static u8 clk_busy_mux_get_parent(struct clk_hw *hw) 13211f68120SShawn Guo { 13311f68120SShawn Guo struct clk_busy_mux *busy = to_clk_busy_mux(hw); 13411f68120SShawn Guo 13511f68120SShawn Guo return busy->mux_ops->get_parent(&busy->mux.hw); 13611f68120SShawn Guo } 13711f68120SShawn Guo 13811f68120SShawn Guo static int clk_busy_mux_set_parent(struct clk_hw *hw, u8 index) 13911f68120SShawn Guo { 14011f68120SShawn Guo struct clk_busy_mux *busy = to_clk_busy_mux(hw); 14111f68120SShawn Guo int ret; 14211f68120SShawn Guo 14311f68120SShawn Guo ret = busy->mux_ops->set_parent(&busy->mux.hw, index); 14411f68120SShawn Guo if (!ret) 14511f68120SShawn Guo ret = clk_busy_wait(busy->reg, busy->shift); 14611f68120SShawn Guo 14711f68120SShawn Guo return ret; 14811f68120SShawn Guo } 14911f68120SShawn Guo 15011f68120SShawn Guo static struct clk_ops clk_busy_mux_ops = { 15111f68120SShawn Guo .get_parent = clk_busy_mux_get_parent, 15211f68120SShawn Guo .set_parent = clk_busy_mux_set_parent, 15311f68120SShawn Guo }; 15411f68120SShawn Guo 15511f68120SShawn Guo struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift, 15611f68120SShawn Guo u8 width, void __iomem *busy_reg, u8 busy_shift, 15711f68120SShawn Guo const char **parent_names, int num_parents) 15811f68120SShawn Guo { 15911f68120SShawn Guo struct clk_busy_mux *busy; 16011f68120SShawn Guo struct clk *clk; 16111f68120SShawn Guo struct clk_init_data init; 16211f68120SShawn Guo 16311f68120SShawn Guo busy = kzalloc(sizeof(*busy), GFP_KERNEL); 16411f68120SShawn Guo if (!busy) 16511f68120SShawn Guo return ERR_PTR(-ENOMEM); 16611f68120SShawn Guo 16711f68120SShawn Guo busy->reg = busy_reg; 16811f68120SShawn Guo busy->shift = busy_shift; 16911f68120SShawn Guo 17011f68120SShawn Guo busy->mux.reg = reg; 17111f68120SShawn Guo busy->mux.shift = shift; 17211f68120SShawn Guo busy->mux.mask = BIT(width) - 1; 17311f68120SShawn Guo busy->mux.lock = &imx_ccm_lock; 17411f68120SShawn Guo busy->mux_ops = &clk_mux_ops; 17511f68120SShawn Guo 17611f68120SShawn Guo init.name = name; 17711f68120SShawn Guo init.ops = &clk_busy_mux_ops; 17811f68120SShawn Guo init.flags = 0; 17911f68120SShawn Guo init.parent_names = parent_names; 18011f68120SShawn Guo init.num_parents = num_parents; 18111f68120SShawn Guo 18211f68120SShawn Guo busy->mux.hw.init = &init; 18311f68120SShawn Guo 18411f68120SShawn Guo clk = clk_register(NULL, &busy->mux.hw); 18511f68120SShawn Guo if (IS_ERR(clk)) 18611f68120SShawn Guo kfree(busy); 18711f68120SShawn Guo 18811f68120SShawn Guo return clk; 18911f68120SShawn Guo } 190