Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.4-rc3 234 lines 5.5 kB view raw
1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. 7 */ 8 9#define pr_fmt(fmt) "mips-gic-timer: " fmt 10 11#include <linux/clk.h> 12#include <linux/clockchips.h> 13#include <linux/cpu.h> 14#include <linux/init.h> 15#include <linux/interrupt.h> 16#include <linux/notifier.h> 17#include <linux/of_irq.h> 18#include <linux/percpu.h> 19#include <linux/smp.h> 20#include <linux/time.h> 21#include <asm/mips-cps.h> 22 23static DEFINE_PER_CPU(struct clock_event_device, gic_clockevent_device); 24static int gic_timer_irq; 25static unsigned int gic_frequency; 26 27static u64 notrace gic_read_count(void) 28{ 29 unsigned int hi, hi2, lo; 30 31 if (mips_cm_is64) 32 return read_gic_counter(); 33 34 do { 35 hi = read_gic_counter_32h(); 36 lo = read_gic_counter_32l(); 37 hi2 = read_gic_counter_32h(); 38 } while (hi2 != hi); 39 40 return (((u64) hi) << 32) + lo; 41} 42 43static int gic_next_event(unsigned long delta, struct clock_event_device *evt) 44{ 45 int cpu = cpumask_first(evt->cpumask); 46 u64 cnt; 47 int res; 48 49 cnt = gic_read_count(); 50 cnt += (u64)delta; 51 if (cpu == raw_smp_processor_id()) { 52 write_gic_vl_compare(cnt); 53 } else { 54 write_gic_vl_other(mips_cm_vp_id(cpu)); 55 write_gic_vo_compare(cnt); 56 } 57 res = ((int)(gic_read_count() - cnt) >= 0) ? -ETIME : 0; 58 return res; 59} 60 61static irqreturn_t gic_compare_interrupt(int irq, void *dev_id) 62{ 63 struct clock_event_device *cd = dev_id; 64 65 write_gic_vl_compare(read_gic_vl_compare()); 66 cd->event_handler(cd); 67 return IRQ_HANDLED; 68} 69 70static struct irqaction gic_compare_irqaction = { 71 .handler = gic_compare_interrupt, 72 .percpu_dev_id = &gic_clockevent_device, 73 .flags = IRQF_PERCPU | IRQF_TIMER, 74 .name = "timer", 75}; 76 77static void gic_clockevent_cpu_init(unsigned int cpu, 78 struct clock_event_device *cd) 79{ 80 cd->name = "MIPS GIC"; 81 cd->features = CLOCK_EVT_FEAT_ONESHOT | 82 CLOCK_EVT_FEAT_C3STOP; 83 84 cd->rating = 350; 85 cd->irq = gic_timer_irq; 86 cd->cpumask = cpumask_of(cpu); 87 cd->set_next_event = gic_next_event; 88 89 clockevents_config_and_register(cd, gic_frequency, 0x300, 0x7fffffff); 90 91 enable_percpu_irq(gic_timer_irq, IRQ_TYPE_NONE); 92} 93 94static void gic_clockevent_cpu_exit(struct clock_event_device *cd) 95{ 96 disable_percpu_irq(gic_timer_irq); 97} 98 99static void gic_update_frequency(void *data) 100{ 101 unsigned long rate = (unsigned long)data; 102 103 clockevents_update_freq(this_cpu_ptr(&gic_clockevent_device), rate); 104} 105 106static int gic_starting_cpu(unsigned int cpu) 107{ 108 gic_clockevent_cpu_init(cpu, this_cpu_ptr(&gic_clockevent_device)); 109 return 0; 110} 111 112static int gic_clk_notifier(struct notifier_block *nb, unsigned long action, 113 void *data) 114{ 115 struct clk_notifier_data *cnd = data; 116 117 if (action == POST_RATE_CHANGE) 118 on_each_cpu(gic_update_frequency, (void *)cnd->new_rate, 1); 119 120 return NOTIFY_OK; 121} 122 123static int gic_dying_cpu(unsigned int cpu) 124{ 125 gic_clockevent_cpu_exit(this_cpu_ptr(&gic_clockevent_device)); 126 return 0; 127} 128 129static struct notifier_block gic_clk_nb = { 130 .notifier_call = gic_clk_notifier, 131}; 132 133static int gic_clockevent_init(void) 134{ 135 int ret; 136 137 if (!gic_frequency) 138 return -ENXIO; 139 140 ret = setup_percpu_irq(gic_timer_irq, &gic_compare_irqaction); 141 if (ret < 0) { 142 pr_err("IRQ %d setup failed (%d)\n", gic_timer_irq, ret); 143 return ret; 144 } 145 146 cpuhp_setup_state(CPUHP_AP_MIPS_GIC_TIMER_STARTING, 147 "clockevents/mips/gic/timer:starting", 148 gic_starting_cpu, gic_dying_cpu); 149 return 0; 150} 151 152static u64 gic_hpt_read(struct clocksource *cs) 153{ 154 return gic_read_count(); 155} 156 157static struct clocksource gic_clocksource = { 158 .name = "GIC", 159 .read = gic_hpt_read, 160 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 161 .archdata = { .vdso_clock_mode = VDSO_CLOCK_GIC }, 162}; 163 164static int __init __gic_clocksource_init(void) 165{ 166 unsigned int count_width; 167 int ret; 168 169 /* Set clocksource mask. */ 170 count_width = read_gic_config() & GIC_CONFIG_COUNTBITS; 171 count_width >>= __ffs(GIC_CONFIG_COUNTBITS); 172 count_width *= 4; 173 count_width += 32; 174 gic_clocksource.mask = CLOCKSOURCE_MASK(count_width); 175 176 /* Calculate a somewhat reasonable rating value. */ 177 gic_clocksource.rating = 200 + gic_frequency / 10000000; 178 179 ret = clocksource_register_hz(&gic_clocksource, gic_frequency); 180 if (ret < 0) 181 pr_warn("Unable to register clocksource\n"); 182 183 return ret; 184} 185 186static int __init gic_clocksource_of_init(struct device_node *node) 187{ 188 struct clk *clk; 189 int ret; 190 191 if (!mips_gic_present() || !node->parent || 192 !of_device_is_compatible(node->parent, "mti,gic")) { 193 pr_warn("No DT definition\n"); 194 return -ENXIO; 195 } 196 197 clk = of_clk_get(node, 0); 198 if (!IS_ERR(clk)) { 199 ret = clk_prepare_enable(clk); 200 if (ret < 0) { 201 pr_err("Failed to enable clock\n"); 202 clk_put(clk); 203 return ret; 204 } 205 206 gic_frequency = clk_get_rate(clk); 207 } else if (of_property_read_u32(node, "clock-frequency", 208 &gic_frequency)) { 209 pr_err("Frequency not specified\n"); 210 return -EINVAL; 211 } 212 gic_timer_irq = irq_of_parse_and_map(node, 0); 213 if (!gic_timer_irq) { 214 pr_err("IRQ not specified\n"); 215 return -EINVAL; 216 } 217 218 ret = __gic_clocksource_init(); 219 if (ret) 220 return ret; 221 222 ret = gic_clockevent_init(); 223 if (!ret && !IS_ERR(clk)) { 224 if (clk_notifier_register(clk, &gic_clk_nb) < 0) 225 pr_warn("Unable to register clock notifier\n"); 226 } 227 228 /* And finally start the counter */ 229 clear_gic_config(GIC_CONFIG_COUNTSTOP); 230 231 return 0; 232} 233TIMER_OF_DECLARE(mips_gic_timer, "mti,gic-timer", 234 gic_clocksource_of_init);