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> 9e4752dbbSAndrew Bresticker #include <linux/cpu.h> 10fa5635a2SAndrew Bresticker #include <linux/init.h> 11a331ce63SAndrew Bresticker #include <linux/interrupt.h> 12fa5635a2SAndrew Bresticker #include <linux/irqchip/mips-gic.h> 13e4752dbbSAndrew Bresticker #include <linux/notifier.h> 14*e12aa828SAndrew Bresticker #include <linux/of_irq.h> 15a331ce63SAndrew Bresticker #include <linux/percpu.h> 16a331ce63SAndrew Bresticker #include <linux/smp.h> 17fa5635a2SAndrew Bresticker #include <linux/time.h> 18fa5635a2SAndrew Bresticker 195fee56e0SAndrew Bresticker static DEFINE_PER_CPU(struct clock_event_device, gic_clockevent_device); 20e4752dbbSAndrew Bresticker static int gic_timer_irq; 21b0854514SAndrew Bresticker static unsigned int gic_frequency; 22a331ce63SAndrew Bresticker 23a331ce63SAndrew Bresticker static int gic_next_event(unsigned long delta, struct clock_event_device *evt) 24a331ce63SAndrew Bresticker { 25a331ce63SAndrew Bresticker u64 cnt; 26a331ce63SAndrew Bresticker int res; 27a331ce63SAndrew Bresticker 28a331ce63SAndrew Bresticker cnt = gic_read_count(); 29a331ce63SAndrew Bresticker cnt += (u64)delta; 30a331ce63SAndrew Bresticker gic_write_cpu_compare(cnt, cpumask_first(evt->cpumask)); 31a331ce63SAndrew Bresticker res = ((int)(gic_read_count() - cnt) >= 0) ? -ETIME : 0; 32a331ce63SAndrew Bresticker return res; 33a331ce63SAndrew Bresticker } 34a331ce63SAndrew Bresticker 355fee56e0SAndrew Bresticker static void gic_set_clock_mode(enum clock_event_mode mode, 36a331ce63SAndrew Bresticker struct clock_event_device *evt) 37a331ce63SAndrew Bresticker { 38a331ce63SAndrew Bresticker /* Nothing to do ... */ 39a331ce63SAndrew Bresticker } 40a331ce63SAndrew Bresticker 415fee56e0SAndrew Bresticker static irqreturn_t gic_compare_interrupt(int irq, void *dev_id) 42a331ce63SAndrew Bresticker { 43f7ea3060SAndrew Bresticker struct clock_event_device *cd = dev_id; 44a331ce63SAndrew Bresticker 45a331ce63SAndrew Bresticker gic_write_compare(gic_read_compare()); 46a331ce63SAndrew Bresticker cd->event_handler(cd); 47a331ce63SAndrew Bresticker return IRQ_HANDLED; 48a331ce63SAndrew Bresticker } 49a331ce63SAndrew Bresticker 50a331ce63SAndrew Bresticker struct irqaction gic_compare_irqaction = { 51a331ce63SAndrew Bresticker .handler = gic_compare_interrupt, 52f7ea3060SAndrew Bresticker .percpu_dev_id = &gic_clockevent_device, 53a331ce63SAndrew Bresticker .flags = IRQF_PERCPU | IRQF_TIMER, 54a331ce63SAndrew Bresticker .name = "timer", 55a331ce63SAndrew Bresticker }; 56a331ce63SAndrew Bresticker 57e4752dbbSAndrew Bresticker static void gic_clockevent_cpu_init(struct clock_event_device *cd) 58a331ce63SAndrew Bresticker { 59a331ce63SAndrew Bresticker unsigned int cpu = smp_processor_id(); 60a331ce63SAndrew Bresticker 61a331ce63SAndrew Bresticker cd->name = "MIPS GIC"; 62a331ce63SAndrew Bresticker cd->features = CLOCK_EVT_FEAT_ONESHOT | 63a331ce63SAndrew Bresticker CLOCK_EVT_FEAT_C3STOP; 64a331ce63SAndrew Bresticker 65a45da565SAndrew Bresticker cd->rating = 350; 66e4752dbbSAndrew Bresticker cd->irq = gic_timer_irq; 67a331ce63SAndrew Bresticker cd->cpumask = cpumask_of(cpu); 68a331ce63SAndrew Bresticker cd->set_next_event = gic_next_event; 69a331ce63SAndrew Bresticker cd->set_mode = gic_set_clock_mode; 70a331ce63SAndrew Bresticker 71b695d8e6SAndrew Bresticker clockevents_config_and_register(cd, gic_frequency, 0x300, 0x7fffffff); 72a331ce63SAndrew Bresticker 73e4752dbbSAndrew Bresticker enable_percpu_irq(gic_timer_irq, IRQ_TYPE_NONE); 74a331ce63SAndrew Bresticker } 75a331ce63SAndrew Bresticker 76e4752dbbSAndrew Bresticker static void gic_clockevent_cpu_exit(struct clock_event_device *cd) 77e4752dbbSAndrew Bresticker { 78e4752dbbSAndrew Bresticker disable_percpu_irq(gic_timer_irq); 79e4752dbbSAndrew Bresticker } 80e4752dbbSAndrew Bresticker 81e4752dbbSAndrew Bresticker static int gic_cpu_notifier(struct notifier_block *nb, unsigned long action, 82e4752dbbSAndrew Bresticker void *data) 83e4752dbbSAndrew Bresticker { 84e4752dbbSAndrew Bresticker switch (action & ~CPU_TASKS_FROZEN) { 85e4752dbbSAndrew Bresticker case CPU_STARTING: 86e4752dbbSAndrew Bresticker gic_clockevent_cpu_init(this_cpu_ptr(&gic_clockevent_device)); 87e4752dbbSAndrew Bresticker break; 88e4752dbbSAndrew Bresticker case CPU_DYING: 89e4752dbbSAndrew Bresticker gic_clockevent_cpu_exit(this_cpu_ptr(&gic_clockevent_device)); 90e4752dbbSAndrew Bresticker break; 91e4752dbbSAndrew Bresticker } 92e4752dbbSAndrew Bresticker 93e4752dbbSAndrew Bresticker return NOTIFY_OK; 94e4752dbbSAndrew Bresticker } 95e4752dbbSAndrew Bresticker 96e4752dbbSAndrew Bresticker static struct notifier_block gic_cpu_nb = { 97e4752dbbSAndrew Bresticker .notifier_call = gic_cpu_notifier, 98e4752dbbSAndrew Bresticker }; 99e4752dbbSAndrew Bresticker 100e4752dbbSAndrew Bresticker static int gic_clockevent_init(void) 101e4752dbbSAndrew Bresticker { 102e4752dbbSAndrew Bresticker if (!cpu_has_counter || !gic_frequency) 103e4752dbbSAndrew Bresticker return -ENXIO; 104e4752dbbSAndrew Bresticker 105e4752dbbSAndrew Bresticker setup_percpu_irq(gic_timer_irq, &gic_compare_irqaction); 106e4752dbbSAndrew Bresticker 107e4752dbbSAndrew Bresticker register_cpu_notifier(&gic_cpu_nb); 108e4752dbbSAndrew Bresticker 109e4752dbbSAndrew Bresticker gic_clockevent_cpu_init(this_cpu_ptr(&gic_clockevent_device)); 110a331ce63SAndrew Bresticker 111a331ce63SAndrew Bresticker return 0; 112a331ce63SAndrew Bresticker } 113a331ce63SAndrew Bresticker 114fa5635a2SAndrew Bresticker static cycle_t gic_hpt_read(struct clocksource *cs) 115fa5635a2SAndrew Bresticker { 116fa5635a2SAndrew Bresticker return gic_read_count(); 117fa5635a2SAndrew Bresticker } 118fa5635a2SAndrew Bresticker 119fa5635a2SAndrew Bresticker static struct clocksource gic_clocksource = { 120fa5635a2SAndrew Bresticker .name = "GIC", 121fa5635a2SAndrew Bresticker .read = gic_hpt_read, 122fa5635a2SAndrew Bresticker .flags = CLOCK_SOURCE_IS_CONTINUOUS, 123fa5635a2SAndrew Bresticker }; 124fa5635a2SAndrew Bresticker 125*e12aa828SAndrew Bresticker static void __init __gic_clocksource_init(void) 126fa5635a2SAndrew Bresticker { 127fa5635a2SAndrew Bresticker /* Set clocksource mask. */ 128fa5635a2SAndrew Bresticker gic_clocksource.mask = CLOCKSOURCE_MASK(gic_get_count_width()); 129fa5635a2SAndrew Bresticker 130fa5635a2SAndrew Bresticker /* Calculate a somewhat reasonable rating value. */ 131*e12aa828SAndrew Bresticker gic_clocksource.rating = 200 + gic_frequency / 10000000; 132fa5635a2SAndrew Bresticker 133*e12aa828SAndrew Bresticker clocksource_register_hz(&gic_clocksource, gic_frequency); 134e4752dbbSAndrew Bresticker 135e4752dbbSAndrew Bresticker gic_clockevent_init(); 136fa5635a2SAndrew Bresticker } 137*e12aa828SAndrew Bresticker 138*e12aa828SAndrew Bresticker void __init gic_clocksource_init(unsigned int frequency) 139*e12aa828SAndrew Bresticker { 140*e12aa828SAndrew Bresticker gic_frequency = frequency; 141*e12aa828SAndrew Bresticker gic_timer_irq = MIPS_GIC_IRQ_BASE + 142*e12aa828SAndrew Bresticker GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_COMPARE); 143*e12aa828SAndrew Bresticker 144*e12aa828SAndrew Bresticker __gic_clocksource_init(); 145*e12aa828SAndrew Bresticker } 146*e12aa828SAndrew Bresticker 147*e12aa828SAndrew Bresticker static void __init gic_clocksource_of_init(struct device_node *node) 148*e12aa828SAndrew Bresticker { 149*e12aa828SAndrew Bresticker if (WARN_ON(!gic_present || !node->parent || 150*e12aa828SAndrew Bresticker !of_device_is_compatible(node->parent, "mti,gic"))) 151*e12aa828SAndrew Bresticker return; 152*e12aa828SAndrew Bresticker 153*e12aa828SAndrew Bresticker if (of_property_read_u32(node, "clock-frequency", &gic_frequency)) { 154*e12aa828SAndrew Bresticker pr_err("GIC frequency not specified.\n"); 155*e12aa828SAndrew Bresticker return; 156*e12aa828SAndrew Bresticker } 157*e12aa828SAndrew Bresticker gic_timer_irq = irq_of_parse_and_map(node, 0); 158*e12aa828SAndrew Bresticker if (!gic_timer_irq) { 159*e12aa828SAndrew Bresticker pr_err("GIC timer IRQ not specified.\n"); 160*e12aa828SAndrew Bresticker return; 161*e12aa828SAndrew Bresticker } 162*e12aa828SAndrew Bresticker 163*e12aa828SAndrew Bresticker __gic_clocksource_init(); 164*e12aa828SAndrew Bresticker } 165*e12aa828SAndrew Bresticker CLOCKSOURCE_OF_DECLARE(mips_gic_timer, "mti,gic-timer", 166*e12aa828SAndrew Bresticker gic_clocksource_of_init); 167