linux/drivers/clocksource/timer-ti-32k.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/**
   3 * timer-ti-32k.c - OMAP2 32k Timer Support
   4 *
   5 * Copyright (C) 2009 Nokia Corporation
   6 *
   7 * Update to use new clocksource/clockevent layers
   8 * Author: Kevin Hilman, MontaVista Software, Inc. <source@mvista.com>
   9 * Copyright (C) 2007 MontaVista Software, Inc.
  10 *
  11 * Original driver:
  12 * Copyright (C) 2005 Nokia Corporation
  13 * Author: Paul Mundt <paul.mundt@nokia.com>
  14 *         Juha Yrj\xC3\xB6l\xC3\xA4 <juha.yrjola@nokia.com>
  15 * OMAP Dual-mode timer framework support by Timo Teras
  16 *
  17 * Some parts based off of TI's 24xx code:
  18 *
  19 * Copyright (C) 2004-2009 Texas Instruments, Inc.
  20 *
  21 * Roughly modelled after the OMAP1 MPU timer code.
  22 * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
  23 *
  24 * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com
  25 */
  26
  27#include <linux/clk.h>
  28#include <linux/init.h>
  29#include <linux/time.h>
  30#include <linux/sched_clock.h>
  31#include <linux/clocksource.h>
  32#include <linux/of.h>
  33#include <linux/of_address.h>
  34
  35/*
  36 * 32KHz clocksource ... always available, on pretty most chips except
  37 * OMAP 730 and 1510.  Other timers could be used as clocksources, with
  38 * higher resolution in free-running counter modes (e.g. 12 MHz xtal),
  39 * but systems won't necessarily want to spend resources that way.
  40 */
  41
  42#define OMAP2_32KSYNCNT_REV_OFF         0x0
  43#define OMAP2_32KSYNCNT_REV_SCHEME      (0x3 << 30)
  44#define OMAP2_32KSYNCNT_CR_OFF_LOW      0x10
  45#define OMAP2_32KSYNCNT_CR_OFF_HIGH     0x30
  46
  47struct ti_32k {
  48        void __iomem            *base;
  49        void __iomem            *counter;
  50        struct clocksource      cs;
  51};
  52
  53static inline struct ti_32k *to_ti_32k(struct clocksource *cs)
  54{
  55        return container_of(cs, struct ti_32k, cs);
  56}
  57
  58static u64 notrace ti_32k_read_cycles(struct clocksource *cs)
  59{
  60        struct ti_32k *ti = to_ti_32k(cs);
  61
  62        return (u64)readl_relaxed(ti->counter);
  63}
  64
  65static struct ti_32k ti_32k_timer = {
  66        .cs = {
  67                .name           = "32k_counter",
  68                .rating         = 250,
  69                .read           = ti_32k_read_cycles,
  70                .mask           = CLOCKSOURCE_MASK(32),
  71                .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
  72        },
  73};
  74
  75static u64 notrace omap_32k_read_sched_clock(void)
  76{
  77        return ti_32k_read_cycles(&ti_32k_timer.cs);
  78}
  79
  80static void __init ti_32k_timer_enable_clock(struct device_node *np,
  81                                             const char *name)
  82{
  83        struct clk *clock;
  84        int error;
  85
  86        clock = of_clk_get_by_name(np->parent, name);
  87        if (IS_ERR(clock)) {
  88                /* Only some SoCs have a separate interface clock */
  89                if (PTR_ERR(clock) == -EINVAL && !strncmp("ick", name, 3))
  90                        return;
  91
  92                pr_warn("%s: could not get clock %s %li\n",
  93                        __func__, name, PTR_ERR(clock));
  94                return;
  95        }
  96
  97        error = clk_prepare_enable(clock);
  98        if (error) {
  99                pr_warn("%s: could not enable %s: %i\n",
 100                        __func__, name, error);
 101                return;
 102        }
 103}
 104
 105static void __init ti_32k_timer_module_init(struct device_node *np,
 106                                            void __iomem *base)
 107{
 108        void __iomem *sysc = base + 4;
 109
 110        if (!of_device_is_compatible(np->parent, "ti,sysc"))
 111                return;
 112
 113        ti_32k_timer_enable_clock(np, "fck");
 114        ti_32k_timer_enable_clock(np, "ick");
 115
 116        /*
 117         * Force idle module as wkup domain is active with MPU.
 118         * No need to tag the module disabled for ti-sysc probe.
 119         */
 120        writel_relaxed(0, sysc);
 121}
 122
 123static int __init ti_32k_timer_init(struct device_node *np)
 124{
 125        int ret;
 126
 127        ti_32k_timer.base = of_iomap(np, 0);
 128        if (!ti_32k_timer.base) {
 129                pr_err("Can't ioremap 32k timer base\n");
 130                return -ENXIO;
 131        }
 132
 133        if (!of_machine_is_compatible("ti,am43"))
 134                ti_32k_timer.cs.flags |= CLOCK_SOURCE_SUSPEND_NONSTOP;
 135
 136        ti_32k_timer.counter = ti_32k_timer.base;
 137        ti_32k_timer_module_init(np, ti_32k_timer.base);
 138
 139        /*
 140         * 32k sync Counter IP register offsets vary between the highlander
 141         * version and the legacy ones.
 142         *
 143         * The 'SCHEME' bits(30-31) of the revision register is used to identify
 144         * the version.
 145         */
 146        if (readl_relaxed(ti_32k_timer.base + OMAP2_32KSYNCNT_REV_OFF) &
 147                        OMAP2_32KSYNCNT_REV_SCHEME)
 148                ti_32k_timer.counter += OMAP2_32KSYNCNT_CR_OFF_HIGH;
 149        else
 150                ti_32k_timer.counter += OMAP2_32KSYNCNT_CR_OFF_LOW;
 151
 152        pr_info("OMAP clocksource: 32k_counter at 32768 Hz\n");
 153
 154        ret = clocksource_register_hz(&ti_32k_timer.cs, 32768);
 155        if (ret) {
 156                pr_err("32k_counter: can't register clocksource\n");
 157                return ret;
 158        }
 159
 160        sched_clock_register(omap_32k_read_sched_clock, 32, 32768);
 161
 162        return 0;
 163}
 164TIMER_OF_DECLARE(ti_32k_timer, "ti,omap-counter32k",
 165                ti_32k_timer_init);
 166