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