linux/drivers/clocksource/ingenic-sysost.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Ingenic XBurst SoCs SYSOST clocks driver
   4 * Copyright (c) 2020 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
   5 */
   6
   7#include <linux/bitfield.h>
   8#include <linux/bitops.h>
   9#include <linux/clk.h>
  10#include <linux/clk-provider.h>
  11#include <linux/clockchips.h>
  12#include <linux/clocksource.h>
  13#include <linux/interrupt.h>
  14#include <linux/mfd/syscon.h>
  15#include <linux/of_address.h>
  16#include <linux/of_irq.h>
  17#include <linux/sched_clock.h>
  18#include <linux/slab.h>
  19#include <linux/syscore_ops.h>
  20
  21#include <dt-bindings/clock/ingenic,sysost.h>
  22
  23/* OST register offsets */
  24#define OST_REG_OSTCCR                  0x00
  25#define OST_REG_OSTCR                   0x08
  26#define OST_REG_OSTFR                   0x0c
  27#define OST_REG_OSTMR                   0x10
  28#define OST_REG_OST1DFR                 0x14
  29#define OST_REG_OST1CNT                 0x18
  30#define OST_REG_OST2CNTL                0x20
  31#define OST_REG_OSTCNT2HBUF             0x24
  32#define OST_REG_OSTESR                  0x34
  33#define OST_REG_OSTECR                  0x38
  34
  35/* bits within the OSTCCR register */
  36#define OSTCCR_PRESCALE1_MASK   0x3
  37#define OSTCCR_PRESCALE2_MASK   0xc
  38
  39/* bits within the OSTCR register */
  40#define OSTCR_OST1CLR                   BIT(0)
  41#define OSTCR_OST2CLR                   BIT(1)
  42
  43/* bits within the OSTFR register */
  44#define OSTFR_FFLAG                             BIT(0)
  45
  46/* bits within the OSTMR register */
  47#define OSTMR_FMASK                             BIT(0)
  48
  49/* bits within the OSTESR register */
  50#define OSTESR_OST1ENS                  BIT(0)
  51#define OSTESR_OST2ENS                  BIT(1)
  52
  53/* bits within the OSTECR register */
  54#define OSTECR_OST1ENC                  BIT(0)
  55#define OSTECR_OST2ENC                  BIT(1)
  56
  57struct ingenic_soc_info {
  58        unsigned int num_channels;
  59};
  60
  61struct ingenic_ost_clk_info {
  62        struct clk_init_data init_data;
  63        u8 ostccr_reg;
  64};
  65
  66struct ingenic_ost_clk {
  67        struct clk_hw hw;
  68        unsigned int idx;
  69        struct ingenic_ost *ost;
  70        const struct ingenic_ost_clk_info *info;
  71};
  72
  73struct ingenic_ost {
  74        void __iomem *base;
  75        const struct ingenic_soc_info *soc_info;
  76        struct clk *clk, *percpu_timer_clk, *global_timer_clk;
  77        struct clock_event_device cevt;
  78        struct clocksource cs;
  79        char name[20];
  80
  81        struct clk_hw_onecell_data *clocks;
  82};
  83
  84static struct ingenic_ost *ingenic_ost;
  85
  86static inline struct ingenic_ost_clk *to_ost_clk(struct clk_hw *hw)
  87{
  88        return container_of(hw, struct ingenic_ost_clk, hw);
  89}
  90
  91static unsigned long ingenic_ost_percpu_timer_recalc_rate(struct clk_hw *hw,
  92                unsigned long parent_rate)
  93{
  94        struct ingenic_ost_clk *ost_clk = to_ost_clk(hw);
  95        const struct ingenic_ost_clk_info *info = ost_clk->info;
  96        unsigned int prescale;
  97
  98        prescale = readl(ost_clk->ost->base + info->ostccr_reg);
  99
 100        prescale = FIELD_GET(OSTCCR_PRESCALE1_MASK, prescale);
 101
 102        return parent_rate >> (prescale * 2);
 103}
 104
 105static unsigned long ingenic_ost_global_timer_recalc_rate(struct clk_hw *hw,
 106                unsigned long parent_rate)
 107{
 108        struct ingenic_ost_clk *ost_clk = to_ost_clk(hw);
 109        const struct ingenic_ost_clk_info *info = ost_clk->info;
 110        unsigned int prescale;
 111
 112        prescale = readl(ost_clk->ost->base + info->ostccr_reg);
 113
 114        prescale = FIELD_GET(OSTCCR_PRESCALE2_MASK, prescale);
 115
 116        return parent_rate >> (prescale * 2);
 117}
 118
 119static u8 ingenic_ost_get_prescale(unsigned long rate, unsigned long req_rate)
 120{
 121        u8 prescale;
 122
 123        for (prescale = 0; prescale < 2; prescale++)
 124                if ((rate >> (prescale * 2)) <= req_rate)
 125                        return prescale;
 126
 127        return 2; /* /16 divider */
 128}
 129
 130static long ingenic_ost_round_rate(struct clk_hw *hw, unsigned long req_rate,
 131                unsigned long *parent_rate)
 132{
 133        unsigned long rate = *parent_rate;
 134        u8 prescale;
 135
 136        if (req_rate > rate)
 137                return rate;
 138
 139        prescale = ingenic_ost_get_prescale(rate, req_rate);
 140
 141        return rate >> (prescale * 2);
 142}
 143
 144static int ingenic_ost_percpu_timer_set_rate(struct clk_hw *hw, unsigned long req_rate,
 145                unsigned long parent_rate)
 146{
 147        struct ingenic_ost_clk *ost_clk = to_ost_clk(hw);
 148        const struct ingenic_ost_clk_info *info = ost_clk->info;
 149        u8 prescale = ingenic_ost_get_prescale(parent_rate, req_rate);
 150        int val;
 151
 152        val = readl(ost_clk->ost->base + info->ostccr_reg);
 153        val &= ~OSTCCR_PRESCALE1_MASK;
 154        val |= FIELD_PREP(OSTCCR_PRESCALE1_MASK, prescale);
 155        writel(val, ost_clk->ost->base + info->ostccr_reg);
 156
 157        return 0;
 158}
 159
 160static int ingenic_ost_global_timer_set_rate(struct clk_hw *hw, unsigned long req_rate,
 161                unsigned long parent_rate)
 162{
 163        struct ingenic_ost_clk *ost_clk = to_ost_clk(hw);
 164        const struct ingenic_ost_clk_info *info = ost_clk->info;
 165        u8 prescale = ingenic_ost_get_prescale(parent_rate, req_rate);
 166        int val;
 167
 168        val = readl(ost_clk->ost->base + info->ostccr_reg);
 169        val &= ~OSTCCR_PRESCALE2_MASK;
 170        val |= FIELD_PREP(OSTCCR_PRESCALE2_MASK, prescale);
 171        writel(val, ost_clk->ost->base + info->ostccr_reg);
 172
 173        return 0;
 174}
 175
 176static const struct clk_ops ingenic_ost_percpu_timer_ops = {
 177        .recalc_rate    = ingenic_ost_percpu_timer_recalc_rate,
 178        .round_rate             = ingenic_ost_round_rate,
 179        .set_rate               = ingenic_ost_percpu_timer_set_rate,
 180};
 181
 182static const struct clk_ops ingenic_ost_global_timer_ops = {
 183        .recalc_rate    = ingenic_ost_global_timer_recalc_rate,
 184        .round_rate             = ingenic_ost_round_rate,
 185        .set_rate               = ingenic_ost_global_timer_set_rate,
 186};
 187
 188static const char * const ingenic_ost_clk_parents[] = { "ext" };
 189
 190static const struct ingenic_ost_clk_info x1000_ost_clk_info[] = {
 191        [OST_CLK_PERCPU_TIMER] = {
 192                .init_data = {
 193                        .name = "percpu timer",
 194                        .parent_names = ingenic_ost_clk_parents,
 195                        .num_parents = ARRAY_SIZE(ingenic_ost_clk_parents),
 196                        .ops = &ingenic_ost_percpu_timer_ops,
 197                        .flags = CLK_SET_RATE_UNGATE,
 198                },
 199                .ostccr_reg = OST_REG_OSTCCR,
 200        },
 201
 202        [OST_CLK_GLOBAL_TIMER] = {
 203                .init_data = {
 204                        .name = "global timer",
 205                        .parent_names = ingenic_ost_clk_parents,
 206                        .num_parents = ARRAY_SIZE(ingenic_ost_clk_parents),
 207                        .ops = &ingenic_ost_global_timer_ops,
 208                        .flags = CLK_SET_RATE_UNGATE,
 209                },
 210                .ostccr_reg = OST_REG_OSTCCR,
 211        },
 212};
 213
 214static u64 notrace ingenic_ost_global_timer_read_cntl(void)
 215{
 216        struct ingenic_ost *ost = ingenic_ost;
 217        unsigned int count;
 218
 219        count = readl(ost->base + OST_REG_OST2CNTL);
 220
 221        return count;
 222}
 223
 224static u64 notrace ingenic_ost_clocksource_read(struct clocksource *cs)
 225{
 226        return ingenic_ost_global_timer_read_cntl();
 227}
 228
 229static inline struct ingenic_ost *to_ingenic_ost(struct clock_event_device *evt)
 230{
 231        return container_of(evt, struct ingenic_ost, cevt);
 232}
 233
 234static int ingenic_ost_cevt_set_state_shutdown(struct clock_event_device *evt)
 235{
 236        struct ingenic_ost *ost = to_ingenic_ost(evt);
 237
 238        writel(OSTECR_OST1ENC, ost->base + OST_REG_OSTECR);
 239
 240        return 0;
 241}
 242
 243static int ingenic_ost_cevt_set_next(unsigned long next,
 244                                     struct clock_event_device *evt)
 245{
 246        struct ingenic_ost *ost = to_ingenic_ost(evt);
 247
 248        writel((u32)~OSTFR_FFLAG, ost->base + OST_REG_OSTFR);
 249        writel(next, ost->base + OST_REG_OST1DFR);
 250        writel(OSTCR_OST1CLR, ost->base + OST_REG_OSTCR);
 251        writel(OSTESR_OST1ENS, ost->base + OST_REG_OSTESR);
 252        writel((u32)~OSTMR_FMASK, ost->base + OST_REG_OSTMR);
 253
 254        return 0;
 255}
 256
 257static irqreturn_t ingenic_ost_cevt_cb(int irq, void *dev_id)
 258{
 259        struct clock_event_device *evt = dev_id;
 260        struct ingenic_ost *ost = to_ingenic_ost(evt);
 261
 262        writel(OSTECR_OST1ENC, ost->base + OST_REG_OSTECR);
 263
 264        if (evt->event_handler)
 265                evt->event_handler(evt);
 266
 267        return IRQ_HANDLED;
 268}
 269
 270static int __init ingenic_ost_register_clock(struct ingenic_ost *ost,
 271                        unsigned int idx, const struct ingenic_ost_clk_info *info,
 272                        struct clk_hw_onecell_data *clocks)
 273{
 274        struct ingenic_ost_clk *ost_clk;
 275        int val, err;
 276
 277        ost_clk = kzalloc(sizeof(*ost_clk), GFP_KERNEL);
 278        if (!ost_clk)
 279                return -ENOMEM;
 280
 281        ost_clk->hw.init = &info->init_data;
 282        ost_clk->idx = idx;
 283        ost_clk->info = info;
 284        ost_clk->ost = ost;
 285
 286        /* Reset clock divider */
 287        val = readl(ost->base + info->ostccr_reg);
 288        val &= ~(OSTCCR_PRESCALE1_MASK | OSTCCR_PRESCALE2_MASK);
 289        writel(val, ost->base + info->ostccr_reg);
 290
 291        err = clk_hw_register(NULL, &ost_clk->hw);
 292        if (err) {
 293                kfree(ost_clk);
 294                return err;
 295        }
 296
 297        clocks->hws[idx] = &ost_clk->hw;
 298
 299        return 0;
 300}
 301
 302static struct clk * __init ingenic_ost_get_clock(struct device_node *np, int id)
 303{
 304        struct of_phandle_args args;
 305
 306        args.np = np;
 307        args.args_count = 1;
 308        args.args[0] = id;
 309
 310        return of_clk_get_from_provider(&args);
 311}
 312
 313static int __init ingenic_ost_percpu_timer_init(struct device_node *np,
 314                                         struct ingenic_ost *ost)
 315{
 316        unsigned int timer_virq, channel = OST_CLK_PERCPU_TIMER;
 317        unsigned long rate;
 318        int err;
 319
 320        ost->percpu_timer_clk = ingenic_ost_get_clock(np, channel);
 321        if (IS_ERR(ost->percpu_timer_clk))
 322                return PTR_ERR(ost->percpu_timer_clk);
 323
 324        err = clk_prepare_enable(ost->percpu_timer_clk);
 325        if (err)
 326                goto err_clk_put;
 327
 328        rate = clk_get_rate(ost->percpu_timer_clk);
 329        if (!rate) {
 330                err = -EINVAL;
 331                goto err_clk_disable;
 332        }
 333
 334        timer_virq = of_irq_get(np, 0);
 335        if (!timer_virq) {
 336                err = -EINVAL;
 337                goto err_clk_disable;
 338        }
 339
 340        snprintf(ost->name, sizeof(ost->name), "OST percpu timer");
 341
 342        err = request_irq(timer_virq, ingenic_ost_cevt_cb, IRQF_TIMER,
 343                          ost->name, &ost->cevt);
 344        if (err)
 345                goto err_irq_dispose_mapping;
 346
 347        ost->cevt.cpumask = cpumask_of(smp_processor_id());
 348        ost->cevt.features = CLOCK_EVT_FEAT_ONESHOT;
 349        ost->cevt.name = ost->name;
 350        ost->cevt.rating = 400;
 351        ost->cevt.set_state_shutdown = ingenic_ost_cevt_set_state_shutdown;
 352        ost->cevt.set_next_event = ingenic_ost_cevt_set_next;
 353
 354        clockevents_config_and_register(&ost->cevt, rate, 4, 0xffffffff);
 355
 356        return 0;
 357
 358err_irq_dispose_mapping:
 359        irq_dispose_mapping(timer_virq);
 360err_clk_disable:
 361        clk_disable_unprepare(ost->percpu_timer_clk);
 362err_clk_put:
 363        clk_put(ost->percpu_timer_clk);
 364        return err;
 365}
 366
 367static int __init ingenic_ost_global_timer_init(struct device_node *np,
 368                                               struct ingenic_ost *ost)
 369{
 370        unsigned int channel = OST_CLK_GLOBAL_TIMER;
 371        struct clocksource *cs = &ost->cs;
 372        unsigned long rate;
 373        int err;
 374
 375        ost->global_timer_clk = ingenic_ost_get_clock(np, channel);
 376        if (IS_ERR(ost->global_timer_clk))
 377                return PTR_ERR(ost->global_timer_clk);
 378
 379        err = clk_prepare_enable(ost->global_timer_clk);
 380        if (err)
 381                goto err_clk_put;
 382
 383        rate = clk_get_rate(ost->global_timer_clk);
 384        if (!rate) {
 385                err = -EINVAL;
 386                goto err_clk_disable;
 387        }
 388
 389        /* Clear counter CNT registers */
 390        writel(OSTCR_OST2CLR, ost->base + OST_REG_OSTCR);
 391
 392        /* Enable OST channel */
 393        writel(OSTESR_OST2ENS, ost->base + OST_REG_OSTESR);
 394
 395        cs->name = "ingenic-ost";
 396        cs->rating = 400;
 397        cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
 398        cs->mask = CLOCKSOURCE_MASK(32);
 399        cs->read = ingenic_ost_clocksource_read;
 400
 401        err = clocksource_register_hz(cs, rate);
 402        if (err)
 403                goto err_clk_disable;
 404
 405        return 0;
 406
 407err_clk_disable:
 408        clk_disable_unprepare(ost->global_timer_clk);
 409err_clk_put:
 410        clk_put(ost->global_timer_clk);
 411        return err;
 412}
 413
 414static const struct ingenic_soc_info x1000_soc_info = {
 415        .num_channels = 2,
 416};
 417
 418static const struct of_device_id __maybe_unused ingenic_ost_of_matches[] __initconst = {
 419        { .compatible = "ingenic,x1000-ost", .data = &x1000_soc_info },
 420        { /* sentinel */ }
 421};
 422
 423static int __init ingenic_ost_probe(struct device_node *np)
 424{
 425        const struct of_device_id *id = of_match_node(ingenic_ost_of_matches, np);
 426        struct ingenic_ost *ost;
 427        unsigned int i;
 428        int ret;
 429
 430        ost = kzalloc(sizeof(*ost), GFP_KERNEL);
 431        if (!ost)
 432                return -ENOMEM;
 433
 434        ost->base = of_io_request_and_map(np, 0, of_node_full_name(np));
 435        if (IS_ERR(ost->base)) {
 436                pr_err("%s: Failed to map OST registers\n", __func__);
 437                ret = PTR_ERR(ost->base);
 438                goto err_free_ost;
 439        }
 440
 441        ost->clk = of_clk_get_by_name(np, "ost");
 442        if (IS_ERR(ost->clk)) {
 443                ret = PTR_ERR(ost->clk);
 444                pr_crit("%s: Cannot get OST clock\n", __func__);
 445                goto err_free_ost;
 446        }
 447
 448        ret = clk_prepare_enable(ost->clk);
 449        if (ret) {
 450                pr_crit("%s: Unable to enable OST clock\n", __func__);
 451                goto err_put_clk;
 452        }
 453
 454        ost->soc_info = id->data;
 455
 456        ost->clocks = kzalloc(struct_size(ost->clocks, hws, ost->soc_info->num_channels),
 457                              GFP_KERNEL);
 458        if (!ost->clocks) {
 459                ret = -ENOMEM;
 460                goto err_clk_disable;
 461        }
 462
 463        ost->clocks->num = ost->soc_info->num_channels;
 464
 465        for (i = 0; i < ost->clocks->num; i++) {
 466                ret = ingenic_ost_register_clock(ost, i, &x1000_ost_clk_info[i], ost->clocks);
 467                if (ret) {
 468                        pr_crit("%s: Cannot register clock %d\n", __func__, i);
 469                        goto err_unregister_ost_clocks;
 470                }
 471        }
 472
 473        ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, ost->clocks);
 474        if (ret) {
 475                pr_crit("%s: Cannot add OF clock provider\n", __func__);
 476                goto err_unregister_ost_clocks;
 477        }
 478
 479        ingenic_ost = ost;
 480
 481        return 0;
 482
 483err_unregister_ost_clocks:
 484        for (i = 0; i < ost->clocks->num; i++)
 485                if (ost->clocks->hws[i])
 486                        clk_hw_unregister(ost->clocks->hws[i]);
 487        kfree(ost->clocks);
 488err_clk_disable:
 489        clk_disable_unprepare(ost->clk);
 490err_put_clk:
 491        clk_put(ost->clk);
 492err_free_ost:
 493        kfree(ost);
 494        return ret;
 495}
 496
 497static int __init ingenic_ost_init(struct device_node *np)
 498{
 499        struct ingenic_ost *ost;
 500        unsigned long rate;
 501        int ret;
 502
 503        ret = ingenic_ost_probe(np);
 504        if (ret) {
 505                pr_crit("%s: Failed to initialize OST clocks: %d\n", __func__, ret);
 506                return ret;
 507        }
 508
 509        of_node_clear_flag(np, OF_POPULATED);
 510
 511        ost = ingenic_ost;
 512        if (IS_ERR(ost))
 513                return PTR_ERR(ost);
 514
 515        ret = ingenic_ost_global_timer_init(np, ost);
 516        if (ret) {
 517                pr_crit("%s: Unable to init global timer: %x\n", __func__, ret);
 518                goto err_free_ingenic_ost;
 519        }
 520
 521        ret = ingenic_ost_percpu_timer_init(np, ost);
 522        if (ret)
 523                goto err_ost_global_timer_cleanup;
 524
 525        /* Register the sched_clock at the end as there's no way to undo it */
 526        rate = clk_get_rate(ost->global_timer_clk);
 527        sched_clock_register(ingenic_ost_global_timer_read_cntl, 32, rate);
 528
 529        return 0;
 530
 531err_ost_global_timer_cleanup:
 532        clocksource_unregister(&ost->cs);
 533        clk_disable_unprepare(ost->global_timer_clk);
 534        clk_put(ost->global_timer_clk);
 535err_free_ingenic_ost:
 536        kfree(ost);
 537        return ret;
 538}
 539
 540TIMER_OF_DECLARE(x1000_ost,  "ingenic,x1000-ost",  ingenic_ost_init);
 541