uboot/drivers/timer/timer-uclass.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw>
   4 */
   5
   6#include <common.h>
   7#include <clk.h>
   8#include <cpu.h>
   9#include <dm.h>
  10#include <asm/global_data.h>
  11#include <dm/lists.h>
  12#include <dm/device_compat.h>
  13#include <dm/device-internal.h>
  14#include <dm/root.h>
  15#include <errno.h>
  16#include <init.h>
  17#include <timer.h>
  18#include <linux/err.h>
  19
  20DECLARE_GLOBAL_DATA_PTR;
  21
  22/*
  23 * Implement a timer uclass to work with lib/time.c. The timer is usually
  24 * a 32/64 bits free-running up counter. The get_rate() method is used to get
  25 * the input clock frequency of the timer. The get_count() method is used
  26 * to get the current 64 bits count value. If the hardware is counting down,
  27 * the value should be inversed inside the method. There may be no real
  28 * tick, and no timer interrupt.
  29 */
  30
  31int notrace timer_get_count(struct udevice *dev, u64 *count)
  32{
  33        const struct timer_ops *ops = device_get_ops(dev);
  34
  35        if (!ops->get_count)
  36                return -ENOSYS;
  37
  38        *count = ops->get_count(dev);
  39        return 0;
  40}
  41
  42unsigned long notrace timer_get_rate(struct udevice *dev)
  43{
  44        struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
  45
  46        return uc_priv->clock_rate;
  47}
  48
  49static int timer_pre_probe(struct udevice *dev)
  50{
  51#if !CONFIG_IS_ENABLED(OF_PLATDATA)
  52        struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
  53        struct clk timer_clk;
  54        int err;
  55        ulong ret;
  56
  57        /* It is possible that a timer device has a null ofnode */
  58        if (!dev_has_ofnode(dev))
  59                return 0;
  60
  61        err = clk_get_by_index(dev, 0, &timer_clk);
  62        if (!err) {
  63                ret = clk_get_rate(&timer_clk);
  64                if (IS_ERR_VALUE(ret))
  65                        return ret;
  66                uc_priv->clock_rate = ret;
  67        } else {
  68                uc_priv->clock_rate =
  69                        dev_read_u32_default(dev, "clock-frequency", 0);
  70        }
  71#endif
  72
  73        return 0;
  74}
  75
  76static int timer_post_probe(struct udevice *dev)
  77{
  78        struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
  79
  80        if (!uc_priv->clock_rate)
  81                return -EINVAL;
  82
  83        return 0;
  84}
  85
  86#if CONFIG_IS_ENABLED(CPU)
  87int timer_timebase_fallback(struct udevice *dev)
  88{
  89        struct udevice *cpu;
  90        struct cpu_plat *cpu_plat;
  91        struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
  92
  93        /* Did we get our clock rate from the device tree? */
  94        if (uc_priv->clock_rate)
  95                return 0;
  96
  97        /* Fall back to timebase-frequency */
  98        dev_dbg(dev, "missing clocks or clock-frequency property; falling back on timebase-frequency\n");
  99        cpu = cpu_get_current_dev();
 100        if (!cpu)
 101                return -ENODEV;
 102
 103        cpu_plat = dev_get_parent_plat(cpu);
 104        if (!cpu_plat)
 105                return -ENODEV;
 106
 107        uc_priv->clock_rate = cpu_plat->timebase_freq;
 108        return 0;
 109}
 110#endif
 111
 112u64 timer_conv_64(u32 count)
 113{
 114        /* increment tbh if tbl has rolled over */
 115        if (count < gd->timebase_l)
 116                gd->timebase_h++;
 117        gd->timebase_l = count;
 118        return ((u64)gd->timebase_h << 32) | gd->timebase_l;
 119}
 120
 121int notrace dm_timer_init(void)
 122{
 123        struct udevice *dev = NULL;
 124        __maybe_unused ofnode node;
 125        int ret;
 126
 127        if (gd->timer)
 128                return 0;
 129
 130        /*
 131         * Directly access gd->dm_root to suppress error messages, if the
 132         * virtual root driver does not yet exist.
 133         */
 134        if (gd->dm_root == NULL)
 135                return -EAGAIN;
 136
 137#if !CONFIG_IS_ENABLED(OF_PLATDATA)
 138        /* Check for a chosen timer to be used for tick */
 139        node = ofnode_get_chosen_node("tick-timer");
 140
 141        if (ofnode_valid(node) &&
 142            uclass_get_device_by_ofnode(UCLASS_TIMER, node, &dev)) {
 143                /*
 144                 * If the timer is not marked to be bound before
 145                 * relocation, bind it anyway.
 146                 */
 147                if (!lists_bind_fdt(dm_root(), node, &dev, false)) {
 148                        ret = device_probe(dev);
 149                        if (ret)
 150                                return ret;
 151                }
 152        }
 153#endif
 154
 155        if (!dev) {
 156                /* Fall back to the first available timer */
 157                ret = uclass_first_device_err(UCLASS_TIMER, &dev);
 158                if (ret)
 159                        return ret;
 160        }
 161
 162        if (dev) {
 163                gd->timer = dev;
 164                return 0;
 165        }
 166
 167        return -ENODEV;
 168}
 169
 170UCLASS_DRIVER(timer) = {
 171        .id             = UCLASS_TIMER,
 172        .name           = "timer",
 173        .pre_probe      = timer_pre_probe,
 174        .flags          = DM_UC_FLAG_SEQ_ALIAS,
 175        .post_probe     = timer_post_probe,
 176        .per_device_auto        = sizeof(struct timer_dev_priv),
 177};
 178