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.
Unified · Split
| @@ -5039,6 +5039,7 @@ S: Supported · 5038 unmodified lines | ||
| 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> |
| @@ -0,0 +1,51 @@ | ||
| 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_ */ | |
| @@ -0,0 +1,29 @@ | ||
| 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 */ | |
| @@ -16,10 +16,13 @@ · 15 unmodified lines | ||
| 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. |
| @@ -37,6 +40,7 @@ · 11 unmodified lines | ||
| 37 | 40 | struct machine_desc { |
| 38 | 41 | const char *name; |
| 39 | 42 | const char **dt_compat; |
| 43 | + struct meta_clock_desc *clocks; | |
| 40 | 44 | |
| 41 | 45 | unsigned int nr_irqs; |
| 42 | 46 | |
| @@ -0,0 +1,53 @@ | ||
| 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 | + | |
| @@ -0,0 +1,15 @@ | ||
| 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 | +} | |
| @@ -58,3 +58,8 @@ config CLKSRC_ARM_GENERIC · 57 unmodified lines | ||
| 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. | |
| @@ -18,3 +18,4 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o · 17 unmodified lines | ||
| 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 | |
| @@ -0,0 +1,198 @@ | ||
| 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 | +} | |
| @@ -0,0 +1,21 @@ | ||
| 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 */ | |