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

clk: at91: add slow clks driver

AT91 slow clk is a clk multiplexer.

In some SoCs (sam9x5, sama5, sam9g45 families) this multiplexer can
choose among 2 sources: an internal RC oscillator circuit and an oscillator
using an external crystal.

In other Socs (sam9260 family) the multiplexer source is hardcoded with
the OSCSEL signal.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Acked-by: Mike Turquette <mturquette@linaro.org>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>

authored by

Boris BREZILLON and committed by
Nicolas Ferre
80eded6c f9e1716f

+557 -2
+2 -2
drivers/clk/at91/Makefile
··· 2 2 # Makefile for at91 specific clk 3 3 # 4 4 5 - obj-y += pmc.o 6 - obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o 5 + obj-y += pmc.o sckc.o 6 + obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o 7 7 obj-y += clk-system.o clk-peripheral.o clk-programmable.o 8 8 9 9 obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o
+467
drivers/clk/at91/clk-slow.c
··· 1 + /* 2 + * drivers/clk/at91/clk-slow.c 3 + * 4 + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + */ 12 + 13 + #include <linux/clk-provider.h> 14 + #include <linux/clkdev.h> 15 + #include <linux/clk/at91_pmc.h> 16 + #include <linux/delay.h> 17 + #include <linux/of.h> 18 + #include <linux/of_address.h> 19 + #include <linux/of_irq.h> 20 + #include <linux/io.h> 21 + #include <linux/interrupt.h> 22 + #include <linux/irq.h> 23 + #include <linux/sched.h> 24 + #include <linux/wait.h> 25 + 26 + #include "pmc.h" 27 + #include "sckc.h" 28 + 29 + #define SLOW_CLOCK_FREQ 32768 30 + #define SLOWCK_SW_CYCLES 5 31 + #define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \ 32 + SLOW_CLOCK_FREQ) 33 + 34 + #define AT91_SCKC_CR 0x00 35 + #define AT91_SCKC_RCEN (1 << 0) 36 + #define AT91_SCKC_OSC32EN (1 << 1) 37 + #define AT91_SCKC_OSC32BYP (1 << 2) 38 + #define AT91_SCKC_OSCSEL (1 << 3) 39 + 40 + struct clk_slow_osc { 41 + struct clk_hw hw; 42 + void __iomem *sckcr; 43 + unsigned long startup_usec; 44 + }; 45 + 46 + #define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw) 47 + 48 + struct clk_slow_rc_osc { 49 + struct clk_hw hw; 50 + void __iomem *sckcr; 51 + unsigned long frequency; 52 + unsigned long accuracy; 53 + unsigned long startup_usec; 54 + }; 55 + 56 + #define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw) 57 + 58 + struct clk_sam9260_slow { 59 + struct clk_hw hw; 60 + struct at91_pmc *pmc; 61 + }; 62 + 63 + #define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw) 64 + 65 + struct clk_sam9x5_slow { 66 + struct clk_hw hw; 67 + void __iomem *sckcr; 68 + u8 parent; 69 + }; 70 + 71 + #define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw) 72 + 73 + 74 + static int clk_slow_osc_prepare(struct clk_hw *hw) 75 + { 76 + struct clk_slow_osc *osc = to_clk_slow_osc(hw); 77 + void __iomem *sckcr = osc->sckcr; 78 + u32 tmp = readl(sckcr); 79 + 80 + if (tmp & AT91_SCKC_OSC32BYP) 81 + return 0; 82 + 83 + writel(tmp | AT91_SCKC_OSC32EN, sckcr); 84 + 85 + usleep_range(osc->startup_usec, osc->startup_usec + 1); 86 + 87 + return 0; 88 + } 89 + 90 + static void clk_slow_osc_unprepare(struct clk_hw *hw) 91 + { 92 + struct clk_slow_osc *osc = to_clk_slow_osc(hw); 93 + void __iomem *sckcr = osc->sckcr; 94 + u32 tmp = readl(sckcr); 95 + 96 + if (tmp & AT91_SCKC_OSC32BYP) 97 + return; 98 + 99 + writel(tmp & ~AT91_SCKC_OSC32EN, sckcr); 100 + } 101 + 102 + static int clk_slow_osc_is_prepared(struct clk_hw *hw) 103 + { 104 + struct clk_slow_osc *osc = to_clk_slow_osc(hw); 105 + void __iomem *sckcr = osc->sckcr; 106 + u32 tmp = readl(sckcr); 107 + 108 + if (tmp & AT91_SCKC_OSC32BYP) 109 + return 1; 110 + 111 + return !!(tmp & AT91_SCKC_OSC32EN); 112 + } 113 + 114 + static const struct clk_ops slow_osc_ops = { 115 + .prepare = clk_slow_osc_prepare, 116 + .unprepare = clk_slow_osc_unprepare, 117 + .is_prepared = clk_slow_osc_is_prepared, 118 + }; 119 + 120 + static struct clk * __init 121 + at91_clk_register_slow_osc(void __iomem *sckcr, 122 + const char *name, 123 + const char *parent_name, 124 + unsigned long startup, 125 + bool bypass) 126 + { 127 + struct clk_slow_osc *osc; 128 + struct clk *clk = NULL; 129 + struct clk_init_data init; 130 + 131 + if (!sckcr || !name || !parent_name) 132 + return ERR_PTR(-EINVAL); 133 + 134 + osc = kzalloc(sizeof(*osc), GFP_KERNEL); 135 + if (!osc) 136 + return ERR_PTR(-ENOMEM); 137 + 138 + init.name = name; 139 + init.ops = &slow_osc_ops; 140 + init.parent_names = &parent_name; 141 + init.num_parents = 1; 142 + init.flags = CLK_IGNORE_UNUSED; 143 + 144 + osc->hw.init = &init; 145 + osc->sckcr = sckcr; 146 + osc->startup_usec = startup; 147 + 148 + if (bypass) 149 + writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP, 150 + sckcr); 151 + 152 + clk = clk_register(NULL, &osc->hw); 153 + if (IS_ERR(clk)) 154 + kfree(osc); 155 + 156 + return clk; 157 + } 158 + 159 + void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, 160 + void __iomem *sckcr) 161 + { 162 + struct clk *clk; 163 + const char *parent_name; 164 + const char *name = np->name; 165 + u32 startup; 166 + bool bypass; 167 + 168 + parent_name = of_clk_get_parent_name(np, 0); 169 + of_property_read_string(np, "clock-output-names", &name); 170 + of_property_read_u32(np, "atmel,startup-time-usec", &startup); 171 + bypass = of_property_read_bool(np, "atmel,osc-bypass"); 172 + 173 + clk = at91_clk_register_slow_osc(sckcr, name, parent_name, startup, 174 + bypass); 175 + if (IS_ERR(clk)) 176 + return; 177 + 178 + of_clk_add_provider(np, of_clk_src_simple_get, clk); 179 + } 180 + 181 + static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw, 182 + unsigned long parent_rate) 183 + { 184 + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 185 + 186 + return osc->frequency; 187 + } 188 + 189 + static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw, 190 + unsigned long parent_acc) 191 + { 192 + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 193 + 194 + return osc->accuracy; 195 + } 196 + 197 + static int clk_slow_rc_osc_prepare(struct clk_hw *hw) 198 + { 199 + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 200 + void __iomem *sckcr = osc->sckcr; 201 + 202 + writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr); 203 + 204 + usleep_range(osc->startup_usec, osc->startup_usec + 1); 205 + 206 + return 0; 207 + } 208 + 209 + static void clk_slow_rc_osc_unprepare(struct clk_hw *hw) 210 + { 211 + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 212 + void __iomem *sckcr = osc->sckcr; 213 + 214 + writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr); 215 + } 216 + 217 + static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw) 218 + { 219 + struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw); 220 + 221 + return !!(readl(osc->sckcr) & AT91_SCKC_RCEN); 222 + } 223 + 224 + static const struct clk_ops slow_rc_osc_ops = { 225 + .prepare = clk_slow_rc_osc_prepare, 226 + .unprepare = clk_slow_rc_osc_unprepare, 227 + .is_prepared = clk_slow_rc_osc_is_prepared, 228 + .recalc_rate = clk_slow_rc_osc_recalc_rate, 229 + .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy, 230 + }; 231 + 232 + static struct clk * __init 233 + at91_clk_register_slow_rc_osc(void __iomem *sckcr, 234 + const char *name, 235 + unsigned long frequency, 236 + unsigned long accuracy, 237 + unsigned long startup) 238 + { 239 + struct clk_slow_rc_osc *osc; 240 + struct clk *clk = NULL; 241 + struct clk_init_data init; 242 + 243 + if (!sckcr || !name) 244 + return ERR_PTR(-EINVAL); 245 + 246 + osc = kzalloc(sizeof(*osc), GFP_KERNEL); 247 + if (!osc) 248 + return ERR_PTR(-ENOMEM); 249 + 250 + init.name = name; 251 + init.ops = &slow_rc_osc_ops; 252 + init.parent_names = NULL; 253 + init.num_parents = 0; 254 + init.flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED; 255 + 256 + osc->hw.init = &init; 257 + osc->sckcr = sckcr; 258 + osc->frequency = frequency; 259 + osc->accuracy = accuracy; 260 + osc->startup_usec = startup; 261 + 262 + clk = clk_register(NULL, &osc->hw); 263 + if (IS_ERR(clk)) 264 + kfree(osc); 265 + 266 + return clk; 267 + } 268 + 269 + void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, 270 + void __iomem *sckcr) 271 + { 272 + struct clk *clk; 273 + u32 frequency = 0; 274 + u32 accuracy = 0; 275 + u32 startup = 0; 276 + const char *name = np->name; 277 + 278 + of_property_read_string(np, "clock-output-names", &name); 279 + of_property_read_u32(np, "clock-frequency", &frequency); 280 + of_property_read_u32(np, "clock-accuracy", &accuracy); 281 + of_property_read_u32(np, "atmel,startup-time-usec", &startup); 282 + 283 + clk = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy, 284 + startup); 285 + if (IS_ERR(clk)) 286 + return; 287 + 288 + of_clk_add_provider(np, of_clk_src_simple_get, clk); 289 + } 290 + 291 + static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index) 292 + { 293 + struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 294 + void __iomem *sckcr = slowck->sckcr; 295 + u32 tmp; 296 + 297 + if (index > 1) 298 + return -EINVAL; 299 + 300 + tmp = readl(sckcr); 301 + 302 + if ((!index && !(tmp & AT91_SCKC_OSCSEL)) || 303 + (index && (tmp & AT91_SCKC_OSCSEL))) 304 + return 0; 305 + 306 + if (index) 307 + tmp |= AT91_SCKC_OSCSEL; 308 + else 309 + tmp &= ~AT91_SCKC_OSCSEL; 310 + 311 + writel(tmp, sckcr); 312 + 313 + usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1); 314 + 315 + return 0; 316 + } 317 + 318 + static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw) 319 + { 320 + struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw); 321 + 322 + return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL); 323 + } 324 + 325 + static const struct clk_ops sam9x5_slow_ops = { 326 + .set_parent = clk_sam9x5_slow_set_parent, 327 + .get_parent = clk_sam9x5_slow_get_parent, 328 + }; 329 + 330 + static struct clk * __init 331 + at91_clk_register_sam9x5_slow(void __iomem *sckcr, 332 + const char *name, 333 + const char **parent_names, 334 + int num_parents) 335 + { 336 + struct clk_sam9x5_slow *slowck; 337 + struct clk *clk = NULL; 338 + struct clk_init_data init; 339 + 340 + if (!sckcr || !name || !parent_names || !num_parents) 341 + return ERR_PTR(-EINVAL); 342 + 343 + slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); 344 + if (!slowck) 345 + return ERR_PTR(-ENOMEM); 346 + 347 + init.name = name; 348 + init.ops = &sam9x5_slow_ops; 349 + init.parent_names = parent_names; 350 + init.num_parents = num_parents; 351 + init.flags = 0; 352 + 353 + slowck->hw.init = &init; 354 + slowck->sckcr = sckcr; 355 + slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL); 356 + 357 + clk = clk_register(NULL, &slowck->hw); 358 + if (IS_ERR(clk)) 359 + kfree(slowck); 360 + 361 + return clk; 362 + } 363 + 364 + void __init of_at91sam9x5_clk_slow_setup(struct device_node *np, 365 + void __iomem *sckcr) 366 + { 367 + struct clk *clk; 368 + const char *parent_names[2]; 369 + int num_parents; 370 + const char *name = np->name; 371 + int i; 372 + 373 + num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); 374 + if (num_parents <= 0 || num_parents > 2) 375 + return; 376 + 377 + for (i = 0; i < num_parents; ++i) { 378 + parent_names[i] = of_clk_get_parent_name(np, i); 379 + if (!parent_names[i]) 380 + return; 381 + } 382 + 383 + of_property_read_string(np, "clock-output-names", &name); 384 + 385 + clk = at91_clk_register_sam9x5_slow(sckcr, name, parent_names, 386 + num_parents); 387 + if (IS_ERR(clk)) 388 + return; 389 + 390 + of_clk_add_provider(np, of_clk_src_simple_get, clk); 391 + } 392 + 393 + static u8 clk_sam9260_slow_get_parent(struct clk_hw *hw) 394 + { 395 + struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(hw); 396 + 397 + return !!(pmc_read(slowck->pmc, AT91_PMC_SR) & AT91_PMC_OSCSEL); 398 + } 399 + 400 + static const struct clk_ops sam9260_slow_ops = { 401 + .get_parent = clk_sam9260_slow_get_parent, 402 + }; 403 + 404 + static struct clk * __init 405 + at91_clk_register_sam9260_slow(struct at91_pmc *pmc, 406 + const char *name, 407 + const char **parent_names, 408 + int num_parents) 409 + { 410 + struct clk_sam9260_slow *slowck; 411 + struct clk *clk = NULL; 412 + struct clk_init_data init; 413 + 414 + if (!pmc || !name) 415 + return ERR_PTR(-EINVAL); 416 + 417 + if (!parent_names || !num_parents) 418 + return ERR_PTR(-EINVAL); 419 + 420 + slowck = kzalloc(sizeof(*slowck), GFP_KERNEL); 421 + if (!slowck) 422 + return ERR_PTR(-ENOMEM); 423 + 424 + init.name = name; 425 + init.ops = &sam9260_slow_ops; 426 + init.parent_names = parent_names; 427 + init.num_parents = num_parents; 428 + init.flags = 0; 429 + 430 + slowck->hw.init = &init; 431 + slowck->pmc = pmc; 432 + 433 + clk = clk_register(NULL, &slowck->hw); 434 + if (IS_ERR(clk)) 435 + kfree(slowck); 436 + 437 + return clk; 438 + } 439 + 440 + void __init of_at91sam9260_clk_slow_setup(struct device_node *np, 441 + struct at91_pmc *pmc) 442 + { 443 + struct clk *clk; 444 + const char *parent_names[2]; 445 + int num_parents; 446 + const char *name = np->name; 447 + int i; 448 + 449 + num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); 450 + if (num_parents <= 0 || num_parents > 1) 451 + return; 452 + 453 + for (i = 0; i < num_parents; ++i) { 454 + parent_names[i] = of_clk_get_parent_name(np, i); 455 + if (!parent_names[i]) 456 + return; 457 + } 458 + 459 + of_property_read_string(np, "clock-output-names", &name); 460 + 461 + clk = at91_clk_register_sam9260_slow(pmc, name, parent_names, 462 + num_parents); 463 + if (IS_ERR(clk)) 464 + return; 465 + 466 + of_clk_add_provider(np, of_clk_src_simple_get, clk); 467 + }
+5
drivers/clk/at91/pmc.c
··· 229 229 } 230 230 231 231 static const struct of_device_id pmc_clk_ids[] __initconst = { 232 + /* Slow oscillator */ 233 + { 234 + .compatible = "atmel,at91sam9260-clk-slow", 235 + .data = of_at91sam9260_clk_slow_setup, 236 + }, 232 237 /* Main clock */ 233 238 { 234 239 .compatible = "atmel,at91rm9200-clk-main-osc",
+3
drivers/clk/at91/pmc.h
··· 58 58 int of_at91_get_clk_range(struct device_node *np, const char *propname, 59 59 struct clk_range *range); 60 60 61 + extern void __init of_at91sam9260_clk_slow_setup(struct device_node *np, 62 + struct at91_pmc *pmc); 63 + 61 64 extern void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np, 62 65 struct at91_pmc *pmc); 63 66 extern void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np,
+57
drivers/clk/at91/sckc.c
··· 1 + /* 2 + * drivers/clk/at91/sckc.c 3 + * 4 + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + */ 12 + 13 + #include <linux/clk-provider.h> 14 + #include <linux/clkdev.h> 15 + #include <linux/of.h> 16 + #include <linux/of_address.h> 17 + #include <linux/io.h> 18 + 19 + #include "sckc.h" 20 + 21 + static const struct of_device_id sckc_clk_ids[] __initconst = { 22 + /* Slow clock */ 23 + { 24 + .compatible = "atmel,at91sam9x5-clk-slow-osc", 25 + .data = of_at91sam9x5_clk_slow_osc_setup, 26 + }, 27 + { 28 + .compatible = "atmel,at91sam9x5-clk-slow-rc-osc", 29 + .data = of_at91sam9x5_clk_slow_rc_osc_setup, 30 + }, 31 + { 32 + .compatible = "atmel,at91sam9x5-clk-slow", 33 + .data = of_at91sam9x5_clk_slow_setup, 34 + }, 35 + { /*sentinel*/ } 36 + }; 37 + 38 + static void __init of_at91sam9x5_sckc_setup(struct device_node *np) 39 + { 40 + struct device_node *childnp; 41 + void (*clk_setup)(struct device_node *, void __iomem *); 42 + const struct of_device_id *clk_id; 43 + void __iomem *regbase = of_iomap(np, 0); 44 + 45 + if (!regbase) 46 + return; 47 + 48 + for_each_child_of_node(np, childnp) { 49 + clk_id = of_match_node(sckc_clk_ids, childnp); 50 + if (!clk_id) 51 + continue; 52 + clk_setup = clk_id->data; 53 + clk_setup(childnp, regbase); 54 + } 55 + } 56 + CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc", 57 + of_at91sam9x5_sckc_setup);
+22
drivers/clk/at91/sckc.h
··· 1 + /* 2 + * drivers/clk/at91/sckc.h 3 + * 4 + * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + */ 11 + 12 + #ifndef __AT91_SCKC_H_ 13 + #define __AT91_SCKC_H_ 14 + 15 + extern void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, 16 + void __iomem *sckcr); 17 + extern void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, 18 + void __iomem *sckcr); 19 + extern void __init of_at91sam9x5_clk_slow_setup(struct device_node *np, 20 + void __iomem *sckcr); 21 + 22 + #endif /* __AT91_SCKC_H_ */
+1
include/linux/clk/at91_pmc.h
··· 155 155 #define AT91_PMC_LOCKB (1 << 2) /* PLLB Lock */ 156 156 #define AT91_PMC_MCKRDY (1 << 3) /* Master Clock */ 157 157 #define AT91_PMC_LOCKU (1 << 6) /* UPLL Lock [some SAM9] */ 158 + #define AT91_PMC_OSCSEL (1 << 7) /* Slow Oscillator Selection [some SAM9] */ 158 159 #define AT91_PMC_PCK0RDY (1 << 8) /* Programmable Clock 0 */ 159 160 #define AT91_PMC_PCK1RDY (1 << 9) /* Programmable Clock 1 */ 160 161 #define AT91_PMC_PCK2RDY (1 << 10) /* Programmable Clock 2 */