Skip to content

Commit a2c5d4e

Browse files
author
James Hogan
committed
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>
1 parent bc3966b commit a2c5d4e

File tree

10 files changed

+378
-0
lines changed

10 files changed

+378
-0
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5039,6 +5039,7 @@ S: Supported
50395039
F: arch/metag/
50405040
F: Documentation/metag/
50415041
F: Documentation/devicetree/bindings/metag/
5042+
F: drivers/clocksource/metag_generic.c
50425043

50435044
MICROBLAZE ARCHITECTURE
50445045
M: Michal Simek <monstr@monstr.eu>

arch/metag/include/asm/clock.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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_ */

arch/metag/include/asm/delay.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 */

arch/metag/include/asm/mach/arch.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@
1616

1717
#include <linux/stddef.h>
1818

19+
#include <asm/clock.h>
20+
1921
/**
2022
* struct machine_desc - Describes a board controlled by a Meta.
2123
* @name: Board/SoC name.
2224
* @dt_compat: Array of device tree 'compatible' strings.
25+
* @clocks: Clock callbacks.
2326
*
2427
* @nr_irqs: Maximum number of IRQs.
2528
* If 0, defaults to NR_IRQS in asm-generic/irq.h.
@@ -37,6 +40,7 @@
3740
struct machine_desc {
3841
const char *name;
3942
const char **dt_compat;
43+
struct meta_clock_desc *clocks;
4044

4145
unsigned int nr_irqs;
4246

arch/metag/kernel/clock.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+

arch/metag/kernel/time.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

drivers/clocksource/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,8 @@ config CLKSRC_ARM_GENERIC
5858
def_bool y if ARM64
5959
help
6060
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.

drivers/clocksource/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
1818
obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o
1919

2020
obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o
21+
obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 commit comments

Comments
 (0)