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