root/drivers/clocksource/timer-integrator-ap.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. integrator_read_sched_clock
  2. integrator_clocksource_init
  3. integrator_timer_interrupt
  4. clkevt_shutdown
  5. clkevt_set_oneshot
  6. clkevt_set_periodic
  7. clkevt_set_next_event
  8. integrator_clockevent_init
  9. integrator_ap_timer_init_of

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Integrator/AP timer driver
   4  * Copyright (C) 2000-2003 Deep Blue Solutions Ltd
   5  * Copyright (c) 2014, Linaro Limited
   6  */
   7 
   8 #include <linux/clk.h>
   9 #include <linux/clocksource.h>
  10 #include <linux/of_irq.h>
  11 #include <linux/of_address.h>
  12 #include <linux/of_platform.h>
  13 #include <linux/clockchips.h>
  14 #include <linux/interrupt.h>
  15 #include <linux/sched_clock.h>
  16 
  17 #include "timer-sp.h"
  18 
  19 static void __iomem * sched_clk_base;
  20 
  21 static u64 notrace integrator_read_sched_clock(void)
  22 {
  23         return -readl(sched_clk_base + TIMER_VALUE);
  24 }
  25 
  26 static int __init integrator_clocksource_init(unsigned long inrate,
  27                                               void __iomem *base)
  28 {
  29         u32 ctrl = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC;
  30         unsigned long rate = inrate;
  31         int ret;
  32 
  33         if (rate >= 1500000) {
  34                 rate /= 16;
  35                 ctrl |= TIMER_CTRL_DIV16;
  36         }
  37 
  38         writel(0xffff, base + TIMER_LOAD);
  39         writel(ctrl, base + TIMER_CTRL);
  40 
  41         ret = clocksource_mmio_init(base + TIMER_VALUE, "timer2",
  42                                     rate, 200, 16, clocksource_mmio_readl_down);
  43         if (ret)
  44                 return ret;
  45 
  46         sched_clk_base = base;
  47         sched_clock_register(integrator_read_sched_clock, 16, rate);
  48 
  49         return 0;
  50 }
  51 
  52 static unsigned long timer_reload;
  53 static void __iomem * clkevt_base;
  54 
  55 /*
  56  * IRQ handler for the timer
  57  */
  58 static irqreturn_t integrator_timer_interrupt(int irq, void *dev_id)
  59 {
  60         struct clock_event_device *evt = dev_id;
  61 
  62         /* clear the interrupt */
  63         writel(1, clkevt_base + TIMER_INTCLR);
  64 
  65         evt->event_handler(evt);
  66 
  67         return IRQ_HANDLED;
  68 }
  69 
  70 static int clkevt_shutdown(struct clock_event_device *evt)
  71 {
  72         u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE;
  73 
  74         /* Disable timer */
  75         writel(ctrl, clkevt_base + TIMER_CTRL);
  76         return 0;
  77 }
  78 
  79 static int clkevt_set_oneshot(struct clock_event_device *evt)
  80 {
  81         u32 ctrl = readl(clkevt_base + TIMER_CTRL) &
  82                    ~(TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC);
  83 
  84         /* Leave the timer disabled, .set_next_event will enable it */
  85         writel(ctrl, clkevt_base + TIMER_CTRL);
  86         return 0;
  87 }
  88 
  89 static int clkevt_set_periodic(struct clock_event_device *evt)
  90 {
  91         u32 ctrl = readl(clkevt_base + TIMER_CTRL) & ~TIMER_CTRL_ENABLE;
  92 
  93         /* Disable timer */
  94         writel(ctrl, clkevt_base + TIMER_CTRL);
  95 
  96         /* Enable the timer and start the periodic tick */
  97         writel(timer_reload, clkevt_base + TIMER_LOAD);
  98         ctrl |= TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE;
  99         writel(ctrl, clkevt_base + TIMER_CTRL);
 100         return 0;
 101 }
 102 
 103 static int clkevt_set_next_event(unsigned long next, struct clock_event_device *evt)
 104 {
 105         unsigned long ctrl = readl(clkevt_base + TIMER_CTRL);
 106 
 107         writel(ctrl & ~TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
 108         writel(next, clkevt_base + TIMER_LOAD);
 109         writel(ctrl | TIMER_CTRL_ENABLE, clkevt_base + TIMER_CTRL);
 110 
 111         return 0;
 112 }
 113 
 114 static struct clock_event_device integrator_clockevent = {
 115         .name                   = "timer1",
 116         .features               = CLOCK_EVT_FEAT_PERIODIC |
 117                                   CLOCK_EVT_FEAT_ONESHOT,
 118         .set_state_shutdown     = clkevt_shutdown,
 119         .set_state_periodic     = clkevt_set_periodic,
 120         .set_state_oneshot      = clkevt_set_oneshot,
 121         .tick_resume            = clkevt_shutdown,
 122         .set_next_event         = clkevt_set_next_event,
 123         .rating                 = 300,
 124 };
 125 
 126 static struct irqaction integrator_timer_irq = {
 127         .name           = "timer",
 128         .flags          = IRQF_TIMER | IRQF_IRQPOLL,
 129         .handler        = integrator_timer_interrupt,
 130         .dev_id         = &integrator_clockevent,
 131 };
 132 
 133 static int integrator_clockevent_init(unsigned long inrate,
 134                                       void __iomem *base, int irq)
 135 {
 136         unsigned long rate = inrate;
 137         unsigned int ctrl = 0;
 138         int ret;
 139 
 140         clkevt_base = base;
 141         /* Calculate and program a divisor */
 142         if (rate > 0x100000 * HZ) {
 143                 rate /= 256;
 144                 ctrl |= TIMER_CTRL_DIV256;
 145         } else if (rate > 0x10000 * HZ) {
 146                 rate /= 16;
 147                 ctrl |= TIMER_CTRL_DIV16;
 148         }
 149         timer_reload = rate / HZ;
 150         writel(ctrl, clkevt_base + TIMER_CTRL);
 151 
 152         ret = setup_irq(irq, &integrator_timer_irq);
 153         if (ret)
 154                 return ret;
 155 
 156         clockevents_config_and_register(&integrator_clockevent,
 157                                         rate,
 158                                         1,
 159                                         0xffffU);
 160         return 0;
 161 }
 162 
 163 static int __init integrator_ap_timer_init_of(struct device_node *node)
 164 {
 165         const char *path;
 166         void __iomem *base;
 167         int err;
 168         int irq;
 169         struct clk *clk;
 170         unsigned long rate;
 171         struct device_node *alias_node;
 172 
 173         base = of_io_request_and_map(node, 0, "integrator-timer");
 174         if (IS_ERR(base))
 175                 return PTR_ERR(base);
 176 
 177         clk = of_clk_get(node, 0);
 178         if (IS_ERR(clk)) {
 179                 pr_err("No clock for %pOFn\n", node);
 180                 return PTR_ERR(clk);
 181         }
 182         clk_prepare_enable(clk);
 183         rate = clk_get_rate(clk);
 184         writel(0, base + TIMER_CTRL);
 185 
 186         err = of_property_read_string(of_aliases,
 187                                 "arm,timer-primary", &path);
 188         if (err) {
 189                 pr_warn("Failed to read property\n");
 190                 return err;
 191         }
 192 
 193         alias_node = of_find_node_by_path(path);
 194 
 195         /*
 196          * The pointer is used as an identifier not as a pointer, we
 197          * can drop the refcount on the of__node immediately after
 198          * getting it.
 199          */
 200         of_node_put(alias_node);
 201 
 202         if (node == alias_node)
 203                 /* The primary timer lacks IRQ, use as clocksource */
 204                 return integrator_clocksource_init(rate, base);
 205 
 206         err = of_property_read_string(of_aliases,
 207                                 "arm,timer-secondary", &path);
 208         if (err) {
 209                 pr_warn("Failed to read property\n");
 210                 return err;
 211         }
 212 
 213         alias_node = of_find_node_by_path(path);
 214 
 215         of_node_put(alias_node);
 216 
 217         if (node == alias_node) {
 218                 /* The secondary timer will drive the clock event */
 219                 irq = irq_of_parse_and_map(node, 0);
 220                 return integrator_clockevent_init(rate, base, irq);
 221         }
 222 
 223         pr_info("Timer @%p unused\n", base);
 224         clk_disable_unprepare(clk);
 225 
 226         return 0;
 227 }
 228 
 229 TIMER_OF_DECLARE(integrator_ap_timer, "arm,integrator-timer",
 230                        integrator_ap_timer_init_of);

/* [<][>][^][v][top][bottom][index][help] */