1/* 2 * linux/arch/arm/mach-w90x900/time.c 3 * 4 * Based on linux/arch/arm/plat-s3c24xx/time.c by Ben Dooks 5 * 6 * Copyright (c) 2009 Nuvoton technology corporation 7 * All rights reserved. 8 * 9 * Wan ZongShun <mcuos.com@gmail.com> 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation; either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 */ 17 18#include <linux/kernel.h> 19#include <linux/sched.h> 20#include <linux/init.h> 21#include <linux/interrupt.h> 22#include <linux/err.h> 23#include <linux/clk.h> 24#include <linux/io.h> 25#include <linux/leds.h> 26#include <linux/clocksource.h> 27#include <linux/clockchips.h> 28 29#include <asm/mach-types.h> 30#include <asm/mach/irq.h> 31#include <asm/mach/time.h> 32 33#include <mach/map.h> 34#include <mach/regs-timer.h> 35 36#define RESETINT 0x1f 37#define PERIOD (0x01 << 27) 38#define ONESHOT (0x00 << 27) 39#define COUNTEN (0x01 << 30) 40#define INTEN (0x01 << 29) 41 42#define TICKS_PER_SEC 100 43#define PRESCALE 0x63 /* Divider = prescale + 1 */ 44 45#define TDR_SHIFT 24 46#define TDR_MASK ((1 << TDR_SHIFT) - 1) 47 48static unsigned int timer0_load; 49 50static void nuc900_clockevent_setmode(enum clock_event_mode mode, 51 struct clock_event_device *clk) 52{ 53 unsigned int val; 54 55 val = __raw_readl(REG_TCSR0); 56 val &= ~(0x03 << 27); 57 58 switch (mode) { 59 case CLOCK_EVT_MODE_PERIODIC: 60 __raw_writel(timer0_load, REG_TICR0); 61 val |= (PERIOD | COUNTEN | INTEN | PRESCALE); 62 break; 63 64 case CLOCK_EVT_MODE_ONESHOT: 65 val |= (ONESHOT | COUNTEN | INTEN | PRESCALE); 66 break; 67 68 case CLOCK_EVT_MODE_UNUSED: 69 case CLOCK_EVT_MODE_SHUTDOWN: 70 case CLOCK_EVT_MODE_RESUME: 71 break; 72 } 73 74 __raw_writel(val, REG_TCSR0); 75} 76 77static int nuc900_clockevent_setnextevent(unsigned long evt, 78 struct clock_event_device *clk) 79{ 80 unsigned int val; 81 82 __raw_writel(evt, REG_TICR0); 83 84 val = __raw_readl(REG_TCSR0); 85 val |= (COUNTEN | INTEN | PRESCALE); 86 __raw_writel(val, REG_TCSR0); 87 88 return 0; 89} 90 91static struct clock_event_device nuc900_clockevent_device = { 92 .name = "nuc900-timer0", 93 .shift = 32, 94 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 95 .set_mode = nuc900_clockevent_setmode, 96 .set_next_event = nuc900_clockevent_setnextevent, 97 .rating = 300, 98}; 99 100/*IRQ handler for the timer*/ 101 102static irqreturn_t nuc900_timer0_interrupt(int irq, void *dev_id) 103{ 104 struct clock_event_device *evt = &nuc900_clockevent_device; 105 106 __raw_writel(0x01, REG_TISR); /* clear TIF0 */ 107 108 evt->event_handler(evt); 109 return IRQ_HANDLED; 110} 111 112static struct irqaction nuc900_timer0_irq = { 113 .name = "nuc900-timer0", 114 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, 115 .handler = nuc900_timer0_interrupt, 116}; 117 118static void __init nuc900_clockevents_init(void) 119{ 120 unsigned int rate; 121 struct clk *clk = clk_get(NULL, "timer0"); 122 123 BUG_ON(IS_ERR(clk)); 124 125 __raw_writel(0x00, REG_TCSR0); 126 127 clk_enable(clk); 128 rate = clk_get_rate(clk) / (PRESCALE + 1); 129 130 timer0_load = (rate / TICKS_PER_SEC); 131 132 __raw_writel(RESETINT, REG_TISR); 133 setup_irq(IRQ_TIMER0, &nuc900_timer0_irq); 134 135 nuc900_clockevent_device.mult = div_sc(rate, NSEC_PER_SEC, 136 nuc900_clockevent_device.shift); 137 nuc900_clockevent_device.max_delta_ns = clockevent_delta2ns(0xffffffff, 138 &nuc900_clockevent_device); 139 nuc900_clockevent_device.min_delta_ns = clockevent_delta2ns(0xf, 140 &nuc900_clockevent_device); 141 nuc900_clockevent_device.cpumask = cpumask_of(0); 142 143 clockevents_register_device(&nuc900_clockevent_device); 144} 145 146static cycle_t nuc900_get_cycles(struct clocksource *cs) 147{ 148 return (~__raw_readl(REG_TDR1)) & TDR_MASK; 149} 150 151static struct clocksource clocksource_nuc900 = { 152 .name = "nuc900-timer1", 153 .rating = 200, 154 .read = nuc900_get_cycles, 155 .mask = CLOCKSOURCE_MASK(TDR_SHIFT), 156 .shift = 10, 157 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 158}; 159 160static void __init nuc900_clocksource_init(void) 161{ 162 unsigned int val; 163 unsigned int rate; 164 struct clk *clk = clk_get(NULL, "timer1"); 165 166 BUG_ON(IS_ERR(clk)); 167 168 __raw_writel(0x00, REG_TCSR1); 169 170 clk_enable(clk); 171 rate = clk_get_rate(clk) / (PRESCALE + 1); 172 173 __raw_writel(0xffffffff, REG_TICR1); 174 175 val = __raw_readl(REG_TCSR1); 176 val |= (COUNTEN | PERIOD | PRESCALE); 177 __raw_writel(val, REG_TCSR1); 178 179 clocksource_nuc900.mult = 180 clocksource_khz2mult((rate / 1000), clocksource_nuc900.shift); 181 clocksource_register(&clocksource_nuc900); 182} 183 184static void __init nuc900_timer_init(void) 185{ 186 nuc900_clocksource_init(); 187 nuc900_clockevents_init(); 188} 189 190struct sys_timer nuc900_timer = { 191 .init = nuc900_timer_init, 192}; 193