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