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

C6X: time management

Original port to early 2.6 kernel using TI COFF toolchain.
Brought up to date by Mark Salter <msalter@redhat.com>

Signed-off-by: Aurelien Jacquiot <a-jacquiot@ti.com>
Signed-off-by: Mark Salter <msalter@redhat.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Arnd Bergmann <arnd@arndb.de>

authored by

Aurelien Jacquiot and committed by
Mark Salter
546a3954 03a34755

+340
+6
arch/c6x/include/asm/timer64.h
··· 1 + #ifndef _C6X_TIMER64_H 2 + #define _C6X_TIMER64_H 3 + 4 + extern void __init timer64_init(void); 5 + 6 + #endif /* _C6X_TIMER64_H */
+33
arch/c6x/include/asm/timex.h
··· 1 + /* 2 + * Port on Texas Instruments TMS320C6x architecture 3 + * 4 + * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated 5 + * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) 6 + * 7 + * Modified for 2.6.34: Mark Salter <msalter@redhat.com> 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License version 2 as 11 + * published by the Free Software Foundation. 12 + */ 13 + #ifndef _ASM_C6X_TIMEX_H 14 + #define _ASM_C6X_TIMEX_H 15 + 16 + #define CLOCK_TICK_RATE ((1000 * 1000000UL) / 6) 17 + 18 + /* 64-bit timestamp */ 19 + typedef unsigned long long cycles_t; 20 + 21 + static inline cycles_t get_cycles(void) 22 + { 23 + unsigned l, h; 24 + 25 + asm volatile (" dint\n" 26 + " mvc .s2 TSCL,%0\n" 27 + " mvc .s2 TSCH,%1\n" 28 + " rint\n" 29 + : "=b"(l), "=b"(h)); 30 + return ((cycles_t)h << 32) | l; 31 + } 32 + 33 + #endif /* _ASM_C6X_TIMEX_H */
+65
arch/c6x/kernel/time.c
··· 1 + /* 2 + * Port on Texas Instruments TMS320C6x architecture 3 + * 4 + * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated 5 + * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + */ 11 + 12 + #include <linux/kernel.h> 13 + #include <linux/clocksource.h> 14 + #include <linux/errno.h> 15 + #include <linux/sched.h> 16 + #include <linux/param.h> 17 + #include <linux/string.h> 18 + #include <linux/mm.h> 19 + #include <linux/interrupt.h> 20 + #include <linux/timex.h> 21 + #include <linux/profile.h> 22 + 23 + #include <asm/timer64.h> 24 + 25 + static u32 sched_clock_multiplier; 26 + #define SCHED_CLOCK_SHIFT 16 27 + 28 + static cycle_t tsc_read(struct clocksource *cs) 29 + { 30 + return get_cycles(); 31 + } 32 + 33 + static struct clocksource clocksource_tsc = { 34 + .name = "timestamp", 35 + .rating = 300, 36 + .read = tsc_read, 37 + .mask = CLOCKSOURCE_MASK(64), 38 + .flags = CLOCK_SOURCE_IS_CONTINUOUS, 39 + }; 40 + 41 + /* 42 + * scheduler clock - returns current time in nanoseconds. 43 + */ 44 + u64 sched_clock(void) 45 + { 46 + u64 tsc = get_cycles(); 47 + 48 + return (tsc * sched_clock_multiplier) >> SCHED_CLOCK_SHIFT; 49 + } 50 + 51 + void time_init(void) 52 + { 53 + u64 tmp = (u64)NSEC_PER_SEC << SCHED_CLOCK_SHIFT; 54 + 55 + do_div(tmp, c6x_core_freq); 56 + sched_clock_multiplier = tmp; 57 + 58 + clocksource_register_hz(&clocksource_tsc, c6x_core_freq); 59 + 60 + /* write anything into TSCL to enable counting */ 61 + set_creg(TSCL, 0); 62 + 63 + /* probe for timer64 event timer */ 64 + timer64_init(); 65 + }
+236
arch/c6x/platforms/timer64.c
··· 1 + /* 2 + * Copyright (C) 2010, 2011 Texas Instruments Incorporated 3 + * Contributed by: Mark Salter (msalter@redhat.com) 4 + * 5 + * This program is free software; you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License version 2 as 7 + * published by the Free Software Foundation. 8 + */ 9 + 10 + #include <linux/clockchips.h> 11 + #include <linux/interrupt.h> 12 + #include <linux/io.h> 13 + #include <linux/of.h> 14 + #include <linux/of_irq.h> 15 + #include <linux/of_address.h> 16 + #include <asm/soc.h> 17 + #include <asm/dscr.h> 18 + #include <asm/timer64.h> 19 + 20 + struct timer_regs { 21 + u32 reserved0; 22 + u32 emumgt; 23 + u32 reserved1; 24 + u32 reserved2; 25 + u32 cntlo; 26 + u32 cnthi; 27 + u32 prdlo; 28 + u32 prdhi; 29 + u32 tcr; 30 + u32 tgcr; 31 + u32 wdtcr; 32 + }; 33 + 34 + static struct timer_regs __iomem *timer; 35 + 36 + #define TCR_TSTATLO 0x001 37 + #define TCR_INVOUTPLO 0x002 38 + #define TCR_INVINPLO 0x004 39 + #define TCR_CPLO 0x008 40 + #define TCR_ENAMODELO_ONCE 0x040 41 + #define TCR_ENAMODELO_CONT 0x080 42 + #define TCR_ENAMODELO_MASK 0x0c0 43 + #define TCR_PWIDLO_MASK 0x030 44 + #define TCR_CLKSRCLO 0x100 45 + #define TCR_TIENLO 0x200 46 + #define TCR_TSTATHI (0x001 << 16) 47 + #define TCR_INVOUTPHI (0x002 << 16) 48 + #define TCR_CPHI (0x008 << 16) 49 + #define TCR_PWIDHI_MASK (0x030 << 16) 50 + #define TCR_ENAMODEHI_ONCE (0x040 << 16) 51 + #define TCR_ENAMODEHI_CONT (0x080 << 16) 52 + #define TCR_ENAMODEHI_MASK (0x0c0 << 16) 53 + 54 + #define TGCR_TIMLORS 0x001 55 + #define TGCR_TIMHIRS 0x002 56 + #define TGCR_TIMMODE_UD32 0x004 57 + #define TGCR_TIMMODE_WDT64 0x008 58 + #define TGCR_TIMMODE_CD32 0x00c 59 + #define TGCR_TIMMODE_MASK 0x00c 60 + #define TGCR_PSCHI_MASK (0x00f << 8) 61 + #define TGCR_TDDRHI_MASK (0x00f << 12) 62 + 63 + /* 64 + * Timer clocks are divided down from the CPU clock 65 + * The divisor is in the EMUMGTCLKSPD register 66 + */ 67 + #define TIMER_DIVISOR \ 68 + ((soc_readl(&timer->emumgt) & (0xf << 16)) >> 16) 69 + 70 + #define TIMER64_RATE (c6x_core_freq / TIMER_DIVISOR) 71 + 72 + #define TIMER64_MODE_DISABLED 0 73 + #define TIMER64_MODE_ONE_SHOT TCR_ENAMODELO_ONCE 74 + #define TIMER64_MODE_PERIODIC TCR_ENAMODELO_CONT 75 + 76 + static int timer64_mode; 77 + static int timer64_devstate_id = -1; 78 + 79 + static void timer64_config(unsigned long period) 80 + { 81 + u32 tcr = soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK; 82 + 83 + soc_writel(tcr, &timer->tcr); 84 + soc_writel(period - 1, &timer->prdlo); 85 + soc_writel(0, &timer->cntlo); 86 + tcr |= timer64_mode; 87 + soc_writel(tcr, &timer->tcr); 88 + } 89 + 90 + static void timer64_enable(void) 91 + { 92 + u32 val; 93 + 94 + if (timer64_devstate_id >= 0) 95 + dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_ENABLED); 96 + 97 + /* disable timer, reset count */ 98 + soc_writel(soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK, &timer->tcr); 99 + soc_writel(0, &timer->prdlo); 100 + 101 + /* use internal clock and 1 cycle pulse width */ 102 + val = soc_readl(&timer->tcr); 103 + soc_writel(val & ~(TCR_CLKSRCLO | TCR_PWIDLO_MASK), &timer->tcr); 104 + 105 + /* dual 32-bit unchained mode */ 106 + val = soc_readl(&timer->tgcr) & ~TGCR_TIMMODE_MASK; 107 + soc_writel(val, &timer->tgcr); 108 + soc_writel(val | (TGCR_TIMLORS | TGCR_TIMMODE_UD32), &timer->tgcr); 109 + } 110 + 111 + static void timer64_disable(void) 112 + { 113 + /* disable timer, reset count */ 114 + soc_writel(soc_readl(&timer->tcr) & ~TCR_ENAMODELO_MASK, &timer->tcr); 115 + soc_writel(0, &timer->prdlo); 116 + 117 + if (timer64_devstate_id >= 0) 118 + dscr_set_devstate(timer64_devstate_id, DSCR_DEVSTATE_DISABLED); 119 + } 120 + 121 + static int next_event(unsigned long delta, 122 + struct clock_event_device *evt) 123 + { 124 + timer64_config(delta); 125 + return 0; 126 + } 127 + 128 + static void set_clock_mode(enum clock_event_mode mode, 129 + struct clock_event_device *evt) 130 + { 131 + switch (mode) { 132 + case CLOCK_EVT_MODE_PERIODIC: 133 + timer64_enable(); 134 + timer64_mode = TIMER64_MODE_PERIODIC; 135 + timer64_config(TIMER64_RATE / HZ); 136 + break; 137 + case CLOCK_EVT_MODE_ONESHOT: 138 + timer64_enable(); 139 + timer64_mode = TIMER64_MODE_ONE_SHOT; 140 + break; 141 + case CLOCK_EVT_MODE_UNUSED: 142 + case CLOCK_EVT_MODE_SHUTDOWN: 143 + timer64_mode = TIMER64_MODE_DISABLED; 144 + timer64_disable(); 145 + break; 146 + case CLOCK_EVT_MODE_RESUME: 147 + break; 148 + } 149 + } 150 + 151 + static struct clock_event_device t64_clockevent_device = { 152 + .name = "TIMER64_EVT32_TIMER", 153 + .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, 154 + .rating = 200, 155 + .set_mode = set_clock_mode, 156 + .set_next_event = next_event, 157 + }; 158 + 159 + static irqreturn_t timer_interrupt(int irq, void *dev_id) 160 + { 161 + struct clock_event_device *cd = &t64_clockevent_device; 162 + 163 + cd->event_handler(cd); 164 + 165 + return IRQ_HANDLED; 166 + } 167 + 168 + static struct irqaction timer_iact = { 169 + .name = "timer", 170 + .flags = IRQF_TIMER, 171 + .handler = timer_interrupt, 172 + .dev_id = &t64_clockevent_device, 173 + }; 174 + 175 + void __init timer64_init(void) 176 + { 177 + struct clock_event_device *cd = &t64_clockevent_device; 178 + struct device_node *np, *first = NULL; 179 + u32 val; 180 + int err, found = 0; 181 + 182 + for_each_compatible_node(np, NULL, "ti,c64x+timer64") { 183 + err = of_property_read_u32(np, "ti,core-mask", &val); 184 + if (!err) { 185 + if (val & (1 << get_coreid())) { 186 + found = 1; 187 + break; 188 + } 189 + } else if (!first) 190 + first = np; 191 + } 192 + if (!found) { 193 + /* try first one with no core-mask */ 194 + if (first) 195 + np = of_node_get(first); 196 + else { 197 + pr_debug("Cannot find ti,c64x+timer64 timer.\n"); 198 + return; 199 + } 200 + } 201 + 202 + timer = of_iomap(np, 0); 203 + if (!timer) { 204 + pr_debug("%s: Cannot map timer registers.\n", np->full_name); 205 + goto out; 206 + } 207 + pr_debug("%s: Timer registers=%p.\n", np->full_name, timer); 208 + 209 + cd->irq = irq_of_parse_and_map(np, 0); 210 + if (cd->irq == NO_IRQ) { 211 + pr_debug("%s: Cannot find interrupt.\n", np->full_name); 212 + iounmap(timer); 213 + goto out; 214 + } 215 + 216 + /* If there is a device state control, save the ID. */ 217 + err = of_property_read_u32(np, "ti,dscr-dev-enable", &val); 218 + if (!err) 219 + timer64_devstate_id = val; 220 + 221 + pr_debug("%s: Timer irq=%d.\n", np->full_name, cd->irq); 222 + 223 + clockevents_calc_mult_shift(cd, c6x_core_freq / TIMER_DIVISOR, 5); 224 + 225 + cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd); 226 + cd->min_delta_ns = clockevent_delta2ns(250, cd); 227 + 228 + cd->cpumask = cpumask_of(smp_processor_id()); 229 + 230 + clockevents_register_device(cd); 231 + setup_irq(cd->irq, &timer_iact); 232 + 233 + out: 234 + of_node_put(np); 235 + return; 236 + }