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#include "nuc9xx.h" 37 38#define RESETINT 0x1f 39#define PERIOD (0x01 << 27) 40#define ONESHOT (0x00 << 27) 41#define COUNTEN (0x01 << 30) 42#define INTEN (0x01 << 29) 43 44#define TICKS_PER_SEC 100 45#define PRESCALE 0x63 /* Divider = prescale + 1 */ 46 47#define TDR_SHIFT 24 48 49static unsigned int timer0_load; 50 51static void nuc900_clockevent_setmode(enum clock_event_mode mode, 52 struct clock_event_device *clk) 53{ 54 unsigned int val; 55 56 val = __raw_readl(REG_TCSR0); 57 val &= ~(0x03 << 27); 58 59 switch (mode) { 60 case CLOCK_EVT_MODE_PERIODIC: 61 __raw_writel(timer0_load, REG_TICR0); 62 val |= (PERIOD | COUNTEN | INTEN | PRESCALE); 63 break; 64 65 case CLOCK_EVT_MODE_ONESHOT: 66 val |= (ONESHOT | COUNTEN | INTEN | PRESCALE); 67 break; 68 69 case CLOCK_EVT_MODE_UNUSED: 70 case CLOCK_EVT_MODE_SHUTDOWN: 71 case CLOCK_EVT_MODE_RESUME: 72 break; 73 } 74 75 __raw_writel(val, REG_TCSR0); 76} 77 78static int nuc900_clockevent_setnextevent(unsigned long evt, 79 struct clock_event_device *clk) 80{ 81 unsigned int val; 82 83 __raw_writel(evt, REG_TICR0); 84 85 val = __raw_readl(REG_TCSR0); 86 val |= (COUNTEN | INTEN | PRESCALE); 87 __raw_writel(val, REG_TCSR0); 88 89 return 0; 90} 91 92static struct clock_event_device nuc900_clockevent_device = { 93 .name = "nuc900-timer0", 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_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.cpumask = cpumask_of(0); 136 137 clockevents_config_and_register(&nuc900_clockevent_device, rate, 138 0xf, 0xffffffff); 139} 140 141static void __init nuc900_clocksource_init(void) 142{ 143 unsigned int val; 144 unsigned int rate; 145 struct clk *clk = clk_get(NULL, "timer1"); 146 147 BUG_ON(IS_ERR(clk)); 148 149 __raw_writel(0x00, REG_TCSR1); 150 151 clk_enable(clk); 152 rate = clk_get_rate(clk) / (PRESCALE + 1); 153 154 __raw_writel(0xffffffff, REG_TICR1); 155 156 val = __raw_readl(REG_TCSR1); 157 val |= (COUNTEN | PERIOD | PRESCALE); 158 __raw_writel(val, REG_TCSR1); 159 160 clocksource_mmio_init(REG_TDR1, "nuc900-timer1", rate, 200, 161 TDR_SHIFT, clocksource_mmio_readl_down); 162} 163 164void __init nuc900_timer_init(void) 165{ 166 nuc900_clocksource_init(); 167 nuc900_clockevents_init(); 168} 169