1/* MN10300 clockevents 2 * 3 * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved. 4 * Written by Mark Salter (msalter@redhat.com) 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public Licence 8 * as published by the Free Software Foundation; either version 9 * 2 of the Licence, or (at your option) any later version. 10 */ 11#include <linux/clockchips.h> 12#include <linux/interrupt.h> 13#include <linux/percpu.h> 14#include <linux/smp.h> 15#include <asm/timex.h> 16#include "internal.h" 17 18#ifdef CONFIG_SMP 19#if (CONFIG_NR_CPUS > 2) && !defined(CONFIG_GEENERIC_CLOCKEVENTS_BROADCAST) 20#error "This doesn't scale well! Need per-core local timers." 21#endif 22#else /* CONFIG_SMP */ 23#define stop_jiffies_counter1() 24#define reload_jiffies_counter1(x) 25#define TMJC1IRQ TMJCIRQ 26#endif 27 28 29static int next_event(unsigned long delta, 30 struct clock_event_device *evt) 31{ 32 unsigned int cpu = smp_processor_id(); 33 34 if (cpu == 0) { 35 stop_jiffies_counter(); 36 reload_jiffies_counter(delta - 1); 37 } else { 38 stop_jiffies_counter1(); 39 reload_jiffies_counter1(delta - 1); 40 } 41 return 0; 42} 43 44static void set_clock_mode(enum clock_event_mode mode, 45 struct clock_event_device *evt) 46{ 47 /* Nothing to do ... */ 48} 49 50static DEFINE_PER_CPU(struct clock_event_device, mn10300_clockevent_device); 51static DEFINE_PER_CPU(struct irqaction, timer_irq); 52 53static irqreturn_t timer_interrupt(int irq, void *dev_id) 54{ 55 struct clock_event_device *cd; 56 unsigned int cpu = smp_processor_id(); 57 58 if (cpu == 0) 59 stop_jiffies_counter(); 60 else 61 stop_jiffies_counter1(); 62 63 cd = &per_cpu(mn10300_clockevent_device, cpu); 64 cd->event_handler(cd); 65 66 return IRQ_HANDLED; 67} 68 69static void event_handler(struct clock_event_device *dev) 70{ 71} 72 73static inline void setup_jiffies_interrupt(int irq, 74 struct irqaction *action) 75{ 76 u16 tmp; 77 setup_irq(irq, action); 78 set_intr_level(irq, NUM2GxICR_LEVEL(CONFIG_TIMER_IRQ_LEVEL)); 79 GxICR(irq) |= GxICR_ENABLE | GxICR_DETECT | GxICR_REQUEST; 80 tmp = GxICR(irq); 81} 82 83int __init init_clockevents(void) 84{ 85 struct clock_event_device *cd; 86 struct irqaction *iact; 87 unsigned int cpu = smp_processor_id(); 88 89 cd = &per_cpu(mn10300_clockevent_device, cpu); 90 91 if (cpu == 0) { 92 stop_jiffies_counter(); 93 cd->irq = TMJCIRQ; 94 } else { 95 stop_jiffies_counter1(); 96 cd->irq = TMJC1IRQ; 97 } 98 99 cd->name = "Timestamp"; 100 cd->features = CLOCK_EVT_FEAT_ONESHOT; 101 102 /* Calculate shift/mult. We want to spawn at least 1 second */ 103 clockevents_calc_mult_shift(cd, MN10300_JCCLK, 1); 104 105 /* Calculate the min / max delta */ 106 cd->max_delta_ns = clockevent_delta2ns(TMJCBR_MAX, cd); 107 cd->min_delta_ns = clockevent_delta2ns(100, cd); 108 109 cd->rating = 200; 110 cd->cpumask = cpumask_of(smp_processor_id()); 111 cd->set_mode = set_clock_mode; 112 cd->event_handler = event_handler; 113 cd->set_next_event = next_event; 114 115 iact = &per_cpu(timer_irq, cpu); 116 iact->flags = IRQF_SHARED | IRQF_TIMER; 117 iact->handler = timer_interrupt; 118 119 clockevents_register_device(cd); 120 121#if defined(CONFIG_SMP) && !defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) 122 /* setup timer irq affinity so it only runs on this cpu */ 123 { 124 struct irq_data *data; 125 data = irq_get_irq_data(cd->irq); 126 cpumask_copy(data->affinity, cpumask_of(cpu)); 127 iact->flags |= IRQF_NOBALANCING; 128 } 129#endif 130 131 if (cpu == 0) { 132 reload_jiffies_counter(MN10300_JC_PER_HZ - 1); 133 iact->name = "CPU0 Timer"; 134 } else { 135 reload_jiffies_counter1(MN10300_JC_PER_HZ - 1); 136 iact->name = "CPU1 Timer"; 137 } 138 139 setup_jiffies_interrupt(cd->irq, iact); 140 141 return 0; 142} 143