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 v4.7 148 lines 4.1 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; 38 39/* 40 * Free-running clocksource handling. 41 */ 42static u64 notrace orion_read_sched_clock(void) 43{ 44 return ~readl(timer_base + TIMER0_VAL); 45} 46 47/* 48 * Clockevent handling. 49 */ 50static u32 ticks_per_jiffy; 51 52static int orion_clkevt_next_event(unsigned long delta, 53 struct clock_event_device *dev) 54{ 55 /* setup and enable one-shot timer */ 56 writel(delta, timer_base + TIMER1_VAL); 57 atomic_io_modify(timer_base + TIMER_CTRL, 58 TIMER1_RELOAD_EN | TIMER1_EN, TIMER1_EN); 59 60 return 0; 61} 62 63static int orion_clkevt_shutdown(struct clock_event_device *dev) 64{ 65 /* disable timer */ 66 atomic_io_modify(timer_base + TIMER_CTRL, 67 TIMER1_RELOAD_EN | TIMER1_EN, 0); 68 return 0; 69} 70 71static int orion_clkevt_set_periodic(struct clock_event_device *dev) 72{ 73 /* setup and enable periodic timer at 1/HZ intervals */ 74 writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD); 75 writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL); 76 atomic_io_modify(timer_base + TIMER_CTRL, 77 TIMER1_RELOAD_EN | TIMER1_EN, 78 TIMER1_RELOAD_EN | TIMER1_EN); 79 return 0; 80} 81 82static struct clock_event_device orion_clkevt = { 83 .name = "orion_event", 84 .features = CLOCK_EVT_FEAT_ONESHOT | 85 CLOCK_EVT_FEAT_PERIODIC, 86 .shift = 32, 87 .rating = 300, 88 .set_next_event = orion_clkevt_next_event, 89 .set_state_shutdown = orion_clkevt_shutdown, 90 .set_state_periodic = orion_clkevt_set_periodic, 91 .set_state_oneshot = orion_clkevt_shutdown, 92 .tick_resume = orion_clkevt_shutdown, 93}; 94 95static irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id) 96{ 97 orion_clkevt.event_handler(&orion_clkevt); 98 return IRQ_HANDLED; 99} 100 101static struct irqaction orion_clkevt_irq = { 102 .name = "orion_event", 103 .flags = IRQF_TIMER, 104 .handler = orion_clkevt_irq_handler, 105}; 106 107static void __init orion_timer_init(struct device_node *np) 108{ 109 struct clk *clk; 110 int irq; 111 112 /* timer registers are shared with watchdog timer */ 113 timer_base = of_iomap(np, 0); 114 if (!timer_base) 115 panic("%s: unable to map resource\n", np->name); 116 117 clk = of_clk_get(np, 0); 118 if (IS_ERR(clk)) 119 panic("%s: unable to get clk\n", np->name); 120 clk_prepare_enable(clk); 121 122 /* we are only interested in timer1 irq */ 123 irq = irq_of_parse_and_map(np, 1); 124 if (irq <= 0) 125 panic("%s: unable to parse timer1 irq\n", np->name); 126 127 /* setup timer0 as free-running clocksource */ 128 writel(~0, timer_base + TIMER0_VAL); 129 writel(~0, timer_base + TIMER0_RELOAD); 130 atomic_io_modify(timer_base + TIMER_CTRL, 131 TIMER0_RELOAD_EN | TIMER0_EN, 132 TIMER0_RELOAD_EN | TIMER0_EN); 133 clocksource_mmio_init(timer_base + TIMER0_VAL, "orion_clocksource", 134 clk_get_rate(clk), 300, 32, 135 clocksource_mmio_readl_down); 136 sched_clock_register(orion_read_sched_clock, 32, clk_get_rate(clk)); 137 138 /* setup timer1 as clockevent timer */ 139 if (setup_irq(irq, &orion_clkevt_irq)) 140 panic("%s: unable to setup irq\n", np->name); 141 142 ticks_per_jiffy = (clk_get_rate(clk) + HZ/2) / HZ; 143 orion_clkevt.cpumask = cpumask_of(0); 144 orion_clkevt.irq = irq; 145 clockevents_config_and_register(&orion_clkevt, clk_get_rate(clk), 146 ORION_ONESHOT_MIN, ORION_ONESHOT_MAX); 147} 148CLOCKSOURCE_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init);