diff options
| -rw-r--r-- | arch/arm/mach-imx/Makefile | 2 | ||||
| -rw-r--r-- | arch/arm/mach-imx/clk-busy.c | 189 | ||||
| -rw-r--r-- | arch/arm/mach-imx/clk.h | 8 |
3 files changed, 198 insertions, 1 deletions
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 4d6be8d33a26..ae0a77960c22 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile | |||
| @@ -12,7 +12,7 @@ obj-$(CONFIG_SOC_IMX35) += mm-imx3.o cpu-imx35.o clock-imx35.o ehci-imx35.o pm-i | |||
| 12 | obj-$(CONFIG_SOC_IMX5) += cpu-imx5.o mm-imx5.o clock-mx51-mx53.o ehci-imx5.o pm-imx5.o cpu_op-mx51.o | 12 | obj-$(CONFIG_SOC_IMX5) += cpu-imx5.o mm-imx5.o clock-mx51-mx53.o ehci-imx5.o pm-imx5.o cpu_op-mx51.o |
| 13 | 13 | ||
| 14 | obj-$(CONFIG_COMMON_CLK) += clk-pllv1.o clk-pllv2.o clk-pllv3.o clk-gate2.o \ | 14 | obj-$(CONFIG_COMMON_CLK) += clk-pllv1.o clk-pllv2.o clk-pllv3.o clk-gate2.o \ |
| 15 | clk-pfd.o | 15 | clk-pfd.o clk-busy.o |
| 16 | 16 | ||
| 17 | # Support for CMOS sensor interface | 17 | # Support for CMOS sensor interface |
| 18 | obj-$(CONFIG_MX1_VIDEO) += mx1-camera-fiq.o mx1-camera-fiq-ksym.o | 18 | obj-$(CONFIG_MX1_VIDEO) += mx1-camera-fiq.o mx1-camera-fiq-ksym.o |
diff --git a/arch/arm/mach-imx/clk-busy.c b/arch/arm/mach-imx/clk-busy.c new file mode 100644 index 000000000000..1a7a8dd045a1 --- /dev/null +++ b/arch/arm/mach-imx/clk-busy.c | |||
| @@ -0,0 +1,189 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2012 Freescale Semiconductor, Inc. | ||
| 3 | * Copyright 2012 Linaro Ltd. | ||
| 4 | * | ||
| 5 | * The code contained herein is licensed under the GNU General Public | ||
| 6 | * License. You may obtain a copy of the GNU General Public License | ||
| 7 | * Version 2 or later at the following locations: | ||
| 8 | * | ||
| 9 | * http://www.opensource.org/licenses/gpl-license.html | ||
| 10 | * http://www.gnu.org/copyleft/gpl.html | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/clk.h> | ||
| 14 | #include <linux/clk-provider.h> | ||
| 15 | #include <linux/io.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/jiffies.h> | ||
| 18 | #include <linux/err.h> | ||
| 19 | #include "clk.h" | ||
| 20 | |||
| 21 | static int clk_busy_wait(void __iomem *reg, u8 shift) | ||
| 22 | { | ||
| 23 | unsigned long timeout = jiffies + msecs_to_jiffies(10); | ||
| 24 | |||
| 25 | while (readl_relaxed(reg) & (1 << shift)) | ||
| 26 | if (time_after(jiffies, timeout)) | ||
| 27 | return -ETIMEDOUT; | ||
| 28 | |||
| 29 | return 0; | ||
| 30 | } | ||
| 31 | |||
| 32 | struct clk_busy_divider { | ||
| 33 | struct clk_divider div; | ||
| 34 | const struct clk_ops *div_ops; | ||
| 35 | void __iomem *reg; | ||
| 36 | u8 shift; | ||
| 37 | }; | ||
| 38 | |||
| 39 | static inline struct clk_busy_divider *to_clk_busy_divider(struct clk_hw *hw) | ||
| 40 | { | ||
| 41 | struct clk_divider *div = container_of(hw, struct clk_divider, hw); | ||
| 42 | |||
| 43 | return container_of(div, struct clk_busy_divider, div); | ||
| 44 | } | ||
| 45 | |||
| 46 | static unsigned long clk_busy_divider_recalc_rate(struct clk_hw *hw, | ||
| 47 | unsigned long parent_rate) | ||
| 48 | { | ||
| 49 | struct clk_busy_divider *busy = to_clk_busy_divider(hw); | ||
| 50 | |||
| 51 | return busy->div_ops->recalc_rate(&busy->div.hw, parent_rate); | ||
| 52 | } | ||
| 53 | |||
| 54 | static long clk_busy_divider_round_rate(struct clk_hw *hw, unsigned long rate, | ||
| 55 | unsigned long *prate) | ||
| 56 | { | ||
| 57 | struct clk_busy_divider *busy = to_clk_busy_divider(hw); | ||
| 58 | |||
| 59 | return busy->div_ops->round_rate(&busy->div.hw, rate, prate); | ||
| 60 | } | ||
| 61 | |||
| 62 | static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate, | ||
| 63 | unsigned long parent_rate) | ||
| 64 | { | ||
| 65 | struct clk_busy_divider *busy = to_clk_busy_divider(hw); | ||
| 66 | int ret; | ||
| 67 | |||
| 68 | ret = busy->div_ops->set_rate(&busy->div.hw, rate, parent_rate); | ||
| 69 | if (!ret) | ||
| 70 | ret = clk_busy_wait(busy->reg, busy->shift); | ||
| 71 | |||
| 72 | return ret; | ||
| 73 | } | ||
| 74 | |||
| 75 | static struct clk_ops clk_busy_divider_ops = { | ||
| 76 | .recalc_rate = clk_busy_divider_recalc_rate, | ||
| 77 | .round_rate = clk_busy_divider_round_rate, | ||
| 78 | .set_rate = clk_busy_divider_set_rate, | ||
| 79 | }; | ||
| 80 | |||
| 81 | struct clk *imx_clk_busy_divider(const char *name, const char *parent_name, | ||
| 82 | void __iomem *reg, u8 shift, u8 width, | ||
| 83 | void __iomem *busy_reg, u8 busy_shift) | ||
| 84 | { | ||
| 85 | struct clk_busy_divider *busy; | ||
| 86 | struct clk *clk; | ||
| 87 | struct clk_init_data init; | ||
| 88 | |||
| 89 | busy = kzalloc(sizeof(*busy), GFP_KERNEL); | ||
| 90 | if (!busy) | ||
| 91 | return ERR_PTR(-ENOMEM); | ||
| 92 | |||
| 93 | busy->reg = busy_reg; | ||
| 94 | busy->shift = busy_shift; | ||
| 95 | |||
| 96 | busy->div.reg = reg; | ||
| 97 | busy->div.shift = shift; | ||
| 98 | busy->div.width = width; | ||
| 99 | busy->div.lock = &imx_ccm_lock; | ||
| 100 | busy->div_ops = &clk_divider_ops; | ||
| 101 | |||
| 102 | init.name = name; | ||
| 103 | init.ops = &clk_busy_divider_ops; | ||
| 104 | init.flags = CLK_SET_RATE_PARENT; | ||
| 105 | init.parent_names = &parent_name; | ||
| 106 | init.num_parents = 1; | ||
| 107 | |||
| 108 | busy->div.hw.init = &init; | ||
| 109 | |||
| 110 | clk = clk_register(NULL, &busy->div.hw); | ||
| 111 | if (!clk) | ||
| 112 | kfree(busy); | ||
| 113 | |||
| 114 | return clk; | ||
| 115 | } | ||
| 116 | |||
| 117 | struct clk_busy_mux { | ||
| 118 | struct clk_mux mux; | ||
| 119 | const struct clk_ops *mux_ops; | ||
| 120 | void __iomem *reg; | ||
| 121 | u8 shift; | ||
| 122 | }; | ||
| 123 | |||
| 124 | static inline struct clk_busy_mux *to_clk_busy_mux(struct clk_hw *hw) | ||
| 125 | { | ||
| 126 | struct clk_mux *mux = container_of(hw, struct clk_mux, hw); | ||
| 127 | |||
| 128 | return container_of(mux, struct clk_busy_mux, mux); | ||
| 129 | } | ||
| 130 | |||
| 131 | static 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 | |||
| 138 | static 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 | |||
| 150 | struct clk_ops clk_busy_mux_ops = { | ||
| 151 | .get_parent = clk_busy_mux_get_parent, | ||
| 152 | .set_parent = clk_busy_mux_set_parent, | ||
| 153 | }; | ||
| 154 | |||
| 155 | struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift, | ||
| 156 | u8 width, void __iomem *busy_reg, u8 busy_shift, | ||
| 157 | const char **parent_names, int num_parents) | ||
| 158 | { | ||
| 159 | struct clk_busy_mux *busy; | ||
| 160 | struct clk *clk; | ||
| 161 | struct clk_init_data init; | ||
| 162 | |||
| 163 | busy = kzalloc(sizeof(*busy), GFP_KERNEL); | ||
| 164 | if (!busy) | ||
| 165 | return ERR_PTR(-ENOMEM); | ||
| 166 | |||
| 167 | busy->reg = busy_reg; | ||
| 168 | busy->shift = busy_shift; | ||
| 169 | |||
| 170 | busy->mux.reg = reg; | ||
| 171 | busy->mux.shift = shift; | ||
| 172 | busy->mux.width = width; | ||
| 173 | busy->mux.lock = &imx_ccm_lock; | ||
| 174 | busy->mux_ops = &clk_mux_ops; | ||
| 175 | |||
| 176 | init.name = name; | ||
| 177 | init.ops = &clk_busy_mux_ops; | ||
| 178 | init.flags = 0; | ||
| 179 | init.parent_names = parent_names; | ||
| 180 | init.num_parents = num_parents; | ||
| 181 | |||
| 182 | busy->mux.hw.init = &init; | ||
| 183 | |||
| 184 | clk = clk_register(NULL, &busy->mux.hw); | ||
| 185 | if (IS_ERR(clk)) | ||
| 186 | kfree(busy); | ||
| 187 | |||
| 188 | return clk; | ||
| 189 | } | ||
diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index 52970996f39a..1bf64fe2523c 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h | |||
| @@ -39,6 +39,14 @@ static inline struct clk *imx_clk_gate2(const char *name, const char *parent, | |||
| 39 | struct clk *imx_clk_pfd(const char *name, const char *parent_name, | 39 | struct clk *imx_clk_pfd(const char *name, const char *parent_name, |
| 40 | void __iomem *reg, u8 idx); | 40 | void __iomem *reg, u8 idx); |
| 41 | 41 | ||
| 42 | struct clk *imx_clk_busy_divider(const char *name, const char *parent_name, | ||
| 43 | void __iomem *reg, u8 shift, u8 width, | ||
| 44 | void __iomem *busy_reg, u8 busy_shift); | ||
| 45 | |||
| 46 | struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift, | ||
| 47 | u8 width, void __iomem *busy_reg, u8 busy_shift, | ||
| 48 | const char **parent_names, int num_parents); | ||
| 49 | |||
| 42 | static inline struct clk *imx_clk_fixed(const char *name, int rate) | 50 | static inline struct clk *imx_clk_fixed(const char *name, int rate) |
| 43 | { | 51 | { |
| 44 | return clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate); | 52 | return clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate); |
