linux/drivers/clk/at91/sckc.c
<<
>>
Prefs
   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/delay.h>
  16#include <linux/of.h>
  17#include <linux/of_address.h>
  18#include <linux/io.h>
  19
  20#define SLOW_CLOCK_FREQ         32768
  21#define SLOWCK_SW_CYCLES        5
  22#define SLOWCK_SW_TIME_USEC     ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
  23                                 SLOW_CLOCK_FREQ)
  24
  25#define AT91_SCKC_CR                    0x00
  26#define         AT91_SCKC_RCEN          (1 << 0)
  27#define         AT91_SCKC_OSC32EN       (1 << 1)
  28#define         AT91_SCKC_OSC32BYP      (1 << 2)
  29#define         AT91_SCKC_OSCSEL        (1 << 3)
  30
  31struct clk_slow_osc {
  32        struct clk_hw hw;
  33        void __iomem *sckcr;
  34        unsigned long startup_usec;
  35};
  36
  37#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)
  38
  39struct clk_sama5d4_slow_osc {
  40        struct clk_hw hw;
  41        void __iomem *sckcr;
  42        unsigned long startup_usec;
  43        bool prepared;
  44};
  45
  46#define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw)
  47
  48struct 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
  58struct clk_sam9x5_slow {
  59        struct clk_hw hw;
  60        void __iomem *sckcr;
  61        u8 parent;
  62};
  63
  64#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)
  65
  66static int clk_slow_osc_prepare(struct clk_hw *hw)
  67{
  68        struct clk_slow_osc *osc = to_clk_slow_osc(hw);
  69        void __iomem *sckcr = osc->sckcr;
  70        u32 tmp = readl(sckcr);
  71
  72        if (tmp & (AT91_SCKC_OSC32BYP | AT91_SCKC_OSC32EN))
  73                return 0;
  74
  75        writel(tmp | AT91_SCKC_OSC32EN, sckcr);
  76
  77        usleep_range(osc->startup_usec, osc->startup_usec + 1);
  78
  79        return 0;
  80}
  81
  82static void clk_slow_osc_unprepare(struct clk_hw *hw)
  83{
  84        struct clk_slow_osc *osc = to_clk_slow_osc(hw);
  85        void __iomem *sckcr = osc->sckcr;
  86        u32 tmp = readl(sckcr);
  87
  88        if (tmp & AT91_SCKC_OSC32BYP)
  89                return;
  90
  91        writel(tmp & ~AT91_SCKC_OSC32EN, sckcr);
  92}
  93
  94static int clk_slow_osc_is_prepared(struct clk_hw *hw)
  95{
  96        struct clk_slow_osc *osc = to_clk_slow_osc(hw);
  97        void __iomem *sckcr = osc->sckcr;
  98        u32 tmp = readl(sckcr);
  99
 100        if (tmp & AT91_SCKC_OSC32BYP)
 101                return 1;
 102
 103        return !!(tmp & AT91_SCKC_OSC32EN);
 104}
 105
 106static const struct clk_ops slow_osc_ops = {
 107        .prepare = clk_slow_osc_prepare,
 108        .unprepare = clk_slow_osc_unprepare,
 109        .is_prepared = clk_slow_osc_is_prepared,
 110};
 111
 112static struct clk_hw * __init
 113at91_clk_register_slow_osc(void __iomem *sckcr,
 114                           const char *name,
 115                           const char *parent_name,
 116                           unsigned long startup,
 117                           bool bypass)
 118{
 119        struct clk_slow_osc *osc;
 120        struct clk_hw *hw;
 121        struct clk_init_data init;
 122        int ret;
 123
 124        if (!sckcr || !name || !parent_name)
 125                return ERR_PTR(-EINVAL);
 126
 127        osc = kzalloc(sizeof(*osc), GFP_KERNEL);
 128        if (!osc)
 129                return ERR_PTR(-ENOMEM);
 130
 131        init.name = name;
 132        init.ops = &slow_osc_ops;
 133        init.parent_names = &parent_name;
 134        init.num_parents = 1;
 135        init.flags = CLK_IGNORE_UNUSED;
 136
 137        osc->hw.init = &init;
 138        osc->sckcr = sckcr;
 139        osc->startup_usec = startup;
 140
 141        if (bypass)
 142                writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP,
 143                       sckcr);
 144
 145        hw = &osc->hw;
 146        ret = clk_hw_register(NULL, &osc->hw);
 147        if (ret) {
 148                kfree(osc);
 149                hw = ERR_PTR(ret);
 150        }
 151
 152        return hw;
 153}
 154
 155static void __init
 156of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, void __iomem *sckcr)
 157{
 158        struct clk_hw *hw;
 159        const char *parent_name;
 160        const char *name = np->name;
 161        u32 startup;
 162        bool bypass;
 163
 164        parent_name = of_clk_get_parent_name(np, 0);
 165        of_property_read_string(np, "clock-output-names", &name);
 166        of_property_read_u32(np, "atmel,startup-time-usec", &startup);
 167        bypass = of_property_read_bool(np, "atmel,osc-bypass");
 168
 169        hw = at91_clk_register_slow_osc(sckcr, name, parent_name, startup,
 170                                         bypass);
 171        if (IS_ERR(hw))
 172                return;
 173
 174        of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
 175}
 176
 177static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
 178                                                 unsigned long parent_rate)
 179{
 180        struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
 181
 182        return osc->frequency;
 183}
 184
 185static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,
 186                                                     unsigned long parent_acc)
 187{
 188        struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
 189
 190        return osc->accuracy;
 191}
 192
 193static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
 194{
 195        struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
 196        void __iomem *sckcr = osc->sckcr;
 197
 198        writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
 199
 200        usleep_range(osc->startup_usec, osc->startup_usec + 1);
 201
 202        return 0;
 203}
 204
 205static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)
 206{
 207        struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
 208        void __iomem *sckcr = osc->sckcr;
 209
 210        writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr);
 211}
 212
 213static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
 214{
 215        struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
 216
 217        return !!(readl(osc->sckcr) & AT91_SCKC_RCEN);
 218}
 219
 220static const struct clk_ops slow_rc_osc_ops = {
 221        .prepare = clk_slow_rc_osc_prepare,
 222        .unprepare = clk_slow_rc_osc_unprepare,
 223        .is_prepared = clk_slow_rc_osc_is_prepared,
 224        .recalc_rate = clk_slow_rc_osc_recalc_rate,
 225        .recalc_accuracy = clk_slow_rc_osc_recalc_accuracy,
 226};
 227
 228static struct clk_hw * __init
 229at91_clk_register_slow_rc_osc(void __iomem *sckcr,
 230                              const char *name,
 231                              unsigned long frequency,
 232                              unsigned long accuracy,
 233                              unsigned long startup)
 234{
 235        struct clk_slow_rc_osc *osc;
 236        struct clk_hw *hw;
 237        struct clk_init_data init;
 238        int ret;
 239
 240        if (!sckcr || !name)
 241                return ERR_PTR(-EINVAL);
 242
 243        osc = kzalloc(sizeof(*osc), GFP_KERNEL);
 244        if (!osc)
 245                return ERR_PTR(-ENOMEM);
 246
 247        init.name = name;
 248        init.ops = &slow_rc_osc_ops;
 249        init.parent_names = NULL;
 250        init.num_parents = 0;
 251        init.flags = CLK_IGNORE_UNUSED;
 252
 253        osc->hw.init = &init;
 254        osc->sckcr = sckcr;
 255        osc->frequency = frequency;
 256        osc->accuracy = accuracy;
 257        osc->startup_usec = startup;
 258
 259        hw = &osc->hw;
 260        ret = clk_hw_register(NULL, &osc->hw);
 261        if (ret) {
 262                kfree(osc);
 263                hw = ERR_PTR(ret);
 264        }
 265
 266        return hw;
 267}
 268
 269static void __init
 270of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, void __iomem *sckcr)
 271{
 272        struct clk_hw *hw;
 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        hw = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy,
 284                                            startup);
 285        if (IS_ERR(hw))
 286                return;
 287
 288        of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
 289}
 290
 291static 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
 318static 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
 325static 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
 330static struct clk_hw * __init
 331at91_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_hw *hw;
 338        struct clk_init_data init;
 339        int ret;
 340
 341        if (!sckcr || !name || !parent_names || !num_parents)
 342                return ERR_PTR(-EINVAL);
 343
 344        slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
 345        if (!slowck)
 346                return ERR_PTR(-ENOMEM);
 347
 348        init.name = name;
 349        init.ops = &sam9x5_slow_ops;
 350        init.parent_names = parent_names;
 351        init.num_parents = num_parents;
 352        init.flags = 0;
 353
 354        slowck->hw.init = &init;
 355        slowck->sckcr = sckcr;
 356        slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL);
 357
 358        hw = &slowck->hw;
 359        ret = clk_hw_register(NULL, &slowck->hw);
 360        if (ret) {
 361                kfree(slowck);
 362                hw = ERR_PTR(ret);
 363        }
 364
 365        return hw;
 366}
 367
 368static void __init
 369of_at91sam9x5_clk_slow_setup(struct device_node *np, void __iomem *sckcr)
 370{
 371        struct clk_hw *hw;
 372        const char *parent_names[2];
 373        unsigned int num_parents;
 374        const char *name = np->name;
 375
 376        num_parents = of_clk_get_parent_count(np);
 377        if (num_parents == 0 || num_parents > 2)
 378                return;
 379
 380        of_clk_parent_fill(np, parent_names, num_parents);
 381
 382        of_property_read_string(np, "clock-output-names", &name);
 383
 384        hw = at91_clk_register_sam9x5_slow(sckcr, name, parent_names,
 385                                            num_parents);
 386        if (IS_ERR(hw))
 387                return;
 388
 389        of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
 390}
 391
 392static const struct of_device_id sckc_clk_ids[] __initconst = {
 393        /* Slow clock */
 394        {
 395                .compatible = "atmel,at91sam9x5-clk-slow-osc",
 396                .data = of_at91sam9x5_clk_slow_osc_setup,
 397        },
 398        {
 399                .compatible = "atmel,at91sam9x5-clk-slow-rc-osc",
 400                .data = of_at91sam9x5_clk_slow_rc_osc_setup,
 401        },
 402        {
 403                .compatible = "atmel,at91sam9x5-clk-slow",
 404                .data = of_at91sam9x5_clk_slow_setup,
 405        },
 406        { /*sentinel*/ }
 407};
 408
 409static void __init of_at91sam9x5_sckc_setup(struct device_node *np)
 410{
 411        struct device_node *childnp;
 412        void (*clk_setup)(struct device_node *, void __iomem *);
 413        const struct of_device_id *clk_id;
 414        void __iomem *regbase = of_iomap(np, 0);
 415
 416        if (!regbase)
 417                return;
 418
 419        for_each_child_of_node(np, childnp) {
 420                clk_id = of_match_node(sckc_clk_ids, childnp);
 421                if (!clk_id)
 422                        continue;
 423                clk_setup = clk_id->data;
 424                clk_setup(childnp, regbase);
 425        }
 426}
 427CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
 428               of_at91sam9x5_sckc_setup);
 429
 430static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
 431{
 432        struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
 433
 434        if (osc->prepared)
 435                return 0;
 436
 437        /*
 438         * Assume that if it has already been selected (for example by the
 439         * bootloader), enough time has aready passed.
 440         */
 441        if ((readl(osc->sckcr) & AT91_SCKC_OSCSEL)) {
 442                osc->prepared = true;
 443                return 0;
 444        }
 445
 446        usleep_range(osc->startup_usec, osc->startup_usec + 1);
 447        osc->prepared = true;
 448
 449        return 0;
 450}
 451
 452static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw)
 453{
 454        struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
 455
 456        return osc->prepared;
 457}
 458
 459static const struct clk_ops sama5d4_slow_osc_ops = {
 460        .prepare = clk_sama5d4_slow_osc_prepare,
 461        .is_prepared = clk_sama5d4_slow_osc_is_prepared,
 462};
 463
 464static void __init of_sama5d4_sckc_setup(struct device_node *np)
 465{
 466        void __iomem *regbase = of_iomap(np, 0);
 467        struct clk_hw *hw;
 468        struct clk_sama5d4_slow_osc *osc;
 469        struct clk_init_data init;
 470        const char *xtal_name;
 471        const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
 472        bool bypass;
 473        int ret;
 474
 475        if (!regbase)
 476                return;
 477
 478        hw = clk_hw_register_fixed_rate_with_accuracy(NULL, parent_names[0],
 479                                                      NULL, 0, 32768,
 480                                                      250000000);
 481        if (IS_ERR(hw))
 482                return;
 483
 484        xtal_name = of_clk_get_parent_name(np, 0);
 485
 486        bypass = of_property_read_bool(np, "atmel,osc-bypass");
 487
 488        osc = kzalloc(sizeof(*osc), GFP_KERNEL);
 489        if (!osc)
 490                return;
 491
 492        init.name = parent_names[1];
 493        init.ops = &sama5d4_slow_osc_ops;
 494        init.parent_names = &xtal_name;
 495        init.num_parents = 1;
 496        init.flags = CLK_IGNORE_UNUSED;
 497
 498        osc->hw.init = &init;
 499        osc->sckcr = regbase;
 500        osc->startup_usec = 1200000;
 501
 502        if (bypass)
 503                writel((readl(regbase) | AT91_SCKC_OSC32BYP), regbase);
 504
 505        hw = &osc->hw;
 506        ret = clk_hw_register(NULL, &osc->hw);
 507        if (ret) {
 508                kfree(osc);
 509                return;
 510        }
 511
 512        hw = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2);
 513        if (IS_ERR(hw))
 514                return;
 515
 516        of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
 517}
 518CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
 519               of_sama5d4_sckc_setup);
 520