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 DEFINE_PER_CPU(struct clock_event_device, mn10300_clockevent_device); 45static DEFINE_PER_CPU(struct irqaction, timer_irq); 46 47static irqreturn_t timer_interrupt(int irq, void *dev_id) 48{ 49 struct clock_event_device *cd; 50 unsigned int cpu = smp_processor_id(); 51 52 if (cpu == 0) 53 stop_jiffies_counter(); 54 else 55 stop_jiffies_counter1(); 56 57 cd = &per_cpu(mn10300_clockevent_device, cpu); 58 cd->event_handler(cd); 59 60 return IRQ_HANDLED; 61} 62 63static void event_handler(struct clock_event_device *dev) 64{ 65} 66 67static inline void setup_jiffies_interrupt(int irq, 68 struct irqaction *action) 69{ 70 u16 tmp; 71 setup_irq(irq, action); 72 set_intr_level(irq, NUM2GxICR_LEVEL(CONFIG_TIMER_IRQ_LEVEL)); 73 GxICR(irq) |= GxICR_ENABLE | GxICR_DETECT | GxICR_REQUEST; 74 tmp = GxICR(irq); 75} 76 77int __init init_clockevents(void) 78{ 79 struct clock_event_device *cd; 80 struct irqaction *iact; 81 unsigned int cpu = smp_processor_id(); 82 83 cd = &per_cpu(mn10300_clockevent_device, cpu); 84 85 if (cpu == 0) { 86 stop_jiffies_counter(); 87 cd->irq = TMJCIRQ; 88 } else { 89 stop_jiffies_counter1(); 90 cd->irq = TMJC1IRQ; 91 } 92 93 cd->name = "Timestamp"; 94 cd->features = CLOCK_EVT_FEAT_ONESHOT; 95 96 /* Calculate shift/mult. We want to spawn at least 1 second */ 97 clockevents_calc_mult_shift(cd, MN10300_JCCLK, 1); 98 99 /* Calculate the min / max delta */ 100 cd->max_delta_ns = clockevent_delta2ns(TMJCBR_MAX, cd); 101 cd->min_delta_ns = clockevent_delta2ns(100, cd); 102 103 cd->rating = 200; 104 cd->cpumask = cpumask_of(smp_processor_id()); 105 cd->event_handler = event_handler; 106 cd->set_next_event = next_event; 107 108 iact = &per_cpu(timer_irq, cpu); 109 iact->flags = IRQF_SHARED | IRQF_TIMER; 110 iact->handler = timer_interrupt; 111 112 clockevents_register_device(cd); 113 114#if defined(CONFIG_SMP) && !defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) 115 /* setup timer irq affinity so it only runs on this cpu */ 116 { 117 struct irq_data *data; 118 data = irq_get_irq_data(cd->irq); 119 cpumask_copy(irq_data_get_affinity_mask(data), cpumask_of(cpu)); 120 iact->flags |= IRQF_NOBALANCING; 121 } 122#endif 123 124 if (cpu == 0) { 125 reload_jiffies_counter(MN10300_JC_PER_HZ - 1); 126 iact->name = "CPU0 Timer"; 127 } else { 128 reload_jiffies_counter1(MN10300_JC_PER_HZ - 1); 129 iact->name = "CPU1 Timer"; 130 } 131 132 setup_jiffies_interrupt(cd->irq, iact); 133 134 return 0; 135} 136