1/* 2 * arch/arm/mach-ns9xxx/time-ns9360.c 3 * 4 * Copyright (C) 2006,2007 by Digi International Inc. 5 * All rights reserved. 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation. 10 */ 11#include <linux/jiffies.h> 12#include <linux/interrupt.h> 13#include <linux/irq.h> 14#include <linux/stringify.h> 15#include <linux/clocksource.h> 16#include <linux/clockchips.h> 17 18#include <mach/processor-ns9360.h> 19#include <mach/regs-sys-ns9360.h> 20#include <mach/irqs.h> 21#include <mach/system.h> 22#include "generic.h" 23 24#define TIMER_CLOCKSOURCE 0 25#define TIMER_CLOCKEVENT 1 26static u32 latch; 27 28static cycle_t ns9360_clocksource_read(struct clocksource *cs) 29{ 30 return __raw_readl(SYS_TR(TIMER_CLOCKSOURCE)); 31} 32 33static struct clocksource ns9360_clocksource = { 34 .name = "ns9360-timer" __stringify(TIMER_CLOCKSOURCE), 35 .rating = 300, 36 .read = ns9360_clocksource_read, 37 .mask = CLOCKSOURCE_MASK(32), 38 .shift = 20, 39 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 40}; 41 42static void ns9360_clockevent_setmode(enum clock_event_mode mode, 43 struct clock_event_device *clk) 44{ 45 u32 tc = __raw_readl(SYS_TC(TIMER_CLOCKEVENT)); 46 47 switch (mode) { 48 case CLOCK_EVT_MODE_PERIODIC: 49 __raw_writel(latch, SYS_TRC(TIMER_CLOCKEVENT)); 50 REGSET(tc, SYS_TCx, REN, EN); 51 REGSET(tc, SYS_TCx, INTS, EN); 52 REGSET(tc, SYS_TCx, TEN, EN); 53 break; 54 55 case CLOCK_EVT_MODE_ONESHOT: 56 REGSET(tc, SYS_TCx, REN, DIS); 57 REGSET(tc, SYS_TCx, INTS, EN); 58 59 /* fall through */ 60 61 case CLOCK_EVT_MODE_UNUSED: 62 case CLOCK_EVT_MODE_SHUTDOWN: 63 case CLOCK_EVT_MODE_RESUME: 64 default: 65 REGSET(tc, SYS_TCx, TEN, DIS); 66 break; 67 } 68 69 __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT)); 70} 71 72static int ns9360_clockevent_setnextevent(unsigned long evt, 73 struct clock_event_device *clk) 74{ 75 u32 tc = __raw_readl(SYS_TC(TIMER_CLOCKEVENT)); 76 77 if (REGGET(tc, SYS_TCx, TEN)) { 78 REGSET(tc, SYS_TCx, TEN, DIS); 79 __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT)); 80 } 81 82 REGSET(tc, SYS_TCx, TEN, EN); 83 84 __raw_writel(evt, SYS_TRC(TIMER_CLOCKEVENT)); 85 86 __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT)); 87 88 return 0; 89} 90 91static struct clock_event_device ns9360_clockevent_device = { 92 .name = "ns9360-timer" __stringify(TIMER_CLOCKEVENT), 93 .shift = 20, 94 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 95 .set_mode = ns9360_clockevent_setmode, 96 .set_next_event = ns9360_clockevent_setnextevent, 97}; 98 99static irqreturn_t ns9360_clockevent_handler(int irq, void *dev_id) 100{ 101 int timerno = irq - IRQ_NS9360_TIMER0; 102 u32 tc; 103 104 struct clock_event_device *evt = &ns9360_clockevent_device; 105 106 /* clear irq */ 107 tc = __raw_readl(SYS_TC(timerno)); 108 if (REGGET(tc, SYS_TCx, REN) == SYS_TCx_REN_DIS) { 109 REGSET(tc, SYS_TCx, TEN, DIS); 110 __raw_writel(tc, SYS_TC(timerno)); 111 } 112 REGSET(tc, SYS_TCx, INTC, SET); 113 __raw_writel(tc, SYS_TC(timerno)); 114 REGSET(tc, SYS_TCx, INTC, UNSET); 115 __raw_writel(tc, SYS_TC(timerno)); 116 117 evt->event_handler(evt); 118 119 return IRQ_HANDLED; 120} 121 122static struct irqaction ns9360_clockevent_action = { 123 .name = "ns9360-timer" __stringify(TIMER_CLOCKEVENT), 124 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, 125 .handler = ns9360_clockevent_handler, 126}; 127 128static void __init ns9360_timer_init(void) 129{ 130 int tc; 131 132 tc = __raw_readl(SYS_TC(TIMER_CLOCKSOURCE)); 133 if (REGGET(tc, SYS_TCx, TEN)) { 134 REGSET(tc, SYS_TCx, TEN, DIS); 135 __raw_writel(tc, SYS_TC(TIMER_CLOCKSOURCE)); 136 } 137 138 __raw_writel(0, SYS_TRC(TIMER_CLOCKSOURCE)); 139 140 REGSET(tc, SYS_TCx, TEN, EN); 141 REGSET(tc, SYS_TCx, TDBG, STOP); 142 REGSET(tc, SYS_TCx, TLCS, CPU); 143 REGSET(tc, SYS_TCx, TM, IEE); 144 REGSET(tc, SYS_TCx, INTS, DIS); 145 REGSET(tc, SYS_TCx, UDS, UP); 146 REGSET(tc, SYS_TCx, TSZ, 32); 147 REGSET(tc, SYS_TCx, REN, EN); 148 149 __raw_writel(tc, SYS_TC(TIMER_CLOCKSOURCE)); 150 151 ns9360_clocksource.mult = clocksource_hz2mult(ns9360_cpuclock(), 152 ns9360_clocksource.shift); 153 154 clocksource_register(&ns9360_clocksource); 155 156 latch = SH_DIV(ns9360_cpuclock(), HZ, 0); 157 158 tc = __raw_readl(SYS_TC(TIMER_CLOCKEVENT)); 159 REGSET(tc, SYS_TCx, TEN, DIS); 160 REGSET(tc, SYS_TCx, TDBG, STOP); 161 REGSET(tc, SYS_TCx, TLCS, CPU); 162 REGSET(tc, SYS_TCx, TM, IEE); 163 REGSET(tc, SYS_TCx, INTS, DIS); 164 REGSET(tc, SYS_TCx, UDS, DOWN); 165 REGSET(tc, SYS_TCx, TSZ, 32); 166 REGSET(tc, SYS_TCx, REN, EN); 167 __raw_writel(tc, SYS_TC(TIMER_CLOCKEVENT)); 168 169 ns9360_clockevent_device.mult = div_sc(ns9360_cpuclock(), 170 NSEC_PER_SEC, ns9360_clockevent_device.shift); 171 ns9360_clockevent_device.max_delta_ns = 172 clockevent_delta2ns(-1, &ns9360_clockevent_device); 173 ns9360_clockevent_device.min_delta_ns = 174 clockevent_delta2ns(1, &ns9360_clockevent_device); 175 176 ns9360_clockevent_device.cpumask = cpumask_of(0); 177 clockevents_register_device(&ns9360_clockevent_device); 178 179 setup_irq(IRQ_NS9360_TIMER0 + TIMER_CLOCKEVENT, 180 &ns9360_clockevent_action); 181} 182 183struct sys_timer ns9360_timer = { 184 .init = ns9360_timer_init, 185}; 186