uboot/drivers/timer/sp804_timer.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * ARM PrimeCell Dual-Timer Module (SP804) driver
   4 * Copyright (C) 2022 Arm Ltd.
   5 */
   6
   7#include <common.h>
   8#include <clk.h>
   9#include <dm.h>
  10#include <init.h>
  11#include <log.h>
  12#include <asm/global_data.h>
  13#include <dm/ofnode.h>
  14#include <mapmem.h>
  15#include <dt-structs.h>
  16#include <timer.h>
  17#include <asm/io.h>
  18
  19DECLARE_GLOBAL_DATA_PTR;
  20
  21#define SP804_TIMERX_LOAD               0x00
  22#define SP804_TIMERX_VALUE              0x04
  23#define SP804_TIMERX_CONTROL            0x08
  24
  25#define SP804_CTRL_TIMER_ENABLE         (1U << 7)
  26#define SP804_CTRL_TIMER_PERIODIC       (1U << 6)
  27#define SP804_CTRL_INT_ENABLE           (1U << 5)
  28#define SP804_CTRL_TIMER_PRESCALE_SHIFT 2
  29#define SP804_CTRL_TIMER_PRESCALE_MASK  (3U << SP804_CTRL_TIMER_PRESCALE_SHIFT)
  30#define SP804_CTRL_TIMER_32BIT          (1U << 1)
  31#define SP804_CTRL_ONESHOT              (1U << 0)
  32
  33
  34struct sp804_timer_plat {
  35        uintptr_t base;
  36};
  37
  38static u64 sp804_timer_get_count(struct udevice *dev)
  39{
  40        struct sp804_timer_plat *plat = dev_get_plat(dev);
  41        uint32_t cntr = readl(plat->base + SP804_TIMERX_VALUE);
  42
  43        /* timers are down-counting */
  44        return ~0u - cntr;
  45}
  46
  47static int sp804_clk_of_to_plat(struct udevice *dev)
  48{
  49        struct sp804_timer_plat *plat = dev_get_plat(dev);
  50
  51        plat->base = dev_read_addr(dev);
  52        if (!plat->base)
  53                return -ENOENT;
  54
  55        return 0;
  56}
  57
  58static int sp804_timer_probe(struct udevice *dev)
  59{
  60        struct sp804_timer_plat *plat = dev_get_plat(dev);
  61        struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
  62        struct clk base_clk;
  63        unsigned int divider = 1;
  64        uint32_t ctlr;
  65        int ret;
  66
  67        ctlr = readl(plat->base + SP804_TIMERX_CONTROL);
  68        ctlr &= SP804_CTRL_TIMER_PRESCALE_MASK;
  69        switch (ctlr >> SP804_CTRL_TIMER_PRESCALE_SHIFT) {
  70        case 0x0: divider = 1; break;
  71        case 0x1: divider = 16; break;
  72        case 0x2: divider = 256; break;
  73        case 0x3: printf("illegal!\n"); break;
  74        }
  75
  76        ret = clk_get_by_index(dev, 0, &base_clk);
  77        if (ret) {
  78                printf("could not find SP804 timer base clock in DT\n");
  79                return ret;
  80        }
  81
  82        uc_priv->clock_rate = clk_get_rate(&base_clk) / divider;
  83
  84        /* keep divider, free-running, wrapping, no IRQs, 32-bit mode */
  85        ctlr |= SP804_CTRL_TIMER_32BIT | SP804_CTRL_TIMER_ENABLE;
  86        writel(ctlr, plat->base + SP804_TIMERX_CONTROL);
  87
  88        return 0;
  89}
  90
  91static const struct timer_ops sp804_timer_ops = {
  92        .get_count = sp804_timer_get_count,
  93};
  94
  95static const struct udevice_id sp804_timer_ids[] = {
  96        { .compatible = "arm,sp804" },
  97        {}
  98};
  99
 100U_BOOT_DRIVER(arm_sp804_timer) = {
 101        .name           = "arm_sp804_timer",
 102        .id             = UCLASS_TIMER,
 103        .of_match       = sp804_timer_ids,
 104        .probe          = sp804_timer_probe,
 105        .ops            = &sp804_timer_ops,
 106        .plat_auto      = sizeof(struct sp804_timer_plat),
 107        .of_to_plat     = sp804_clk_of_to_plat,
 108};
 109