1fa5635a2SAndrew Bresticker /* 2fa5635a2SAndrew Bresticker * This file is subject to the terms and conditions of the GNU General Public 3fa5635a2SAndrew Bresticker * License. See the file "COPYING" in the main directory of this archive 4fa5635a2SAndrew Bresticker * for more details. 5fa5635a2SAndrew Bresticker * 6fa5635a2SAndrew Bresticker * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. 7fa5635a2SAndrew Bresticker */ 8*a331ce63SAndrew Bresticker #include <linux/clockchips.h> 9fa5635a2SAndrew Bresticker #include <linux/init.h> 10*a331ce63SAndrew Bresticker #include <linux/interrupt.h> 11fa5635a2SAndrew Bresticker #include <linux/irqchip/mips-gic.h> 12*a331ce63SAndrew Bresticker #include <linux/percpu.h> 13*a331ce63SAndrew Bresticker #include <linux/smp.h> 14fa5635a2SAndrew Bresticker #include <linux/time.h> 15fa5635a2SAndrew Bresticker 16*a331ce63SAndrew Bresticker #include <asm/time.h> 17*a331ce63SAndrew Bresticker 18*a331ce63SAndrew Bresticker DEFINE_PER_CPU(struct clock_event_device, gic_clockevent_device); 19*a331ce63SAndrew Bresticker int gic_timer_irq_installed; 20*a331ce63SAndrew Bresticker 21*a331ce63SAndrew Bresticker static int gic_next_event(unsigned long delta, struct clock_event_device *evt) 22*a331ce63SAndrew Bresticker { 23*a331ce63SAndrew Bresticker u64 cnt; 24*a331ce63SAndrew Bresticker int res; 25*a331ce63SAndrew Bresticker 26*a331ce63SAndrew Bresticker cnt = gic_read_count(); 27*a331ce63SAndrew Bresticker cnt += (u64)delta; 28*a331ce63SAndrew Bresticker gic_write_cpu_compare(cnt, cpumask_first(evt->cpumask)); 29*a331ce63SAndrew Bresticker res = ((int)(gic_read_count() - cnt) >= 0) ? -ETIME : 0; 30*a331ce63SAndrew Bresticker return res; 31*a331ce63SAndrew Bresticker } 32*a331ce63SAndrew Bresticker 33*a331ce63SAndrew Bresticker void gic_set_clock_mode(enum clock_event_mode mode, 34*a331ce63SAndrew Bresticker struct clock_event_device *evt) 35*a331ce63SAndrew Bresticker { 36*a331ce63SAndrew Bresticker /* Nothing to do ... */ 37*a331ce63SAndrew Bresticker } 38*a331ce63SAndrew Bresticker 39*a331ce63SAndrew Bresticker irqreturn_t gic_compare_interrupt(int irq, void *dev_id) 40*a331ce63SAndrew Bresticker { 41*a331ce63SAndrew Bresticker struct clock_event_device *cd; 42*a331ce63SAndrew Bresticker int cpu = smp_processor_id(); 43*a331ce63SAndrew Bresticker 44*a331ce63SAndrew Bresticker gic_write_compare(gic_read_compare()); 45*a331ce63SAndrew Bresticker cd = &per_cpu(gic_clockevent_device, cpu); 46*a331ce63SAndrew Bresticker cd->event_handler(cd); 47*a331ce63SAndrew Bresticker return IRQ_HANDLED; 48*a331ce63SAndrew Bresticker } 49*a331ce63SAndrew Bresticker 50*a331ce63SAndrew Bresticker struct irqaction gic_compare_irqaction = { 51*a331ce63SAndrew Bresticker .handler = gic_compare_interrupt, 52*a331ce63SAndrew Bresticker .flags = IRQF_PERCPU | IRQF_TIMER, 53*a331ce63SAndrew Bresticker .name = "timer", 54*a331ce63SAndrew Bresticker }; 55*a331ce63SAndrew Bresticker 56*a331ce63SAndrew Bresticker void gic_event_handler(struct clock_event_device *dev) 57*a331ce63SAndrew Bresticker { 58*a331ce63SAndrew Bresticker } 59*a331ce63SAndrew Bresticker 60*a331ce63SAndrew Bresticker int gic_clockevent_init(void) 61*a331ce63SAndrew Bresticker { 62*a331ce63SAndrew Bresticker unsigned int cpu = smp_processor_id(); 63*a331ce63SAndrew Bresticker struct clock_event_device *cd; 64*a331ce63SAndrew Bresticker unsigned int irq; 65*a331ce63SAndrew Bresticker 66*a331ce63SAndrew Bresticker if (!cpu_has_counter || !gic_frequency) 67*a331ce63SAndrew Bresticker return -ENXIO; 68*a331ce63SAndrew Bresticker 69*a331ce63SAndrew Bresticker irq = MIPS_GIC_IRQ_BASE + GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_COMPARE); 70*a331ce63SAndrew Bresticker 71*a331ce63SAndrew Bresticker cd = &per_cpu(gic_clockevent_device, cpu); 72*a331ce63SAndrew Bresticker 73*a331ce63SAndrew Bresticker cd->name = "MIPS GIC"; 74*a331ce63SAndrew Bresticker cd->features = CLOCK_EVT_FEAT_ONESHOT | 75*a331ce63SAndrew Bresticker CLOCK_EVT_FEAT_C3STOP; 76*a331ce63SAndrew Bresticker 77*a331ce63SAndrew Bresticker clockevent_set_clock(cd, gic_frequency); 78*a331ce63SAndrew Bresticker 79*a331ce63SAndrew Bresticker /* Calculate the min / max delta */ 80*a331ce63SAndrew Bresticker cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd); 81*a331ce63SAndrew Bresticker cd->min_delta_ns = clockevent_delta2ns(0x300, cd); 82*a331ce63SAndrew Bresticker 83*a331ce63SAndrew Bresticker cd->rating = 300; 84*a331ce63SAndrew Bresticker cd->irq = irq; 85*a331ce63SAndrew Bresticker cd->cpumask = cpumask_of(cpu); 86*a331ce63SAndrew Bresticker cd->set_next_event = gic_next_event; 87*a331ce63SAndrew Bresticker cd->set_mode = gic_set_clock_mode; 88*a331ce63SAndrew Bresticker cd->event_handler = gic_event_handler; 89*a331ce63SAndrew Bresticker 90*a331ce63SAndrew Bresticker clockevents_register_device(cd); 91*a331ce63SAndrew Bresticker 92*a331ce63SAndrew Bresticker if (!gic_timer_irq_installed) { 93*a331ce63SAndrew Bresticker setup_percpu_irq(irq, &gic_compare_irqaction); 94*a331ce63SAndrew Bresticker gic_timer_irq_installed = 1; 95*a331ce63SAndrew Bresticker } 96*a331ce63SAndrew Bresticker 97*a331ce63SAndrew Bresticker enable_percpu_irq(irq, IRQ_TYPE_NONE); 98*a331ce63SAndrew Bresticker 99*a331ce63SAndrew Bresticker return 0; 100*a331ce63SAndrew Bresticker } 101*a331ce63SAndrew Bresticker 102fa5635a2SAndrew Bresticker static cycle_t gic_hpt_read(struct clocksource *cs) 103fa5635a2SAndrew Bresticker { 104fa5635a2SAndrew Bresticker return gic_read_count(); 105fa5635a2SAndrew Bresticker } 106fa5635a2SAndrew Bresticker 107fa5635a2SAndrew Bresticker static struct clocksource gic_clocksource = { 108fa5635a2SAndrew Bresticker .name = "GIC", 109fa5635a2SAndrew Bresticker .read = gic_hpt_read, 110fa5635a2SAndrew Bresticker .flags = CLOCK_SOURCE_IS_CONTINUOUS, 111fa5635a2SAndrew Bresticker }; 112fa5635a2SAndrew Bresticker 113fa5635a2SAndrew Bresticker void __init gic_clocksource_init(unsigned int frequency) 114fa5635a2SAndrew Bresticker { 115fa5635a2SAndrew Bresticker /* Set clocksource mask. */ 116fa5635a2SAndrew Bresticker gic_clocksource.mask = CLOCKSOURCE_MASK(gic_get_count_width()); 117fa5635a2SAndrew Bresticker 118fa5635a2SAndrew Bresticker /* Calculate a somewhat reasonable rating value. */ 119fa5635a2SAndrew Bresticker gic_clocksource.rating = 200 + frequency / 10000000; 120fa5635a2SAndrew Bresticker 121fa5635a2SAndrew Bresticker clocksource_register_hz(&gic_clocksource, frequency); 122fa5635a2SAndrew Bresticker } 123