root/drivers/clocksource/timer-mp-csky.c

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

DEFINITIONS

This source file includes following definitions.
  1. csky_mptimer_set_next_event
  2. csky_mptimer_shutdown
  3. csky_mptimer_oneshot
  4. csky_mptimer_oneshot_stopped
  5. csky_timer_interrupt
  6. csky_mptimer_starting_cpu
  7. csky_mptimer_dying_cpu
  8. sched_clock_read
  9. clksrc_read
  10. csky_mptimer_init

   1 // SPDX-License-Identifier: GPL-2.0
   2 // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
   3 
   4 #include <linux/init.h>
   5 #include <linux/interrupt.h>
   6 #include <linux/sched_clock.h>
   7 #include <linux/cpu.h>
   8 #include <linux/of_irq.h>
   9 #include <asm/reg_ops.h>
  10 
  11 #include "timer-of.h"
  12 
  13 #define PTIM_CCVR       "cr<3, 14>"
  14 #define PTIM_CTLR       "cr<0, 14>"
  15 #define PTIM_LVR        "cr<6, 14>"
  16 #define PTIM_TSR        "cr<1, 14>"
  17 
  18 static int csky_mptimer_irq;
  19 
  20 static int csky_mptimer_set_next_event(unsigned long delta,
  21                                        struct clock_event_device *ce)
  22 {
  23         mtcr(PTIM_LVR, delta);
  24 
  25         return 0;
  26 }
  27 
  28 static int csky_mptimer_shutdown(struct clock_event_device *ce)
  29 {
  30         mtcr(PTIM_CTLR, 0);
  31 
  32         return 0;
  33 }
  34 
  35 static int csky_mptimer_oneshot(struct clock_event_device *ce)
  36 {
  37         mtcr(PTIM_CTLR, 1);
  38 
  39         return 0;
  40 }
  41 
  42 static int csky_mptimer_oneshot_stopped(struct clock_event_device *ce)
  43 {
  44         mtcr(PTIM_CTLR, 0);
  45 
  46         return 0;
  47 }
  48 
  49 static DEFINE_PER_CPU(struct timer_of, csky_to) = {
  50         .flags                                  = TIMER_OF_CLOCK,
  51         .clkevt = {
  52                 .rating                         = 300,
  53                 .features                       = CLOCK_EVT_FEAT_PERCPU |
  54                                                   CLOCK_EVT_FEAT_ONESHOT,
  55                 .set_state_shutdown             = csky_mptimer_shutdown,
  56                 .set_state_oneshot              = csky_mptimer_oneshot,
  57                 .set_state_oneshot_stopped      = csky_mptimer_oneshot_stopped,
  58                 .set_next_event                 = csky_mptimer_set_next_event,
  59         },
  60 };
  61 
  62 static irqreturn_t csky_timer_interrupt(int irq, void *dev)
  63 {
  64         struct timer_of *to = this_cpu_ptr(&csky_to);
  65 
  66         mtcr(PTIM_TSR, 0);
  67 
  68         to->clkevt.event_handler(&to->clkevt);
  69 
  70         return IRQ_HANDLED;
  71 }
  72 
  73 /*
  74  * clock event for percpu
  75  */
  76 static int csky_mptimer_starting_cpu(unsigned int cpu)
  77 {
  78         struct timer_of *to = per_cpu_ptr(&csky_to, cpu);
  79 
  80         to->clkevt.cpumask = cpumask_of(cpu);
  81 
  82         enable_percpu_irq(csky_mptimer_irq, 0);
  83 
  84         clockevents_config_and_register(&to->clkevt, timer_of_rate(to),
  85                                         2, ULONG_MAX);
  86 
  87         return 0;
  88 }
  89 
  90 static int csky_mptimer_dying_cpu(unsigned int cpu)
  91 {
  92         disable_percpu_irq(csky_mptimer_irq);
  93 
  94         return 0;
  95 }
  96 
  97 /*
  98  * clock source
  99  */
 100 static u64 notrace sched_clock_read(void)
 101 {
 102         return (u64)mfcr(PTIM_CCVR);
 103 }
 104 
 105 static u64 clksrc_read(struct clocksource *c)
 106 {
 107         return (u64)mfcr(PTIM_CCVR);
 108 }
 109 
 110 struct clocksource csky_clocksource = {
 111         .name   = "csky",
 112         .rating = 400,
 113         .mask   = CLOCKSOURCE_MASK(32),
 114         .flags  = CLOCK_SOURCE_IS_CONTINUOUS,
 115         .read   = clksrc_read,
 116 };
 117 
 118 static int __init csky_mptimer_init(struct device_node *np)
 119 {
 120         int ret, cpu, cpu_rollback;
 121         struct timer_of *to = NULL;
 122 
 123         /*
 124          * Csky_mptimer is designed for C-SKY SMP multi-processors and
 125          * every core has it's own private irq and regs for clkevt and
 126          * clksrc.
 127          *
 128          * The regs is accessed by cpu instruction: mfcr/mtcr instead of
 129          * mmio map style. So we needn't mmio-address in dts, but we still
 130          * need to give clk and irq number.
 131          *
 132          * We use private irq for the mptimer and irq number is the same
 133          * for every core. So we use request_percpu_irq() in timer_of_init.
 134          */
 135         csky_mptimer_irq = irq_of_parse_and_map(np, 0);
 136         if (csky_mptimer_irq <= 0)
 137                 return -EINVAL;
 138 
 139         ret = request_percpu_irq(csky_mptimer_irq, csky_timer_interrupt,
 140                                  "csky_mp_timer", &csky_to);
 141         if (ret)
 142                 return -EINVAL;
 143 
 144         for_each_possible_cpu(cpu) {
 145                 to = per_cpu_ptr(&csky_to, cpu);
 146                 ret = timer_of_init(np, to);
 147                 if (ret)
 148                         goto rollback;
 149         }
 150 
 151         clocksource_register_hz(&csky_clocksource, timer_of_rate(to));
 152         sched_clock_register(sched_clock_read, 32, timer_of_rate(to));
 153 
 154         ret = cpuhp_setup_state(CPUHP_AP_CSKY_TIMER_STARTING,
 155                                 "clockevents/csky/timer:starting",
 156                                 csky_mptimer_starting_cpu,
 157                                 csky_mptimer_dying_cpu);
 158         if (ret)
 159                 return -EINVAL;
 160 
 161         return 0;
 162 
 163 rollback:
 164         for_each_possible_cpu(cpu_rollback) {
 165                 if (cpu_rollback == cpu)
 166                         break;
 167 
 168                 to = per_cpu_ptr(&csky_to, cpu_rollback);
 169                 timer_of_cleanup(to);
 170         }
 171         return -EINVAL;
 172 }
 173 TIMER_OF_DECLARE(csky_mptimer, "csky,mptimer", csky_mptimer_init);

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