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 v3.13-rc8 150 lines 4.2 kB view raw
1/* 2 * Marvell Orion SoC timer handling. 3 * 4 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 5 * 6 * This file is licensed under the terms of the GNU General Public 7 * License version 2. This program is licensed "as is" without any 8 * warranty of any kind, whether express or implied. 9 * 10 * Timer 0 is used as free-running clocksource, while timer 1 is 11 * used as clock_event_device. 12 */ 13 14#include <linux/kernel.h> 15#include <linux/bitops.h> 16#include <linux/clk.h> 17#include <linux/clockchips.h> 18#include <linux/interrupt.h> 19#include <linux/of_address.h> 20#include <linux/of_irq.h> 21#include <linux/spinlock.h> 22#include <linux/sched_clock.h> 23 24#define TIMER_CTRL 0x00 25#define TIMER0_EN BIT(0) 26#define TIMER0_RELOAD_EN BIT(1) 27#define TIMER1_EN BIT(2) 28#define TIMER1_RELOAD_EN BIT(3) 29#define TIMER0_RELOAD 0x10 30#define TIMER0_VAL 0x14 31#define TIMER1_RELOAD 0x18 32#define TIMER1_VAL 0x1c 33 34#define ORION_ONESHOT_MIN 1 35#define ORION_ONESHOT_MAX 0xfffffffe 36 37static void __iomem *timer_base; 38static DEFINE_SPINLOCK(timer_ctrl_lock); 39 40/* 41 * Thread-safe access to TIMER_CTRL register 42 * (shared with watchdog timer) 43 */ 44void orion_timer_ctrl_clrset(u32 clr, u32 set) 45{ 46 spin_lock(&timer_ctrl_lock); 47 writel((readl(timer_base + TIMER_CTRL) & ~clr) | set, 48 timer_base + TIMER_CTRL); 49 spin_unlock(&timer_ctrl_lock); 50} 51EXPORT_SYMBOL(orion_timer_ctrl_clrset); 52 53/* 54 * Free-running clocksource handling. 55 */ 56static u32 notrace orion_read_sched_clock(void) 57{ 58 return ~readl(timer_base + TIMER0_VAL); 59} 60 61/* 62 * Clockevent handling. 63 */ 64static u32 ticks_per_jiffy; 65 66static int orion_clkevt_next_event(unsigned long delta, 67 struct clock_event_device *dev) 68{ 69 /* setup and enable one-shot timer */ 70 writel(delta, timer_base + TIMER1_VAL); 71 orion_timer_ctrl_clrset(TIMER1_RELOAD_EN, TIMER1_EN); 72 73 return 0; 74} 75 76static void orion_clkevt_mode(enum clock_event_mode mode, 77 struct clock_event_device *dev) 78{ 79 if (mode == CLOCK_EVT_MODE_PERIODIC) { 80 /* setup and enable periodic timer at 1/HZ intervals */ 81 writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD); 82 writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL); 83 orion_timer_ctrl_clrset(0, TIMER1_RELOAD_EN | TIMER1_EN); 84 } else { 85 /* disable timer */ 86 orion_timer_ctrl_clrset(TIMER1_RELOAD_EN | TIMER1_EN, 0); 87 } 88} 89 90static struct clock_event_device orion_clkevt = { 91 .name = "orion_event", 92 .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, 93 .shift = 32, 94 .rating = 300, 95 .set_next_event = orion_clkevt_next_event, 96 .set_mode = orion_clkevt_mode, 97}; 98 99static irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id) 100{ 101 orion_clkevt.event_handler(&orion_clkevt); 102 return IRQ_HANDLED; 103} 104 105static struct irqaction orion_clkevt_irq = { 106 .name = "orion_event", 107 .flags = IRQF_TIMER, 108 .handler = orion_clkevt_irq_handler, 109}; 110 111static void __init orion_timer_init(struct device_node *np) 112{ 113 struct clk *clk; 114 int irq; 115 116 /* timer registers are shared with watchdog timer */ 117 timer_base = of_iomap(np, 0); 118 if (!timer_base) 119 panic("%s: unable to map resource\n", np->name); 120 121 clk = of_clk_get(np, 0); 122 if (IS_ERR(clk)) 123 panic("%s: unable to get clk\n", np->name); 124 clk_prepare_enable(clk); 125 126 /* we are only interested in timer1 irq */ 127 irq = irq_of_parse_and_map(np, 1); 128 if (irq <= 0) 129 panic("%s: unable to parse timer1 irq\n", np->name); 130 131 /* setup timer0 as free-running clocksource */ 132 writel(~0, timer_base + TIMER0_VAL); 133 writel(~0, timer_base + TIMER0_RELOAD); 134 orion_timer_ctrl_clrset(0, TIMER0_RELOAD_EN | TIMER0_EN); 135 clocksource_mmio_init(timer_base + TIMER0_VAL, "orion_clocksource", 136 clk_get_rate(clk), 300, 32, 137 clocksource_mmio_readl_down); 138 setup_sched_clock(orion_read_sched_clock, 32, clk_get_rate(clk)); 139 140 /* setup timer1 as clockevent timer */ 141 if (setup_irq(irq, &orion_clkevt_irq)) 142 panic("%s: unable to setup irq\n", np->name); 143 144 ticks_per_jiffy = (clk_get_rate(clk) + HZ/2) / HZ; 145 orion_clkevt.cpumask = cpumask_of(0); 146 orion_clkevt.irq = irq; 147 clockevents_config_and_register(&orion_clkevt, clk_get_rate(clk), 148 ORION_ONESHOT_MIN, ORION_ONESHOT_MAX); 149} 150CLOCKSOURCE_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init);