diff options
| -rw-r--r-- | arch/arm/mach-exynos/exynos.c | 21 | ||||
| -rw-r--r-- | drivers/clk/clk.c | 3 | ||||
| -rw-r--r-- | drivers/clk/samsung/Makefile | 2 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk-cpu.c | 349 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk-cpu.h | 73 | ||||
| -rw-r--r-- | drivers/clk/samsung/clk-exynos4.c | 24 | ||||
| -rw-r--r-- | drivers/cpufreq/Kconfig.arm | 11 | ||||
| -rw-r--r-- | drivers/cpufreq/Makefile | 1 | ||||
| -rw-r--r-- | drivers/cpufreq/exynos-cpufreq.c | 5 | ||||
| -rw-r--r-- | drivers/cpufreq/exynos-cpufreq.h | 9 | ||||
| -rw-r--r-- | drivers/cpufreq/exynos4210-cpufreq.c | 184 | ||||
| -rw-r--r-- | include/linux/clk-provider.h | 1 |
12 files changed, 471 insertions, 212 deletions
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index 5917a30eee33..9cb17ff1e152 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c | |||
| @@ -224,6 +224,25 @@ static void __init exynos_init_irq(void) | |||
| 224 | exynos_map_pmu(); | 224 | exynos_map_pmu(); |
| 225 | } | 225 | } |
| 226 | 226 | ||
| 227 | static const struct of_device_id exynos_cpufreq_matches[] = { | ||
| 228 | { .compatible = "samsung,exynos4210", .data = "cpufreq-dt" }, | ||
| 229 | { /* sentinel */ } | ||
| 230 | }; | ||
| 231 | |||
| 232 | static void __init exynos_cpufreq_init(void) | ||
| 233 | { | ||
| 234 | struct device_node *root = of_find_node_by_path("/"); | ||
| 235 | const struct of_device_id *match; | ||
| 236 | |||
| 237 | match = of_match_node(exynos_cpufreq_matches, root); | ||
| 238 | if (!match) { | ||
| 239 | platform_device_register_simple("exynos-cpufreq", -1, NULL, 0); | ||
| 240 | return; | ||
| 241 | } | ||
| 242 | |||
| 243 | platform_device_register_simple(match->data, -1, NULL, 0); | ||
| 244 | } | ||
| 245 | |||
| 227 | static void __init exynos_dt_machine_init(void) | 246 | static void __init exynos_dt_machine_init(void) |
| 228 | { | 247 | { |
| 229 | /* | 248 | /* |
| @@ -245,7 +264,7 @@ static void __init exynos_dt_machine_init(void) | |||
| 245 | of_machine_is_compatible("samsung,exynos5250")) | 264 | of_machine_is_compatible("samsung,exynos5250")) |
| 246 | platform_device_register(&exynos_cpuidle); | 265 | platform_device_register(&exynos_cpuidle); |
| 247 | 266 | ||
| 248 | platform_device_register_simple("exynos-cpufreq", -1, NULL, 0); | 267 | exynos_cpufreq_init(); |
| 249 | 268 | ||
| 250 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); | 269 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); |
| 251 | } | 270 | } |
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 1cf479b9f3b4..059e5d25c9ba 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c | |||
| @@ -1417,6 +1417,9 @@ static void clk_change_rate(struct clk_core *core) | |||
| 1417 | if (core->notifier_count && old_rate != core->rate) | 1417 | if (core->notifier_count && old_rate != core->rate) |
| 1418 | __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); | 1418 | __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); |
| 1419 | 1419 | ||
| 1420 | if (core->flags & CLK_RECALC_NEW_RATES) | ||
| 1421 | (void)clk_calc_new_rates(core, core->new_rate); | ||
| 1422 | |||
| 1420 | /* | 1423 | /* |
| 1421 | * Use safe iteration, as change_rate can actually swap parents | 1424 | * Use safe iteration, as change_rate can actually swap parents |
| 1422 | * for certain clock types. | 1425 | * for certain clock types. |
diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index a17683b2cf27..5f6833ea355d 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | # Samsung Clock specific Makefile | 2 | # Samsung Clock specific Makefile |
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o | 5 | obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o clk-cpu.o |
| 6 | obj-$(CONFIG_SOC_EXYNOS3250) += clk-exynos3250.o | 6 | obj-$(CONFIG_SOC_EXYNOS3250) += clk-exynos3250.o |
| 7 | obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o | 7 | obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o |
| 8 | obj-$(CONFIG_SOC_EXYNOS4415) += clk-exynos4415.o | 8 | obj-$(CONFIG_SOC_EXYNOS4415) += clk-exynos4415.o |
diff --git a/drivers/clk/samsung/clk-cpu.c b/drivers/clk/samsung/clk-cpu.c new file mode 100644 index 000000000000..3a1fe07cfe9e --- /dev/null +++ b/drivers/clk/samsung/clk-cpu.c | |||
| @@ -0,0 +1,349 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2014 Samsung Electronics Co., Ltd. | ||
| 3 | * Author: Thomas Abraham <thomas.ab@samsung.com> | ||
| 4 | * | ||
| 5 | * Copyright (c) 2015 Samsung Electronics Co., Ltd. | ||
| 6 | * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This file contains the utility function to register CPU clock for Samsung | ||
| 13 | * Exynos platforms. A CPU clock is defined as a clock supplied to a CPU or a | ||
| 14 | * group of CPUs. The CPU clock is typically derived from a hierarchy of clock | ||
| 15 | * blocks which includes mux and divider blocks. There are a number of other | ||
| 16 | * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI | ||
| 17 | * clock for CPU domain. The rates of these auxiliary clocks are related to the | ||
| 18 | * CPU clock rate and this relation is usually specified in the hardware manual | ||
| 19 | * of the SoC or supplied after the SoC characterization. | ||
| 20 | * | ||
| 21 | * The below implementation of the CPU clock allows the rate changes of the CPU | ||
| 22 | * clock and the corresponding rate changes of the auxillary clocks of the CPU | ||
| 23 | * domain. The platform clock driver provides a clock register configuration | ||
| 24 | * for each configurable rate which is then used to program the clock hardware | ||
| 25 | * registers to acheive a fast co-oridinated rate change for all the CPU domain | ||
| 26 | * clocks. | ||
| 27 | * | ||
| 28 | * On a rate change request for the CPU clock, the rate change is propagated | ||
| 29 | * upto the PLL supplying the clock to the CPU domain clock blocks. While the | ||
| 30 | * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an | ||
| 31 | * alternate clock source. If required, the alternate clock source is divided | ||
| 32 | * down in order to keep the output clock rate within the previous OPP limits. | ||
| 33 | */ | ||
| 34 | |||
| 35 | #include <linux/errno.h> | ||
| 36 | #include "clk-cpu.h" | ||
| 37 | |||
| 38 | #define E4210_SRC_CPU 0x0 | ||
| 39 | #define E4210_STAT_CPU 0x200 | ||
| 40 | #define E4210_DIV_CPU0 0x300 | ||
| 41 | #define E4210_DIV_CPU1 0x304 | ||
| 42 | #define E4210_DIV_STAT_CPU0 0x400 | ||
| 43 | #define E4210_DIV_STAT_CPU1 0x404 | ||
| 44 | |||
| 45 | #define E4210_DIV0_RATIO0_MASK 0x7 | ||
| 46 | #define E4210_DIV1_HPM_MASK (0x7 << 4) | ||
| 47 | #define E4210_DIV1_COPY_MASK (0x7 << 0) | ||
| 48 | #define E4210_MUX_HPM_MASK (1 << 20) | ||
| 49 | #define E4210_DIV0_ATB_SHIFT 16 | ||
| 50 | #define E4210_DIV0_ATB_MASK (DIV_MASK << E4210_DIV0_ATB_SHIFT) | ||
| 51 | |||
| 52 | #define MAX_DIV 8 | ||
| 53 | #define DIV_MASK 7 | ||
| 54 | #define DIV_MASK_ALL 0xffffffff | ||
| 55 | #define MUX_MASK 7 | ||
| 56 | |||
| 57 | /* | ||
| 58 | * Helper function to wait until divider(s) have stabilized after the divider | ||
| 59 | * value has changed. | ||
| 60 | */ | ||
| 61 | static void wait_until_divider_stable(void __iomem *div_reg, unsigned long mask) | ||
| 62 | { | ||
| 63 | unsigned long timeout = jiffies + msecs_to_jiffies(10); | ||
| 64 | |||
| 65 | do { | ||
| 66 | if (!(readl(div_reg) & mask)) | ||
| 67 | return; | ||
| 68 | } while (time_before(jiffies, timeout)); | ||
| 69 | |||
| 70 | if (!(readl(div_reg) & mask)) | ||
| 71 | return; | ||
| 72 | |||
| 73 | pr_err("%s: timeout in divider stablization\n", __func__); | ||
| 74 | } | ||
| 75 | |||
| 76 | /* | ||
| 77 | * Helper function to wait until mux has stabilized after the mux selection | ||
| 78 | * value was changed. | ||
| 79 | */ | ||
| 80 | static void wait_until_mux_stable(void __iomem *mux_reg, u32 mux_pos, | ||
| 81 | unsigned long mux_value) | ||
| 82 | { | ||
| 83 | unsigned long timeout = jiffies + msecs_to_jiffies(10); | ||
| 84 | |||
| 85 | do { | ||
| 86 | if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value) | ||
| 87 | return; | ||
| 88 | } while (time_before(jiffies, timeout)); | ||
| 89 | |||
| 90 | if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value) | ||
| 91 | return; | ||
| 92 | |||
| 93 | pr_err("%s: re-parenting mux timed-out\n", __func__); | ||
| 94 | } | ||
| 95 | |||
| 96 | /* common round rate callback useable for all types of CPU clocks */ | ||
| 97 | static long exynos_cpuclk_round_rate(struct clk_hw *hw, | ||
| 98 | unsigned long drate, unsigned long *prate) | ||
| 99 | { | ||
| 100 | struct clk *parent = __clk_get_parent(hw->clk); | ||
| 101 | *prate = __clk_round_rate(parent, drate); | ||
| 102 | return *prate; | ||
| 103 | } | ||
| 104 | |||
| 105 | /* common recalc rate callback useable for all types of CPU clocks */ | ||
| 106 | static unsigned long exynos_cpuclk_recalc_rate(struct clk_hw *hw, | ||
| 107 | unsigned long parent_rate) | ||
| 108 | { | ||
| 109 | /* | ||
| 110 | * The CPU clock output (armclk) rate is the same as its parent | ||
| 111 | * rate. Although there exist certain dividers inside the CPU | ||
| 112 | * clock block that could be used to divide the parent clock, | ||
| 113 | * the driver does not make use of them currently, except during | ||
| 114 | * frequency transitions. | ||
| 115 | */ | ||
| 116 | return parent_rate; | ||
| 117 | } | ||
| 118 | |||
| 119 | static const struct clk_ops exynos_cpuclk_clk_ops = { | ||
| 120 | .recalc_rate = exynos_cpuclk_recalc_rate, | ||
| 121 | .round_rate = exynos_cpuclk_round_rate, | ||
| 122 | }; | ||
| 123 | |||
| 124 | /* | ||
| 125 | * Helper function to set the 'safe' dividers for the CPU clock. The parameters | ||
| 126 | * div and mask contain the divider value and the register bit mask of the | ||
| 127 | * dividers to be programmed. | ||
| 128 | */ | ||
| 129 | static void exynos_set_safe_div(void __iomem *base, unsigned long div, | ||
| 130 | unsigned long mask) | ||
| 131 | { | ||
| 132 | unsigned long div0; | ||
| 133 | |||
| 134 | div0 = readl(base + E4210_DIV_CPU0); | ||
| 135 | div0 = (div0 & ~mask) | (div & mask); | ||
| 136 | writel(div0, base + E4210_DIV_CPU0); | ||
| 137 | wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, mask); | ||
| 138 | } | ||
| 139 | |||
| 140 | /* handler for pre-rate change notification from parent clock */ | ||
| 141 | static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata, | ||
| 142 | struct exynos_cpuclk *cpuclk, void __iomem *base) | ||
| 143 | { | ||
| 144 | const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg; | ||
| 145 | unsigned long alt_prate = clk_get_rate(cpuclk->alt_parent); | ||
| 146 | unsigned long alt_div = 0, alt_div_mask = DIV_MASK; | ||
| 147 | unsigned long div0, div1 = 0, mux_reg; | ||
| 148 | |||
| 149 | /* find out the divider values to use for clock data */ | ||
| 150 | while ((cfg_data->prate * 1000) != ndata->new_rate) { | ||
| 151 | if (cfg_data->prate == 0) | ||
| 152 | return -EINVAL; | ||
| 153 | cfg_data++; | ||
| 154 | } | ||
| 155 | |||
| 156 | spin_lock(cpuclk->lock); | ||
| 157 | |||
| 158 | /* | ||
| 159 | * For the selected PLL clock frequency, get the pre-defined divider | ||
| 160 | * values. If the clock for sclk_hpm is not sourced from apll, then | ||
| 161 | * the values for DIV_COPY and DIV_HPM dividers need not be set. | ||
| 162 | */ | ||
| 163 | div0 = cfg_data->div0; | ||
| 164 | if (test_bit(CLK_CPU_HAS_DIV1, &cpuclk->flags)) { | ||
| 165 | div1 = cfg_data->div1; | ||
| 166 | if (readl(base + E4210_SRC_CPU) & E4210_MUX_HPM_MASK) | ||
| 167 | div1 = readl(base + E4210_DIV_CPU1) & | ||
| 168 | (E4210_DIV1_HPM_MASK | E4210_DIV1_COPY_MASK); | ||
| 169 | } | ||
| 170 | |||
| 171 | /* | ||
| 172 | * If the old parent clock speed is less than the clock speed of | ||
| 173 | * the alternate parent, then it should be ensured that at no point | ||
| 174 | * the armclk speed is more than the old_prate until the dividers are | ||
| 175 | * set. Also workaround the issue of the dividers being set to lower | ||
| 176 | * values before the parent clock speed is set to new lower speed | ||
| 177 | * (this can result in too high speed of armclk output clocks). | ||
| 178 | */ | ||
| 179 | if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) { | ||
| 180 | unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate); | ||
| 181 | |||
| 182 | alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1; | ||
| 183 | WARN_ON(alt_div >= MAX_DIV); | ||
| 184 | |||
| 185 | if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { | ||
| 186 | /* | ||
| 187 | * In Exynos4210, ATB clock parent is also mout_core. So | ||
| 188 | * ATB clock also needs to be mantained at safe speed. | ||
| 189 | */ | ||
| 190 | alt_div |= E4210_DIV0_ATB_MASK; | ||
| 191 | alt_div_mask |= E4210_DIV0_ATB_MASK; | ||
| 192 | } | ||
| 193 | exynos_set_safe_div(base, alt_div, alt_div_mask); | ||
| 194 | div0 |= alt_div; | ||
| 195 | } | ||
| 196 | |||
| 197 | /* select sclk_mpll as the alternate parent */ | ||
| 198 | mux_reg = readl(base + E4210_SRC_CPU); | ||
| 199 | writel(mux_reg | (1 << 16), base + E4210_SRC_CPU); | ||
| 200 | wait_until_mux_stable(base + E4210_STAT_CPU, 16, 2); | ||
| 201 | |||
| 202 | /* alternate parent is active now. set the dividers */ | ||
| 203 | writel(div0, base + E4210_DIV_CPU0); | ||
| 204 | wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, DIV_MASK_ALL); | ||
| 205 | |||
| 206 | if (test_bit(CLK_CPU_HAS_DIV1, &cpuclk->flags)) { | ||
| 207 | writel(div1, base + E4210_DIV_CPU1); | ||
| 208 | wait_until_divider_stable(base + E4210_DIV_STAT_CPU1, | ||
| 209 | DIV_MASK_ALL); | ||
| 210 | } | ||
| 211 | |||
| 212 | spin_unlock(cpuclk->lock); | ||
| 213 | return 0; | ||
| 214 | } | ||
| 215 | |||
| 216 | /* handler for post-rate change notification from parent clock */ | ||
| 217 | static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata, | ||
| 218 | struct exynos_cpuclk *cpuclk, void __iomem *base) | ||
| 219 | { | ||
| 220 | const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg; | ||
| 221 | unsigned long div = 0, div_mask = DIV_MASK; | ||
| 222 | unsigned long mux_reg; | ||
| 223 | |||
| 224 | /* find out the divider values to use for clock data */ | ||
| 225 | if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { | ||
| 226 | while ((cfg_data->prate * 1000) != ndata->new_rate) { | ||
| 227 | if (cfg_data->prate == 0) | ||
| 228 | return -EINVAL; | ||
| 229 | cfg_data++; | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | spin_lock(cpuclk->lock); | ||
| 234 | |||
| 235 | /* select mout_apll as the alternate parent */ | ||
| 236 | mux_reg = readl(base + E4210_SRC_CPU); | ||
| 237 | writel(mux_reg & ~(1 << 16), base + E4210_SRC_CPU); | ||
| 238 | wait_until_mux_stable(base + E4210_STAT_CPU, 16, 1); | ||
| 239 | |||
| 240 | if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { | ||
| 241 | div |= (cfg_data->div0 & E4210_DIV0_ATB_MASK); | ||
| 242 | div_mask |= E4210_DIV0_ATB_MASK; | ||
| 243 | } | ||
| 244 | |||
| 245 | exynos_set_safe_div(base, div, div_mask); | ||
| 246 | spin_unlock(cpuclk->lock); | ||
| 247 | return 0; | ||
| 248 | } | ||
| 249 | |||
| 250 | /* | ||
| 251 | * This notifier function is called for the pre-rate and post-rate change | ||
| 252 | * notifications of the parent clock of cpuclk. | ||
| 253 | */ | ||
| 254 | static int exynos_cpuclk_notifier_cb(struct notifier_block *nb, | ||
| 255 | unsigned long event, void *data) | ||
| 256 | { | ||
| 257 | struct clk_notifier_data *ndata = data; | ||
| 258 | struct exynos_cpuclk *cpuclk; | ||
| 259 | void __iomem *base; | ||
| 260 | int err = 0; | ||
| 261 | |||
| 262 | cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb); | ||
| 263 | base = cpuclk->ctrl_base; | ||
| 264 | |||
| 265 | if (event == PRE_RATE_CHANGE) | ||
| 266 | err = exynos_cpuclk_pre_rate_change(ndata, cpuclk, base); | ||
| 267 | else if (event == POST_RATE_CHANGE) | ||
| 268 | err = exynos_cpuclk_post_rate_change(ndata, cpuclk, base); | ||
| 269 | |||
| 270 | return notifier_from_errno(err); | ||
| 271 | } | ||
| 272 | |||
| 273 | /* helper function to register a CPU clock */ | ||
| 274 | int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx, | ||
| 275 | unsigned int lookup_id, const char *name, const char *parent, | ||
| 276 | const char *alt_parent, unsigned long offset, | ||
| 277 | const struct exynos_cpuclk_cfg_data *cfg, | ||
| 278 | unsigned long num_cfgs, unsigned long flags) | ||
| 279 | { | ||
| 280 | struct exynos_cpuclk *cpuclk; | ||
| 281 | struct clk_init_data init; | ||
| 282 | struct clk *clk; | ||
| 283 | int ret = 0; | ||
| 284 | |||
| 285 | cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL); | ||
| 286 | if (!cpuclk) | ||
| 287 | return -ENOMEM; | ||
| 288 | |||
| 289 | init.name = name; | ||
| 290 | init.flags = CLK_SET_RATE_PARENT; | ||
| 291 | init.parent_names = &parent; | ||
| 292 | init.num_parents = 1; | ||
| 293 | init.ops = &exynos_cpuclk_clk_ops; | ||
| 294 | |||
| 295 | cpuclk->hw.init = &init; | ||
| 296 | cpuclk->ctrl_base = ctx->reg_base + offset; | ||
| 297 | cpuclk->lock = &ctx->lock; | ||
| 298 | cpuclk->flags = flags; | ||
| 299 | cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb; | ||
| 300 | |||
| 301 | cpuclk->alt_parent = __clk_lookup(alt_parent); | ||
| 302 | if (!cpuclk->alt_parent) { | ||
| 303 | pr_err("%s: could not lookup alternate parent %s\n", | ||
| 304 | __func__, alt_parent); | ||
| 305 | ret = -EINVAL; | ||
| 306 | goto free_cpuclk; | ||
| 307 | } | ||
| 308 | |||
| 309 | clk = __clk_lookup(parent); | ||
| 310 | if (!clk) { | ||
| 311 | pr_err("%s: could not lookup parent clock %s\n", | ||
| 312 | __func__, parent); | ||
| 313 | ret = -EINVAL; | ||
| 314 | goto free_cpuclk; | ||
| 315 | } | ||
| 316 | |||
| 317 | ret = clk_notifier_register(clk, &cpuclk->clk_nb); | ||
| 318 | if (ret) { | ||
| 319 | pr_err("%s: failed to register clock notifier for %s\n", | ||
| 320 | __func__, name); | ||
| 321 | goto free_cpuclk; | ||
| 322 | } | ||
| 323 | |||
| 324 | cpuclk->cfg = kmemdup(cfg, sizeof(*cfg) * num_cfgs, GFP_KERNEL); | ||
| 325 | if (!cpuclk->cfg) { | ||
| 326 | pr_err("%s: could not allocate memory for cpuclk data\n", | ||
| 327 | __func__); | ||
| 328 | ret = -ENOMEM; | ||
| 329 | goto unregister_clk_nb; | ||
| 330 | } | ||
| 331 | |||
| 332 | clk = clk_register(NULL, &cpuclk->hw); | ||
| 333 | if (IS_ERR(clk)) { | ||
| 334 | pr_err("%s: could not register cpuclk %s\n", __func__, name); | ||
| 335 | ret = PTR_ERR(clk); | ||
| 336 | goto free_cpuclk_data; | ||
| 337 | } | ||
| 338 | |||
| 339 | samsung_clk_add_lookup(ctx, clk, lookup_id); | ||
| 340 | return 0; | ||
| 341 | |||
| 342 | free_cpuclk_data: | ||
| 343 | kfree(cpuclk->cfg); | ||
| 344 | unregister_clk_nb: | ||
| 345 | clk_notifier_unregister(__clk_lookup(parent), &cpuclk->clk_nb); | ||
| 346 | free_cpuclk: | ||
| 347 | kfree(cpuclk); | ||
| 348 | return ret; | ||
| 349 | } | ||
diff --git a/drivers/clk/samsung/clk-cpu.h b/drivers/clk/samsung/clk-cpu.h new file mode 100644 index 000000000000..37874d3c3165 --- /dev/null +++ b/drivers/clk/samsung/clk-cpu.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2014 Samsung Electronics Co., Ltd. | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * Common Clock Framework support for all PLL's in Samsung platforms | ||
| 9 | */ | ||
| 10 | |||
| 11 | #ifndef __SAMSUNG_CLK_CPU_H | ||
| 12 | #define __SAMSUNG_CLK_CPU_H | ||
| 13 | |||
| 14 | #include "clk.h" | ||
| 15 | |||
| 16 | /** | ||
| 17 | * struct exynos_cpuclk_data: config data to setup cpu clocks. | ||
| 18 | * @prate: frequency of the primary parent clock (in KHz). | ||
| 19 | * @div0: value to be programmed in the div_cpu0 register. | ||
| 20 | * @div1: value to be programmed in the div_cpu1 register. | ||
| 21 | * | ||
| 22 | * This structure holds the divider configuration data for dividers in the CPU | ||
| 23 | * clock domain. The parent frequency at which these divider values are valid is | ||
| 24 | * specified in @prate. The @prate is the frequency of the primary parent clock. | ||
| 25 | * For CPU clock domains that do not have a DIV1 register, the @div1 member | ||
| 26 | * value is not used. | ||
| 27 | */ | ||
| 28 | struct exynos_cpuclk_cfg_data { | ||
| 29 | unsigned long prate; | ||
| 30 | unsigned long div0; | ||
| 31 | unsigned long div1; | ||
| 32 | }; | ||
| 33 | |||
| 34 | /** | ||
| 35 | * struct exynos_cpuclk: information about clock supplied to a CPU core. | ||
| 36 | * @hw: handle between CCF and CPU clock. | ||
| 37 | * @alt_parent: alternate parent clock to use when switching the speed | ||
| 38 | * of the primary parent clock. | ||
| 39 | * @ctrl_base: base address of the clock controller. | ||
| 40 | * @lock: cpu clock domain register access lock. | ||
| 41 | * @cfg: cpu clock rate configuration data. | ||
| 42 | * @num_cfgs: number of array elements in @cfg array. | ||
| 43 | * @clk_nb: clock notifier registered for changes in clock speed of the | ||
| 44 | * primary parent clock. | ||
| 45 | * @flags: configuration flags for the CPU clock. | ||
| 46 | * | ||
| 47 | * This structure holds information required for programming the CPU clock for | ||
| 48 | * various clock speeds. | ||
| 49 | */ | ||
| 50 | struct exynos_cpuclk { | ||
| 51 | struct clk_hw hw; | ||
| 52 | struct clk *alt_parent; | ||
| 53 | void __iomem *ctrl_base; | ||
| 54 | spinlock_t *lock; | ||
| 55 | const struct exynos_cpuclk_cfg_data *cfg; | ||
| 56 | const unsigned long num_cfgs; | ||
| 57 | struct notifier_block clk_nb; | ||
| 58 | unsigned long flags; | ||
| 59 | |||
| 60 | /* The CPU clock registers has DIV1 configuration register */ | ||
| 61 | #define CLK_CPU_HAS_DIV1 (1 << 0) | ||
| 62 | /* When ALT parent is active, debug clocks need safe divider values */ | ||
| 63 | #define CLK_CPU_NEEDS_DEBUG_ALT_DIV (1 << 1) | ||
| 64 | }; | ||
| 65 | |||
| 66 | extern int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx, | ||
| 67 | unsigned int lookup_id, const char *name, | ||
| 68 | const char *parent, const char *alt_parent, | ||
| 69 | unsigned long offset, | ||
| 70 | const struct exynos_cpuclk_cfg_data *cfg, | ||
| 71 | unsigned long num_cfgs, unsigned long flags); | ||
| 72 | |||
| 73 | #endif /* __SAMSUNG_CLK_CPU_H */ | ||
diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 714d6ba782c8..cae2c048488d 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include <linux/syscore_ops.h> | 19 | #include <linux/syscore_ops.h> |
| 20 | 20 | ||
| 21 | #include "clk.h" | 21 | #include "clk.h" |
| 22 | #include "clk-cpu.h" | ||
| 22 | 23 | ||
| 23 | /* Exynos4 clock controller register offsets */ | 24 | /* Exynos4 clock controller register offsets */ |
| 24 | #define SRC_LEFTBUS 0x4200 | 25 | #define SRC_LEFTBUS 0x4200 |
| @@ -534,7 +535,8 @@ static struct samsung_fixed_factor_clock exynos4x12_fixed_factor_clks[] __initda | |||
| 534 | /* list of mux clocks supported in all exynos4 soc's */ | 535 | /* list of mux clocks supported in all exynos4 soc's */ |
| 535 | static struct samsung_mux_clock exynos4_mux_clks[] __initdata = { | 536 | static struct samsung_mux_clock exynos4_mux_clks[] __initdata = { |
| 536 | MUX_FA(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, | 537 | MUX_FA(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, |
| 537 | CLK_SET_RATE_PARENT, 0, "mout_apll"), | 538 | CLK_SET_RATE_PARENT | CLK_RECALC_NEW_RATES, 0, |
| 539 | "mout_apll"), | ||
| 538 | MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), | 540 | MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), |
| 539 | MUX(0, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), | 541 | MUX(0, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), |
| 540 | MUX(0, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), | 542 | MUX(0, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), |
| @@ -1378,6 +1380,22 @@ static void __init exynos4x12_core_down_clock(void) | |||
| 1378 | __raw_writel(0x0, reg_base + E4X12_PWR_CTRL2); | 1380 | __raw_writel(0x0, reg_base + E4X12_PWR_CTRL2); |
| 1379 | } | 1381 | } |
| 1380 | 1382 | ||
| 1383 | #define E4210_CPU_DIV0(apll, pclk_dbg, atb, periph, corem1, corem0) \ | ||
| 1384 | (((apll) << 24) | ((pclk_dbg) << 20) | ((atb) << 16) | \ | ||
| 1385 | ((periph) << 12) | ((corem1) << 8) | ((corem0) << 4)) | ||
| 1386 | #define E4210_CPU_DIV1(hpm, copy) \ | ||
| 1387 | (((hpm) << 4) | ((copy) << 0)) | ||
| 1388 | |||
| 1389 | static const struct exynos_cpuclk_cfg_data e4210_armclk_d[] __initconst = { | ||
| 1390 | { 1200000, E4210_CPU_DIV0(7, 1, 4, 3, 7, 3), E4210_CPU_DIV1(0, 5), }, | ||
| 1391 | { 1000000, E4210_CPU_DIV0(7, 1, 4, 3, 7, 3), E4210_CPU_DIV1(0, 4), }, | ||
| 1392 | { 800000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), }, | ||
| 1393 | { 500000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), }, | ||
| 1394 | { 400000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), }, | ||
| 1395 | { 200000, E4210_CPU_DIV0(0, 1, 1, 1, 3, 1), E4210_CPU_DIV1(0, 3), }, | ||
| 1396 | { 0 }, | ||
| 1397 | }; | ||
| 1398 | |||
| 1381 | /* register exynos4 clocks */ | 1399 | /* register exynos4 clocks */ |
| 1382 | static void __init exynos4_clk_init(struct device_node *np, | 1400 | static void __init exynos4_clk_init(struct device_node *np, |
| 1383 | enum exynos4_soc soc) | 1401 | enum exynos4_soc soc) |
| @@ -1455,6 +1473,10 @@ static void __init exynos4_clk_init(struct device_node *np, | |||
| 1455 | samsung_clk_register_fixed_factor(ctx, | 1473 | samsung_clk_register_fixed_factor(ctx, |
| 1456 | exynos4210_fixed_factor_clks, | 1474 | exynos4210_fixed_factor_clks, |
| 1457 | ARRAY_SIZE(exynos4210_fixed_factor_clks)); | 1475 | ARRAY_SIZE(exynos4210_fixed_factor_clks)); |
| 1476 | exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk", | ||
| 1477 | mout_core_p4210[0], mout_core_p4210[1], 0x14200, | ||
| 1478 | e4210_armclk_d, ARRAY_SIZE(e4210_armclk_d), | ||
| 1479 | CLK_CPU_NEEDS_DEBUG_ALT_DIV | CLK_CPU_HAS_DIV1); | ||
| 1458 | } else { | 1480 | } else { |
| 1459 | samsung_clk_register_mux(ctx, exynos4x12_mux_clks, | 1481 | samsung_clk_register_mux(ctx, exynos4x12_mux_clks, |
| 1460 | ARRAY_SIZE(exynos4x12_mux_clks)); | 1482 | ARRAY_SIZE(exynos4x12_mux_clks)); |
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 4f3dbc8cf729..a86a196e32f6 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm | |||
| @@ -36,17 +36,6 @@ config ARM_EXYNOS_CPUFREQ | |||
| 36 | 36 | ||
| 37 | If in doubt, say N. | 37 | If in doubt, say N. |
| 38 | 38 | ||
| 39 | config ARM_EXYNOS4210_CPUFREQ | ||
| 40 | bool "SAMSUNG EXYNOS4210" | ||
| 41 | depends on CPU_EXYNOS4210 | ||
| 42 | depends on ARM_EXYNOS_CPUFREQ | ||
| 43 | default y | ||
| 44 | help | ||
| 45 | This adds the CPUFreq driver for Samsung EXYNOS4210 | ||
| 46 | SoC (S5PV310 or S5PC210). | ||
| 47 | |||
| 48 | If in doubt, say N. | ||
| 49 | |||
| 50 | config ARM_EXYNOS4X12_CPUFREQ | 39 | config ARM_EXYNOS4X12_CPUFREQ |
| 51 | bool "SAMSUNG EXYNOS4x12" | 40 | bool "SAMSUNG EXYNOS4x12" |
| 52 | depends on SOC_EXYNOS4212 || SOC_EXYNOS4412 | 41 | depends on SOC_EXYNOS4212 || SOC_EXYNOS4412 |
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index cdce92ae2e8b..2169bf792db7 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile | |||
| @@ -54,7 +54,6 @@ obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o | |||
| 54 | obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o | 54 | obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o |
| 55 | obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += arm-exynos-cpufreq.o | 55 | obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += arm-exynos-cpufreq.o |
| 56 | arm-exynos-cpufreq-y := exynos-cpufreq.o | 56 | arm-exynos-cpufreq-y := exynos-cpufreq.o |
| 57 | arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o | ||
| 58 | arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o | 57 | arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o |
| 59 | arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o | 58 | arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o |
| 60 | obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o | 59 | obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o |
diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 82d2fbb20f7e..fb24aaf4adcf 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c | |||
| @@ -168,10 +168,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) | |||
| 168 | 168 | ||
| 169 | exynos_info->dev = &pdev->dev; | 169 | exynos_info->dev = &pdev->dev; |
| 170 | 170 | ||
| 171 | if (of_machine_is_compatible("samsung,exynos4210")) { | 171 | if (of_machine_is_compatible("samsung,exynos4212")) { |
| 172 | exynos_info->type = EXYNOS_SOC_4210; | ||
| 173 | ret = exynos4210_cpufreq_init(exynos_info); | ||
| 174 | } else if (of_machine_is_compatible("samsung,exynos4212")) { | ||
| 175 | exynos_info->type = EXYNOS_SOC_4212; | 172 | exynos_info->type = EXYNOS_SOC_4212; |
| 176 | ret = exynos4x12_cpufreq_init(exynos_info); | 173 | ret = exynos4x12_cpufreq_init(exynos_info); |
| 177 | } else if (of_machine_is_compatible("samsung,exynos4412")) { | 174 | } else if (of_machine_is_compatible("samsung,exynos4412")) { |
diff --git a/drivers/cpufreq/exynos-cpufreq.h b/drivers/cpufreq/exynos-cpufreq.h index 9f2062a7cc02..a3855e4d913d 100644 --- a/drivers/cpufreq/exynos-cpufreq.h +++ b/drivers/cpufreq/exynos-cpufreq.h | |||
| @@ -18,7 +18,6 @@ enum cpufreq_level_index { | |||
| 18 | }; | 18 | }; |
| 19 | 19 | ||
| 20 | enum exynos_soc_type { | 20 | enum exynos_soc_type { |
| 21 | EXYNOS_SOC_4210, | ||
| 22 | EXYNOS_SOC_4212, | 21 | EXYNOS_SOC_4212, |
| 23 | EXYNOS_SOC_4412, | 22 | EXYNOS_SOC_4412, |
| 24 | EXYNOS_SOC_5250, | 23 | EXYNOS_SOC_5250, |
| @@ -53,14 +52,6 @@ struct exynos_dvfs_info { | |||
| 53 | void __iomem *cmu_regs; | 52 | void __iomem *cmu_regs; |
| 54 | }; | 53 | }; |
| 55 | 54 | ||
| 56 | #ifdef CONFIG_ARM_EXYNOS4210_CPUFREQ | ||
| 57 | extern int exynos4210_cpufreq_init(struct exynos_dvfs_info *); | ||
| 58 | #else | ||
| 59 | static inline int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) | ||
| 60 | { | ||
| 61 | return -EOPNOTSUPP; | ||
| 62 | } | ||
| 63 | #endif | ||
| 64 | #ifdef CONFIG_ARM_EXYNOS4X12_CPUFREQ | 55 | #ifdef CONFIG_ARM_EXYNOS4X12_CPUFREQ |
| 65 | extern int exynos4x12_cpufreq_init(struct exynos_dvfs_info *); | 56 | extern int exynos4x12_cpufreq_init(struct exynos_dvfs_info *); |
| 66 | #else | 57 | #else |
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c deleted file mode 100644 index 843ec824fd91..000000000000 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ /dev/null | |||
| @@ -1,184 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. | ||
| 3 | * http://www.samsung.com | ||
| 4 | * | ||
| 5 | * EXYNOS4210 - CPU frequency scaling support | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/err.h> | ||
| 15 | #include <linux/clk.h> | ||
| 16 | #include <linux/io.h> | ||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/cpufreq.h> | ||
| 19 | #include <linux/of.h> | ||
| 20 | #include <linux/of_address.h> | ||
| 21 | |||
| 22 | #include "exynos-cpufreq.h" | ||
| 23 | |||
| 24 | static struct clk *cpu_clk; | ||
| 25 | static struct clk *moutcore; | ||
| 26 | static struct clk *mout_mpll; | ||
| 27 | static struct clk *mout_apll; | ||
| 28 | static struct exynos_dvfs_info *cpufreq; | ||
| 29 | |||
| 30 | static unsigned int exynos4210_volt_table[] = { | ||
| 31 | 1250000, 1150000, 1050000, 975000, 950000, | ||
| 32 | }; | ||
| 33 | |||
| 34 | static struct cpufreq_frequency_table exynos4210_freq_table[] = { | ||
| 35 | {0, L0, 1200 * 1000}, | ||
| 36 | {0, L1, 1000 * 1000}, | ||
| 37 | {0, L2, 800 * 1000}, | ||
| 38 | {0, L3, 500 * 1000}, | ||
| 39 | {0, L4, 200 * 1000}, | ||
| 40 | {0, 0, CPUFREQ_TABLE_END}, | ||
| 41 | }; | ||
| 42 | |||
| 43 | static struct apll_freq apll_freq_4210[] = { | ||
| 44 | /* | ||
| 45 | * values: | ||
| 46 | * freq | ||
| 47 | * clock divider for CORE, COREM0, COREM1, PERIPH, ATB, PCLK_DBG, APLL, RESERVED | ||
| 48 | * clock divider for COPY, HPM, RESERVED | ||
| 49 | * PLL M, P, S | ||
| 50 | */ | ||
| 51 | APLL_FREQ(1200, 0, 3, 7, 3, 4, 1, 7, 0, 5, 0, 0, 150, 3, 1), | ||
| 52 | APLL_FREQ(1000, 0, 3, 7, 3, 4, 1, 7, 0, 4, 0, 0, 250, 6, 1), | ||
| 53 | APLL_FREQ(800, 0, 3, 7, 3, 3, 1, 7, 0, 3, 0, 0, 200, 6, 1), | ||
| 54 | APLL_FREQ(500, 0, 3, 7, 3, 3, 1, 7, 0, 3, 0, 0, 250, 6, 2), | ||
| 55 | APLL_FREQ(200, 0, 1, 3, 1, 3, 1, 0, 0, 3, 0, 0, 200, 6, 3), | ||
| 56 | }; | ||
| 57 | |||
| 58 | static void exynos4210_set_clkdiv(unsigned int div_index) | ||
| 59 | { | ||
| 60 | unsigned int tmp; | ||
| 61 | |||
| 62 | /* Change Divider - CPU0 */ | ||
| 63 | |||
| 64 | tmp = apll_freq_4210[div_index].clk_div_cpu0; | ||
| 65 | |||
| 66 | __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU); | ||
| 67 | |||
| 68 | do { | ||
| 69 | tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU); | ||
| 70 | } while (tmp & 0x1111111); | ||
| 71 | |||
| 72 | /* Change Divider - CPU1 */ | ||
| 73 | |||
| 74 | tmp = apll_freq_4210[div_index].clk_div_cpu1; | ||
| 75 | |||
| 76 | __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU1); | ||
| 77 | |||
| 78 | do { | ||
| 79 | tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU1); | ||
| 80 | } while (tmp & 0x11); | ||
| 81 | } | ||
| 82 | |||
| 83 | static void exynos4210_set_apll(unsigned int index) | ||
| 84 | { | ||
| 85 | unsigned int tmp, freq = apll_freq_4210[index].freq; | ||
| 86 | |||
| 87 | /* MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */ | ||
| 88 | clk_set_parent(moutcore, mout_mpll); | ||
| 89 | |||
| 90 | do { | ||
| 91 | tmp = (__raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU) | ||
| 92 | >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT); | ||
| 93 | tmp &= 0x7; | ||
| 94 | } while (tmp != 0x2); | ||
| 95 | |||
| 96 | clk_set_rate(mout_apll, freq * 1000); | ||
| 97 | |||
| 98 | /* MUX_CORE_SEL = APLL */ | ||
| 99 | clk_set_parent(moutcore, mout_apll); | ||
| 100 | |||
| 101 | do { | ||
| 102 | tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU); | ||
| 103 | tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK; | ||
| 104 | } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT)); | ||
| 105 | } | ||
| 106 | |||
| 107 | static void exynos4210_set_frequency(unsigned int old_index, | ||
| 108 | unsigned int new_index) | ||
| 109 | { | ||
| 110 | if (old_index > new_index) { | ||
| 111 | exynos4210_set_clkdiv(new_index); | ||
| 112 | exynos4210_set_apll(new_index); | ||
| 113 | } else if (old_index < new_index) { | ||
| 114 | exynos4210_set_apll(new_index); | ||
| 115 | exynos4210_set_clkdiv(new_index); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) | ||
| 120 | { | ||
| 121 | struct device_node *np; | ||
| 122 | unsigned long rate; | ||
| 123 | |||
| 124 | /* | ||
| 125 | * HACK: This is a temporary workaround to get access to clock | ||
| 126 | * controller registers directly and remove static mappings and | ||
| 127 | * dependencies on platform headers. It is necessary to enable | ||
| 128 | * Exynos multi-platform support and will be removed together with | ||
| 129 | * this whole driver as soon as Exynos gets migrated to use | ||
| 130 | * cpufreq-dt driver. | ||
| 131 | */ | ||
| 132 | np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-clock"); | ||
| 133 | if (!np) { | ||
| 134 | pr_err("%s: failed to find clock controller DT node\n", | ||
| 135 | __func__); | ||
| 136 | return -ENODEV; | ||
| 137 | } | ||
| 138 | |||
| 139 | info->cmu_regs = of_iomap(np, 0); | ||
| 140 | if (!info->cmu_regs) { | ||
| 141 | pr_err("%s: failed to map CMU registers\n", __func__); | ||
| 142 | return -EFAULT; | ||
| 143 | } | ||
| 144 | |||
| 145 | cpu_clk = clk_get(NULL, "armclk"); | ||
| 146 | if (IS_ERR(cpu_clk)) | ||
| 147 | return PTR_ERR(cpu_clk); | ||
| 148 | |||
| 149 | moutcore = clk_get(NULL, "moutcore"); | ||
| 150 | if (IS_ERR(moutcore)) | ||
| 151 | goto err_moutcore; | ||
| 152 | |||
| 153 | mout_mpll = clk_get(NULL, "mout_mpll"); | ||
| 154 | if (IS_ERR(mout_mpll)) | ||
| 155 | goto err_mout_mpll; | ||
| 156 | |||
| 157 | rate = clk_get_rate(mout_mpll) / 1000; | ||
| 158 | |||
| 159 | mout_apll = clk_get(NULL, "mout_apll"); | ||
| 160 | if (IS_ERR(mout_apll)) | ||
| 161 | goto err_mout_apll; | ||
| 162 | |||
| 163 | info->mpll_freq_khz = rate; | ||
| 164 | /* 800Mhz */ | ||
| 165 | info->pll_safe_idx = L2; | ||
| 166 | info->cpu_clk = cpu_clk; | ||
| 167 | info->volt_table = exynos4210_volt_table; | ||
| 168 | info->freq_table = exynos4210_freq_table; | ||
| 169 | info->set_freq = exynos4210_set_frequency; | ||
| 170 | |||
| 171 | cpufreq = info; | ||
| 172 | |||
| 173 | return 0; | ||
| 174 | |||
| 175 | err_mout_apll: | ||
| 176 | clk_put(mout_mpll); | ||
| 177 | err_mout_mpll: | ||
| 178 | clk_put(moutcore); | ||
| 179 | err_moutcore: | ||
| 180 | clk_put(cpu_clk); | ||
| 181 | |||
| 182 | pr_debug("%s: failed initialization\n", __func__); | ||
| 183 | return -EINVAL; | ||
| 184 | } | ||
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 4a943d13625b..6a24df64b0a0 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | #define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ | 31 | #define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ |
| 32 | #define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ | 32 | #define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ |
| 33 | #define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */ | 33 | #define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */ |
| 34 | #define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */ | ||
| 34 | 35 | ||
| 35 | struct clk_hw; | 36 | struct clk_hw; |
| 36 | struct clk_core; | 37 | struct clk_core; |
