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

metag: Time keeping

Add time keeping code for metag. Meta hardware threads have 2 timers.
The background timer (TXTIMER) is used as a free-running time base, and
the interrupt timer (TXTIMERI) is used for the timer interrupt. Both
counters traditionally count at approximately 1MHz.

Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: John Stultz <johnstul@us.ibm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>

+378
+1
MAINTAINERS
··· 5039 5039 F: arch/metag/ 5040 5040 F: Documentation/metag/ 5041 5041 F: Documentation/devicetree/bindings/metag/ 5042 + F: drivers/clocksource/metag_generic.c 5042 5043 5043 5044 MICROBLAZE ARCHITECTURE 5044 5045 M: Michal Simek <monstr@monstr.eu>
+51
arch/metag/include/asm/clock.h
··· 1 + /* 2 + * arch/metag/include/asm/clock.h 3 + * 4 + * Copyright (C) 2012 Imagination Technologies Ltd. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + 11 + #ifndef _METAG_CLOCK_H_ 12 + #define _METAG_CLOCK_H_ 13 + 14 + #include <asm/mach/arch.h> 15 + 16 + /** 17 + * struct meta_clock_desc - Meta Core clock callbacks. 18 + * @get_core_freq: Get the frequency of the Meta core. If this is NULL, the 19 + * core frequency will be determined like this: 20 + * Meta 1: based on loops_per_jiffy. 21 + * Meta 2: (EXPAND_TIMER_DIV + 1) MHz. 22 + */ 23 + struct meta_clock_desc { 24 + unsigned long (*get_core_freq)(void); 25 + }; 26 + 27 + extern struct meta_clock_desc _meta_clock; 28 + 29 + /* 30 + * Set up the default clock, ensuring all callbacks are valid - only accessible 31 + * during boot. 32 + */ 33 + void setup_meta_clocks(struct meta_clock_desc *desc); 34 + 35 + /** 36 + * get_coreclock() - Get the frequency of the Meta core clock. 37 + * 38 + * Returns: The Meta core clock frequency in Hz. 39 + */ 40 + static inline unsigned long get_coreclock(void) 41 + { 42 + /* 43 + * Use the current clock callback. If set correctly this will provide 44 + * the most accurate frequency as it can be calculated directly from the 45 + * PLL configuration. otherwise a default callback will have been set 46 + * instead. 47 + */ 48 + return _meta_clock.get_core_freq(); 49 + } 50 + 51 + #endif /* _METAG_CLOCK_H_ */
+29
arch/metag/include/asm/delay.h
··· 1 + #ifndef _METAG_DELAY_H 2 + #define _METAG_DELAY_H 3 + 4 + /* 5 + * Copyright (C) 1993 Linus Torvalds 6 + * 7 + * Delay routines calling functions in arch/metag/lib/delay.c 8 + */ 9 + 10 + /* Undefined functions to get compile-time errors */ 11 + extern void __bad_udelay(void); 12 + extern void __bad_ndelay(void); 13 + 14 + extern void __udelay(unsigned long usecs); 15 + extern void __ndelay(unsigned long nsecs); 16 + extern void __const_udelay(unsigned long xloops); 17 + extern void __delay(unsigned long loops); 18 + 19 + /* 0x10c7 is 2**32 / 1000000 (rounded up) */ 20 + #define udelay(n) (__builtin_constant_p(n) ? \ 21 + ((n) > 20000 ? __bad_udelay() : __const_udelay((n) * 0x10c7ul)) : \ 22 + __udelay(n)) 23 + 24 + /* 0x5 is 2**32 / 1000000000 (rounded up) */ 25 + #define ndelay(n) (__builtin_constant_p(n) ? \ 26 + ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \ 27 + __ndelay(n)) 28 + 29 + #endif /* _METAG_DELAY_H */
+4
arch/metag/include/asm/mach/arch.h
··· 16 16 17 17 #include <linux/stddef.h> 18 18 19 + #include <asm/clock.h> 20 + 19 21 /** 20 22 * struct machine_desc - Describes a board controlled by a Meta. 21 23 * @name: Board/SoC name. 22 24 * @dt_compat: Array of device tree 'compatible' strings. 25 + * @clocks: Clock callbacks. 23 26 * 24 27 * @nr_irqs: Maximum number of IRQs. 25 28 * If 0, defaults to NR_IRQS in asm-generic/irq.h. ··· 40 37 struct machine_desc { 41 38 const char *name; 42 39 const char **dt_compat; 40 + struct meta_clock_desc *clocks; 43 41 44 42 unsigned int nr_irqs; 45 43
+53
arch/metag/kernel/clock.c
··· 1 + /* 2 + * arch/metag/kernel/clock.c 3 + * 4 + * Copyright (C) 2012 Imagination Technologies Ltd. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + 11 + #include <linux/delay.h> 12 + #include <linux/io.h> 13 + 14 + #include <asm/param.h> 15 + #include <asm/clock.h> 16 + 17 + struct meta_clock_desc _meta_clock; 18 + 19 + /* Default machine get_core_freq callback. */ 20 + static unsigned long get_core_freq_default(void) 21 + { 22 + #ifdef CONFIG_METAG_META21 23 + /* 24 + * Meta 2 cores divide down the core clock for the Meta timers, so we 25 + * can estimate the core clock from the divider. 26 + */ 27 + return (metag_in32(EXPAND_TIMER_DIV) + 1) * 1000000; 28 + #else 29 + /* 30 + * On Meta 1 we don't know the core clock, but assuming the Meta timer 31 + * is correct it can be estimated based on loops_per_jiffy. 32 + */ 33 + return (loops_per_jiffy * HZ * 5) >> 1; 34 + #endif 35 + } 36 + 37 + /** 38 + * setup_meta_clocks() - Set up the Meta clock. 39 + * @desc: Clock descriptor usually provided by machine description 40 + * 41 + * Ensures all callbacks are valid. 42 + */ 43 + void __init setup_meta_clocks(struct meta_clock_desc *desc) 44 + { 45 + /* copy callbacks */ 46 + if (desc) 47 + _meta_clock = *desc; 48 + 49 + /* set fallback functions */ 50 + if (!_meta_clock.get_core_freq) 51 + _meta_clock.get_core_freq = get_core_freq_default; 52 + } 53 +
+15
arch/metag/kernel/time.c
··· 1 + /* 2 + * Copyright (C) 2005-2013 Imagination Technologies Ltd. 3 + * 4 + * This file contains the Meta-specific time handling details. 5 + * 6 + */ 7 + 8 + #include <linux/init.h> 9 + 10 + #include <clocksource/metag_generic.h> 11 + 12 + void __init time_init(void) 13 + { 14 + metag_generic_timer_init(); 15 + }
+5
drivers/clocksource/Kconfig
··· 58 58 def_bool y if ARM64 59 59 help 60 60 This option enables support for the ARM generic timer. 61 + 62 + config CLKSRC_METAG_GENERIC 63 + def_bool y if METAG 64 + help 65 + This option enables support for the Meta per-thread timers.
+1
drivers/clocksource/Makefile
··· 18 18 obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o 19 19 20 20 obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o 21 + obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
+198
drivers/clocksource/metag_generic.c
··· 1 + /* 2 + * Copyright (C) 2005-2013 Imagination Technologies Ltd. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 as 6 + * published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope that it will be useful, 9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 + * GNU General Public License for more details. 12 + * 13 + * You should have received a copy of the GNU General Public License 14 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 + * 16 + * 17 + * Support for Meta per-thread timers. 18 + * 19 + * Meta hardware threads have 2 timers. The background timer (TXTIMER) is used 20 + * as a free-running time base (hz clocksource), and the interrupt timer 21 + * (TXTIMERI) is used for the timer interrupt (clock event). Both counters 22 + * traditionally count at approximately 1MHz. 23 + */ 24 + 25 + #include <clocksource/metag_generic.h> 26 + #include <linux/cpu.h> 27 + #include <linux/errno.h> 28 + #include <linux/sched.h> 29 + #include <linux/kernel.h> 30 + #include <linux/param.h> 31 + #include <linux/time.h> 32 + #include <linux/init.h> 33 + #include <linux/proc_fs.h> 34 + #include <linux/clocksource.h> 35 + #include <linux/clockchips.h> 36 + #include <linux/interrupt.h> 37 + 38 + #include <asm/clock.h> 39 + #include <asm/hwthread.h> 40 + #include <asm/core_reg.h> 41 + #include <asm/metag_mem.h> 42 + #include <asm/tbx.h> 43 + 44 + #define HARDWARE_FREQ 1000000 /* 1MHz */ 45 + #define HARDWARE_DIV 1 /* divide by 1 = 1MHz clock */ 46 + #define HARDWARE_TO_NS_SHIFT 10 /* convert ticks to ns */ 47 + 48 + static unsigned int hwtimer_freq = HARDWARE_FREQ; 49 + static DEFINE_PER_CPU(struct clock_event_device, local_clockevent); 50 + static DEFINE_PER_CPU(char [11], local_clockevent_name); 51 + 52 + static int metag_timer_set_next_event(unsigned long delta, 53 + struct clock_event_device *dev) 54 + { 55 + __core_reg_set(TXTIMERI, -delta); 56 + return 0; 57 + } 58 + 59 + static void metag_timer_set_mode(enum clock_event_mode mode, 60 + struct clock_event_device *evt) 61 + { 62 + switch (mode) { 63 + case CLOCK_EVT_MODE_ONESHOT: 64 + case CLOCK_EVT_MODE_RESUME: 65 + break; 66 + 67 + case CLOCK_EVT_MODE_SHUTDOWN: 68 + /* We should disable the IRQ here */ 69 + break; 70 + 71 + case CLOCK_EVT_MODE_PERIODIC: 72 + case CLOCK_EVT_MODE_UNUSED: 73 + WARN_ON(1); 74 + break; 75 + }; 76 + } 77 + 78 + static cycle_t metag_clocksource_read(struct clocksource *cs) 79 + { 80 + return __core_reg_get(TXTIMER); 81 + } 82 + 83 + static struct clocksource clocksource_metag = { 84 + .name = "META", 85 + .rating = 200, 86 + .mask = CLOCKSOURCE_MASK(32), 87 + .read = metag_clocksource_read, 88 + .flags = CLOCK_SOURCE_IS_CONTINUOUS, 89 + }; 90 + 91 + static irqreturn_t metag_timer_interrupt(int irq, void *dummy) 92 + { 93 + struct clock_event_device *evt = &__get_cpu_var(local_clockevent); 94 + 95 + evt->event_handler(evt); 96 + 97 + return IRQ_HANDLED; 98 + } 99 + 100 + static struct irqaction metag_timer_irq = { 101 + .name = "META core timer", 102 + .handler = metag_timer_interrupt, 103 + .flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_PERCPU, 104 + }; 105 + 106 + unsigned long long sched_clock(void) 107 + { 108 + unsigned long long ticks = __core_reg_get(TXTIMER); 109 + return ticks << HARDWARE_TO_NS_SHIFT; 110 + } 111 + 112 + static void __cpuinit arch_timer_setup(unsigned int cpu) 113 + { 114 + unsigned int txdivtime; 115 + struct clock_event_device *clk = &per_cpu(local_clockevent, cpu); 116 + char *name = per_cpu(local_clockevent_name, cpu); 117 + 118 + txdivtime = __core_reg_get(TXDIVTIME); 119 + 120 + txdivtime &= ~TXDIVTIME_DIV_BITS; 121 + txdivtime |= (HARDWARE_DIV & TXDIVTIME_DIV_BITS); 122 + 123 + __core_reg_set(TXDIVTIME, txdivtime); 124 + 125 + sprintf(name, "META %d", cpu); 126 + clk->name = name; 127 + clk->features = CLOCK_EVT_FEAT_ONESHOT, 128 + 129 + clk->rating = 200, 130 + clk->shift = 12, 131 + clk->irq = tbisig_map(TBID_SIGNUM_TRT), 132 + clk->set_mode = metag_timer_set_mode, 133 + clk->set_next_event = metag_timer_set_next_event, 134 + 135 + clk->mult = div_sc(hwtimer_freq, NSEC_PER_SEC, clk->shift); 136 + clk->max_delta_ns = clockevent_delta2ns(0x7fffffff, clk); 137 + clk->min_delta_ns = clockevent_delta2ns(0xf, clk); 138 + clk->cpumask = cpumask_of(cpu); 139 + 140 + clockevents_register_device(clk); 141 + 142 + /* 143 + * For all non-boot CPUs we need to synchronize our free 144 + * running clock (TXTIMER) with the boot CPU's clock. 145 + * 146 + * While this won't be accurate, it should be close enough. 147 + */ 148 + if (cpu) { 149 + unsigned int thread0 = cpu_2_hwthread_id[0]; 150 + unsigned long val; 151 + 152 + val = core_reg_read(TXUCT_ID, TXTIMER_REGNUM, thread0); 153 + __core_reg_set(TXTIMER, val); 154 + } 155 + } 156 + 157 + static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self, 158 + unsigned long action, void *hcpu) 159 + { 160 + int cpu = (long)hcpu; 161 + 162 + switch (action) { 163 + case CPU_STARTING: 164 + case CPU_STARTING_FROZEN: 165 + arch_timer_setup(cpu); 166 + break; 167 + } 168 + 169 + return NOTIFY_OK; 170 + } 171 + 172 + static struct notifier_block __cpuinitdata arch_timer_cpu_nb = { 173 + .notifier_call = arch_timer_cpu_notify, 174 + }; 175 + 176 + int __init metag_generic_timer_init(void) 177 + { 178 + /* 179 + * On Meta 2 SoCs, the actual frequency of the timer is based on the 180 + * Meta core clock speed divided by an integer, so it is only 181 + * approximately 1MHz. Calculating the real frequency here drastically 182 + * reduces clock skew on these SoCs. 183 + */ 184 + #ifdef CONFIG_METAG_META21 185 + hwtimer_freq = get_coreclock() / (metag_in32(EXPAND_TIMER_DIV) + 1); 186 + #endif 187 + clocksource_register_hz(&clocksource_metag, hwtimer_freq); 188 + 189 + setup_irq(tbisig_map(TBID_SIGNUM_TRT), &metag_timer_irq); 190 + 191 + /* Configure timer on boot CPU */ 192 + arch_timer_setup(smp_processor_id()); 193 + 194 + /* Hook cpu boot to configure other CPU's timers */ 195 + register_cpu_notifier(&arch_timer_cpu_nb); 196 + 197 + return 0; 198 + }
+21
include/clocksource/metag_generic.h
··· 1 + /* 2 + * Copyright (C) 2013 Imaginaton Technologies Ltd. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 as 6 + * published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope that it will be useful, 9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 + * GNU General Public License for more details. 12 + * 13 + * You should have received a copy of the GNU General Public License 14 + * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 + */ 16 + #ifndef __CLKSOURCE_METAG_GENERIC_H 17 + #define __CLKSOURCE_METAG_GENERIC_H 18 + 19 + extern int metag_generic_timer_init(void); 20 + 21 + #endif /* __CLKSOURCE_METAG_GENERIC_H */