Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: st: Support for DIVMUX and PreDiv Clocks

The patch supports the DIVMUX and PreDiv clocks used by ClockGenA(s)

DIVMUX clock : Divider-Multiplexer-Gate inside ClockGenA(s)
It includes support for each channel : 3-parent Multiplexer,
Divider for each Parent, Gate to switch OFF the output channel. The
clock is implemented using generic clocks implemented in the kernel
clk_divider and clk_mux.

PreDiv clock : Fixed Divider Clock used inside ClockGenA(s) to divide
the oscillator clock by factor-of-16. The clock is implemented using
generic clocks implemented in the kernel clk_divider.

Signed-off-by: Pankaj Dev <pankaj.dev@st.com>
Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
Signed-off-by: Mike Turquette <mturquette@linaro.org>

authored by

Gabriel FERNANDEZ and committed by
Mike Turquette
94885faf c646cbf1

+531
+1
drivers/clk/Makefile
··· 45 45 obj-$(CONFIG_ARCH_SIRF) += sirf/ 46 46 obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ 47 47 obj-$(CONFIG_PLAT_SPEAR) += spear/ 48 + obj-$(CONFIG_ARCH_STI) += st/ 48 49 obj-$(CONFIG_ARCH_SUNXI) += sunxi/ 49 50 obj-$(CONFIG_ARCH_TEGRA) += tegra/ 50 51 obj-$(CONFIG_ARCH_OMAP2PLUS) += ti/
+1
drivers/clk/st/Makefile
··· 1 + obj-y += clkgen-mux.o
+529
drivers/clk/st/clkgen-mux.c
··· 1 + /* 2 + * clkgen-mux.c: ST GEN-MUX Clock driver 3 + * 4 + * Copyright (C) 2014 STMicroelectronics (R&D) Limited 5 + * 6 + * Authors: Stephen Gallimore <stephen.gallimore@st.com> 7 + * Pankaj Dev <pankaj.dev@st.com> 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License as published by 11 + * the Free Software Foundation; either version 2 of the License, or 12 + * (at your option) any later version. 13 + * 14 + */ 15 + 16 + #include <linux/slab.h> 17 + #include <linux/of_address.h> 18 + #include <linux/clk-provider.h> 19 + 20 + static DEFINE_SPINLOCK(clkgena_divmux_lock); 21 + 22 + static const char ** __init clkgen_mux_get_parents(struct device_node *np, 23 + int *num_parents) 24 + { 25 + const char **parents; 26 + int nparents, i; 27 + 28 + nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); 29 + if (WARN_ON(nparents <= 0)) 30 + return ERR_PTR(-EINVAL); 31 + 32 + parents = kzalloc(nparents * sizeof(const char *), GFP_KERNEL); 33 + if (!parents) 34 + return ERR_PTR(-ENOMEM); 35 + 36 + for (i = 0; i < nparents; i++) 37 + parents[i] = of_clk_get_parent_name(np, i); 38 + 39 + *num_parents = nparents; 40 + return parents; 41 + } 42 + 43 + /** 44 + * DOC: Clock mux with a programmable divider on each of its three inputs. 45 + * The mux has an input setting which effectively gates its output. 46 + * 47 + * Traits of this clock: 48 + * prepare - clk_(un)prepare only ensures parent is (un)prepared 49 + * enable - clk_enable and clk_disable are functional & control gating 50 + * rate - set rate is supported 51 + * parent - set/get parent 52 + */ 53 + 54 + #define NUM_INPUTS 3 55 + 56 + struct clkgena_divmux { 57 + struct clk_hw hw; 58 + /* Subclassed mux and divider structures */ 59 + struct clk_mux mux; 60 + struct clk_divider div[NUM_INPUTS]; 61 + /* Enable/running feedback register bits for each input */ 62 + void __iomem *feedback_reg[NUM_INPUTS]; 63 + int feedback_bit_idx; 64 + 65 + u8 muxsel; 66 + }; 67 + 68 + #define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw) 69 + 70 + struct clkgena_divmux_data { 71 + int num_outputs; 72 + int mux_offset; 73 + int mux_offset2; 74 + int mux_start_bit; 75 + int div_offsets[NUM_INPUTS]; 76 + int fb_offsets[NUM_INPUTS]; 77 + int fb_start_bit_idx; 78 + }; 79 + 80 + #define CKGAX_CLKOPSRC_SWITCH_OFF 0x3 81 + 82 + static int clkgena_divmux_is_running(struct clkgena_divmux *mux) 83 + { 84 + u32 regval = readl(mux->feedback_reg[mux->muxsel]); 85 + u32 running = regval & BIT(mux->feedback_bit_idx); 86 + return !!running; 87 + } 88 + 89 + static int clkgena_divmux_enable(struct clk_hw *hw) 90 + { 91 + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 92 + struct clk_hw *mux_hw = &genamux->mux.hw; 93 + unsigned long timeout; 94 + int ret = 0; 95 + 96 + mux_hw->clk = hw->clk; 97 + 98 + ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel); 99 + if (ret) 100 + return ret; 101 + 102 + timeout = jiffies + msecs_to_jiffies(10); 103 + 104 + while (!clkgena_divmux_is_running(genamux)) { 105 + if (time_after(jiffies, timeout)) 106 + return -ETIMEDOUT; 107 + cpu_relax(); 108 + } 109 + 110 + return 0; 111 + } 112 + 113 + static void clkgena_divmux_disable(struct clk_hw *hw) 114 + { 115 + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 116 + struct clk_hw *mux_hw = &genamux->mux.hw; 117 + 118 + mux_hw->clk = hw->clk; 119 + 120 + clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF); 121 + } 122 + 123 + static int clkgena_divmux_is_enabled(struct clk_hw *hw) 124 + { 125 + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 126 + struct clk_hw *mux_hw = &genamux->mux.hw; 127 + 128 + mux_hw->clk = hw->clk; 129 + 130 + return (s8)clk_mux_ops.get_parent(mux_hw) > 0; 131 + } 132 + 133 + u8 clkgena_divmux_get_parent(struct clk_hw *hw) 134 + { 135 + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 136 + struct clk_hw *mux_hw = &genamux->mux.hw; 137 + 138 + mux_hw->clk = hw->clk; 139 + 140 + genamux->muxsel = clk_mux_ops.get_parent(mux_hw); 141 + if ((s8)genamux->muxsel < 0) { 142 + pr_debug("%s: %s: Invalid parent, setting to default.\n", 143 + __func__, __clk_get_name(hw->clk)); 144 + genamux->muxsel = 0; 145 + } 146 + 147 + return genamux->muxsel; 148 + } 149 + 150 + static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index) 151 + { 152 + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 153 + 154 + if (index >= CKGAX_CLKOPSRC_SWITCH_OFF) 155 + return -EINVAL; 156 + 157 + genamux->muxsel = index; 158 + 159 + /* 160 + * If the mux is already enabled, call enable directly to set the 161 + * new mux position and wait for it to start running again. Otherwise 162 + * do nothing. 163 + */ 164 + if (clkgena_divmux_is_enabled(hw)) 165 + clkgena_divmux_enable(hw); 166 + 167 + return 0; 168 + } 169 + 170 + unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, 171 + unsigned long parent_rate) 172 + { 173 + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 174 + struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; 175 + 176 + div_hw->clk = hw->clk; 177 + 178 + return clk_divider_ops.recalc_rate(div_hw, parent_rate); 179 + } 180 + 181 + static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate, 182 + unsigned long parent_rate) 183 + { 184 + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 185 + struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; 186 + 187 + div_hw->clk = hw->clk; 188 + 189 + return clk_divider_ops.set_rate(div_hw, rate, parent_rate); 190 + } 191 + 192 + static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate, 193 + unsigned long *prate) 194 + { 195 + struct clkgena_divmux *genamux = to_clkgena_divmux(hw); 196 + struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw; 197 + 198 + div_hw->clk = hw->clk; 199 + 200 + return clk_divider_ops.round_rate(div_hw, rate, prate); 201 + } 202 + 203 + static const struct clk_ops clkgena_divmux_ops = { 204 + .enable = clkgena_divmux_enable, 205 + .disable = clkgena_divmux_disable, 206 + .is_enabled = clkgena_divmux_is_enabled, 207 + .get_parent = clkgena_divmux_get_parent, 208 + .set_parent = clkgena_divmux_set_parent, 209 + .round_rate = clkgena_divmux_round_rate, 210 + .recalc_rate = clkgena_divmux_recalc_rate, 211 + .set_rate = clkgena_divmux_set_rate, 212 + }; 213 + 214 + /** 215 + * clk_register_genamux - register a genamux clock with the clock framework 216 + */ 217 + struct clk *clk_register_genamux(const char *name, 218 + const char **parent_names, u8 num_parents, 219 + void __iomem *reg, 220 + const struct clkgena_divmux_data *muxdata, 221 + u32 idx) 222 + { 223 + /* 224 + * Fixed constants across all ClockgenA variants 225 + */ 226 + const int mux_width = 2; 227 + const int divider_width = 5; 228 + struct clkgena_divmux *genamux; 229 + struct clk *clk; 230 + struct clk_init_data init; 231 + int i; 232 + 233 + genamux = kzalloc(sizeof(*genamux), GFP_KERNEL); 234 + if (!genamux) 235 + return ERR_PTR(-ENOMEM); 236 + 237 + init.name = name; 238 + init.ops = &clkgena_divmux_ops; 239 + init.flags = CLK_IS_BASIC; 240 + init.parent_names = parent_names; 241 + init.num_parents = num_parents; 242 + 243 + genamux->mux.lock = &clkgena_divmux_lock; 244 + genamux->mux.mask = BIT(mux_width) - 1; 245 + genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width); 246 + if (genamux->mux.shift > 31) { 247 + /* 248 + * We have spilled into the second mux register so 249 + * adjust the register address and the bit shift accordingly 250 + */ 251 + genamux->mux.reg = reg + muxdata->mux_offset2; 252 + genamux->mux.shift -= 32; 253 + } else { 254 + genamux->mux.reg = reg + muxdata->mux_offset; 255 + } 256 + 257 + for (i = 0; i < NUM_INPUTS; i++) { 258 + /* 259 + * Divider config for each input 260 + */ 261 + void __iomem *divbase = reg + muxdata->div_offsets[i]; 262 + genamux->div[i].width = divider_width; 263 + genamux->div[i].reg = divbase + (idx * sizeof(u32)); 264 + 265 + /* 266 + * Mux enabled/running feedback register for each input. 267 + */ 268 + genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i]; 269 + } 270 + 271 + genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx; 272 + genamux->hw.init = &init; 273 + 274 + clk = clk_register(NULL, &genamux->hw); 275 + if (IS_ERR(clk)) { 276 + kfree(genamux); 277 + goto err; 278 + } 279 + 280 + pr_debug("%s: parent %s rate %lu\n", 281 + __clk_get_name(clk), 282 + __clk_get_name(clk_get_parent(clk)), 283 + clk_get_rate(clk)); 284 + err: 285 + return clk; 286 + } 287 + 288 + static struct clkgena_divmux_data st_divmux_c65hs = { 289 + .num_outputs = 4, 290 + .mux_offset = 0x14, 291 + .mux_start_bit = 0, 292 + .div_offsets = { 0x800, 0x900, 0xb00 }, 293 + .fb_offsets = { 0x18, 0x1c, 0x20 }, 294 + .fb_start_bit_idx = 0, 295 + }; 296 + 297 + static struct clkgena_divmux_data st_divmux_c65ls = { 298 + .num_outputs = 14, 299 + .mux_offset = 0x14, 300 + .mux_offset2 = 0x24, 301 + .mux_start_bit = 8, 302 + .div_offsets = { 0x810, 0xa10, 0xb10 }, 303 + .fb_offsets = { 0x18, 0x1c, 0x20 }, 304 + .fb_start_bit_idx = 4, 305 + }; 306 + 307 + static struct clkgena_divmux_data st_divmux_c32odf0 = { 308 + .num_outputs = 8, 309 + .mux_offset = 0x1c, 310 + .mux_start_bit = 0, 311 + .div_offsets = { 0x800, 0x900, 0xa60 }, 312 + .fb_offsets = { 0x2c, 0x24, 0x28 }, 313 + .fb_start_bit_idx = 0, 314 + }; 315 + 316 + static struct clkgena_divmux_data st_divmux_c32odf1 = { 317 + .num_outputs = 8, 318 + .mux_offset = 0x1c, 319 + .mux_start_bit = 16, 320 + .div_offsets = { 0x820, 0x980, 0xa80 }, 321 + .fb_offsets = { 0x2c, 0x24, 0x28 }, 322 + .fb_start_bit_idx = 8, 323 + }; 324 + 325 + static struct clkgena_divmux_data st_divmux_c32odf2 = { 326 + .num_outputs = 8, 327 + .mux_offset = 0x20, 328 + .mux_start_bit = 0, 329 + .div_offsets = { 0x840, 0xa20, 0xb10 }, 330 + .fb_offsets = { 0x2c, 0x24, 0x28 }, 331 + .fb_start_bit_idx = 16, 332 + }; 333 + 334 + static struct clkgena_divmux_data st_divmux_c32odf3 = { 335 + .num_outputs = 8, 336 + .mux_offset = 0x20, 337 + .mux_start_bit = 16, 338 + .div_offsets = { 0x860, 0xa40, 0xb30 }, 339 + .fb_offsets = { 0x2c, 0x24, 0x28 }, 340 + .fb_start_bit_idx = 24, 341 + }; 342 + 343 + static struct of_device_id clkgena_divmux_of_match[] = { 344 + { 345 + .compatible = "st,clkgena-divmux-c65-hs", 346 + .data = &st_divmux_c65hs, 347 + }, 348 + { 349 + .compatible = "st,clkgena-divmux-c65-ls", 350 + .data = &st_divmux_c65ls, 351 + }, 352 + { 353 + .compatible = "st,clkgena-divmux-c32-odf0", 354 + .data = &st_divmux_c32odf0, 355 + }, 356 + { 357 + .compatible = "st,clkgena-divmux-c32-odf1", 358 + .data = &st_divmux_c32odf1, 359 + }, 360 + { 361 + .compatible = "st,clkgena-divmux-c32-odf2", 362 + .data = &st_divmux_c32odf2, 363 + }, 364 + { 365 + .compatible = "st,clkgena-divmux-c32-odf3", 366 + .data = &st_divmux_c32odf3, 367 + }, 368 + {} 369 + }; 370 + 371 + static void __iomem * __init clkgen_get_register_base( 372 + struct device_node *np) 373 + { 374 + struct device_node *pnode; 375 + void __iomem *reg = NULL; 376 + 377 + pnode = of_get_parent(np); 378 + if (!pnode) 379 + return NULL; 380 + 381 + reg = of_iomap(pnode, 0); 382 + 383 + of_node_put(pnode); 384 + return reg; 385 + } 386 + 387 + void __init st_of_clkgena_divmux_setup(struct device_node *np) 388 + { 389 + const struct of_device_id *match; 390 + const struct clkgena_divmux_data *data; 391 + struct clk_onecell_data *clk_data; 392 + void __iomem *reg; 393 + const char **parents; 394 + int num_parents = 0, i; 395 + 396 + match = of_match_node(clkgena_divmux_of_match, np); 397 + if (WARN_ON(!match)) 398 + return; 399 + 400 + data = (struct clkgena_divmux_data *)match->data; 401 + 402 + reg = clkgen_get_register_base(np); 403 + if (!reg) 404 + return; 405 + 406 + parents = clkgen_mux_get_parents(np, &num_parents); 407 + if (IS_ERR(parents)) 408 + return; 409 + 410 + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); 411 + if (!clk_data) 412 + goto err; 413 + 414 + clk_data->clk_num = data->num_outputs; 415 + clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *), 416 + GFP_KERNEL); 417 + 418 + if (!clk_data->clks) 419 + goto err; 420 + 421 + for (i = 0; i < clk_data->clk_num; i++) { 422 + struct clk *clk; 423 + const char *clk_name; 424 + 425 + if (of_property_read_string_index(np, "clock-output-names", 426 + i, &clk_name)) 427 + break; 428 + 429 + /* 430 + * If we read an empty clock name then the output is unused 431 + */ 432 + if (*clk_name == '\0') 433 + continue; 434 + 435 + clk = clk_register_genamux(clk_name, parents, num_parents, 436 + reg, data, i); 437 + 438 + if (IS_ERR(clk)) 439 + goto err; 440 + 441 + clk_data->clks[i] = clk; 442 + } 443 + 444 + kfree(parents); 445 + 446 + of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); 447 + return; 448 + err: 449 + if (clk_data) 450 + kfree(clk_data->clks); 451 + 452 + kfree(clk_data); 453 + kfree(parents); 454 + } 455 + CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup); 456 + 457 + struct clkgena_prediv_data { 458 + u32 offset; 459 + u8 shift; 460 + struct clk_div_table *table; 461 + }; 462 + 463 + static struct clk_div_table prediv_table16[] = { 464 + { .val = 0, .div = 1 }, 465 + { .val = 1, .div = 16 }, 466 + { .div = 0 }, 467 + }; 468 + 469 + static struct clkgena_prediv_data prediv_c65_data = { 470 + .offset = 0x4c, 471 + .shift = 31, 472 + .table = prediv_table16, 473 + }; 474 + 475 + static struct clkgena_prediv_data prediv_c32_data = { 476 + .offset = 0x50, 477 + .shift = 1, 478 + .table = prediv_table16, 479 + }; 480 + 481 + static struct of_device_id clkgena_prediv_of_match[] = { 482 + { .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data }, 483 + { .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data }, 484 + {} 485 + }; 486 + 487 + void __init st_of_clkgena_prediv_setup(struct device_node *np) 488 + { 489 + const struct of_device_id *match; 490 + void __iomem *reg; 491 + const char *parent_name, *clk_name; 492 + struct clk *clk; 493 + struct clkgena_prediv_data *data; 494 + 495 + match = of_match_node(clkgena_prediv_of_match, np); 496 + if (!match) { 497 + pr_err("%s: No matching data\n", __func__); 498 + return; 499 + } 500 + 501 + data = (struct clkgena_prediv_data *)match->data; 502 + 503 + reg = clkgen_get_register_base(np); 504 + if (!reg) 505 + return; 506 + 507 + parent_name = of_clk_get_parent_name(np, 0); 508 + if (!parent_name) 509 + return; 510 + 511 + if (of_property_read_string_index(np, "clock-output-names", 512 + 0, &clk_name)) 513 + return; 514 + 515 + clk = clk_register_divider_table(NULL, clk_name, parent_name, 0, 516 + reg + data->offset, data->shift, 1, 517 + 0, data->table, NULL); 518 + if (IS_ERR(clk)) 519 + return; 520 + 521 + of_clk_add_provider(np, of_clk_src_simple_get, clk); 522 + pr_debug("%s: parent %s rate %u\n", 523 + __clk_get_name(clk), 524 + __clk_get_name(clk_get_parent(clk)), 525 + (unsigned int)clk_get_rate(clk)); 526 + 527 + return; 528 + } 529 + CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup);