| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | *  linux/arch/arm/mach-nomadik/timer.c | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2008 STMicroelectronics | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 5 | * Copyright (C) 2010 Alessandro Rubini | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 6 | * | 
|  | 7 | * This program is free software; you can redistribute it and/or modify | 
|  | 8 | * it under the terms of the GNU General Public License version 2, as | 
|  | 9 | * published by the Free Software Foundation. | 
|  | 10 | */ | 
|  | 11 | #include <linux/init.h> | 
|  | 12 | #include <linux/interrupt.h> | 
|  | 13 | #include <linux/irq.h> | 
|  | 14 | #include <linux/io.h> | 
|  | 15 | #include <linux/clockchips.h> | 
| Linus Walleij | ba327b1 | 2010-05-26 07:38:54 +0100 | [diff] [blame] | 16 | #include <linux/clk.h> | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 17 | #include <linux/jiffies.h> | 
| Linus Walleij | ba327b1 | 2010-05-26 07:38:54 +0100 | [diff] [blame] | 18 | #include <linux/err.h> | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 19 | #include <asm/mach/time.h> | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 20 |  | 
| Srinidhi Kasagar | 59b559d | 2009-11-12 06:20:54 +0100 | [diff] [blame] | 21 | #include <plat/mtu.h> | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 22 |  | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 23 | void __iomem *mtu_base; /* ssigned by machine code */ | 
| Srinidhi Kasagar | 59b559d | 2009-11-12 06:20:54 +0100 | [diff] [blame] | 24 |  | 
| Linus Walleij | 2a84751 | 2010-05-07 10:03:02 +0100 | [diff] [blame] | 25 | /* | 
|  | 26 | * Kernel assumes that sched_clock can be called early | 
|  | 27 | * but the MTU may not yet be initialized. | 
|  | 28 | */ | 
|  | 29 | static cycle_t nmdk_read_timer_dummy(struct clocksource *cs) | 
|  | 30 | { | 
|  | 31 | return 0; | 
|  | 32 | } | 
|  | 33 |  | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 34 | /* clocksource: MTU decrements, so we negate the value being read. */ | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 35 | static cycle_t nmdk_read_timer(struct clocksource *cs) | 
|  | 36 | { | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 37 | return -readl(mtu_base + MTU_VAL(0)); | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 38 | } | 
|  | 39 |  | 
|  | 40 | static struct clocksource nmdk_clksrc = { | 
|  | 41 | .name		= "mtu_0", | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 42 | .rating		= 200, | 
| Linus Walleij | 2a84751 | 2010-05-07 10:03:02 +0100 | [diff] [blame] | 43 | .read		= nmdk_read_timer_dummy, | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 44 | .mask		= CLOCKSOURCE_MASK(32), | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 45 | .flags		= CLOCK_SOURCE_IS_CONTINUOUS, | 
|  | 46 | }; | 
|  | 47 |  | 
| Linus Walleij | 2a84751 | 2010-05-07 10:03:02 +0100 | [diff] [blame] | 48 | /* | 
|  | 49 | * Override the global weak sched_clock symbol with this | 
|  | 50 | * local implementation which uses the clocksource to get some | 
|  | 51 | * better resolution when scheduling the kernel. We accept that | 
|  | 52 | * this wraps around for now, since it is just a relative time | 
|  | 53 | * stamp. (Inspired by OMAP implementation.) | 
|  | 54 | */ | 
|  | 55 | unsigned long long notrace sched_clock(void) | 
|  | 56 | { | 
|  | 57 | return clocksource_cyc2ns(nmdk_clksrc.read( | 
|  | 58 | &nmdk_clksrc), | 
|  | 59 | nmdk_clksrc.mult, | 
|  | 60 | nmdk_clksrc.shift); | 
|  | 61 | } | 
|  | 62 |  | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 63 | /* Clockevent device: use one-shot mode */ | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 64 | static void nmdk_clkevt_mode(enum clock_event_mode mode, | 
|  | 65 | struct clock_event_device *dev) | 
|  | 66 | { | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 67 | u32 cr; | 
|  | 68 |  | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 69 | switch (mode) { | 
|  | 70 | case CLOCK_EVT_MODE_PERIODIC: | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 71 | pr_err("%s: periodic mode not supported\n", __func__); | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 72 | break; | 
|  | 73 | case CLOCK_EVT_MODE_ONESHOT: | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 74 | /* Load highest value, enable device, enable interrupts */ | 
|  | 75 | cr = readl(mtu_base + MTU_CR(1)); | 
|  | 76 | writel(0, mtu_base + MTU_LR(1)); | 
|  | 77 | writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(1)); | 
|  | 78 | writel(0x2, mtu_base + MTU_IMSC); | 
|  | 79 | break; | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 80 | case CLOCK_EVT_MODE_SHUTDOWN: | 
|  | 81 | case CLOCK_EVT_MODE_UNUSED: | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 82 | /* disable irq */ | 
|  | 83 | writel(0, mtu_base + MTU_IMSC); | 
| Linus Walleij | 2917947 | 2010-06-01 08:26:49 +0100 | [diff] [blame] | 84 | /* disable timer */ | 
|  | 85 | cr = readl(mtu_base + MTU_CR(1)); | 
|  | 86 | cr &= ~MTU_CRn_ENA; | 
|  | 87 | writel(cr, mtu_base + MTU_CR(1)); | 
|  | 88 | /* load some high default value */ | 
|  | 89 | writel(0xffffffff, mtu_base + MTU_LR(1)); | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 90 | break; | 
|  | 91 | case CLOCK_EVT_MODE_RESUME: | 
|  | 92 | break; | 
|  | 93 | } | 
|  | 94 | } | 
|  | 95 |  | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 96 | static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev) | 
|  | 97 | { | 
|  | 98 | /* writing the value has immediate effect */ | 
|  | 99 | writel(evt, mtu_base + MTU_LR(1)); | 
|  | 100 | return 0; | 
|  | 101 | } | 
|  | 102 |  | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 103 | static struct clock_event_device nmdk_clkevt = { | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 104 | .name		= "mtu_1", | 
|  | 105 | .features	= CLOCK_EVT_FEAT_ONESHOT, | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 106 | .rating		= 200, | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 107 | .set_mode	= nmdk_clkevt_mode, | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 108 | .set_next_event	= nmdk_clkevt_next, | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 109 | }; | 
|  | 110 |  | 
|  | 111 | /* | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 112 | * IRQ Handler for timer 1 of the MTU block. | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 113 | */ | 
|  | 114 | static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id) | 
|  | 115 | { | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 116 | struct clock_event_device *evdev = dev_id; | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 117 |  | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 118 | writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */ | 
|  | 119 | evdev->event_handler(evdev); | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 120 | return IRQ_HANDLED; | 
|  | 121 | } | 
|  | 122 |  | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 123 | static struct irqaction nmdk_timer_irq = { | 
|  | 124 | .name		= "Nomadik Timer Tick", | 
|  | 125 | .flags		= IRQF_DISABLED | IRQF_TIMER, | 
|  | 126 | .handler	= nmdk_timer_interrupt, | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 127 | .dev_id		= &nmdk_clkevt, | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 128 | }; | 
|  | 129 |  | 
| Srinidhi Kasagar | 59b559d | 2009-11-12 06:20:54 +0100 | [diff] [blame] | 130 | void __init nmdk_timer_init(void) | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 131 | { | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 132 | unsigned long rate; | 
| Linus Walleij | ba327b1 | 2010-05-26 07:38:54 +0100 | [diff] [blame] | 133 | struct clk *clk0; | 
|  | 134 | struct clk *clk1; | 
|  | 135 | u32 cr; | 
|  | 136 |  | 
|  | 137 | clk0 = clk_get_sys("mtu0", NULL); | 
|  | 138 | BUG_ON(IS_ERR(clk0)); | 
|  | 139 |  | 
|  | 140 | clk1 = clk_get_sys("mtu1", NULL); | 
|  | 141 | BUG_ON(IS_ERR(clk1)); | 
|  | 142 |  | 
|  | 143 | clk_enable(clk0); | 
|  | 144 | clk_enable(clk1); | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 145 |  | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 146 | /* | 
|  | 147 | * Tick rate is 2.4MHz for Nomadik and 110MHz for ux500: | 
|  | 148 | * use a divide-by-16 counter if it's more than 16MHz | 
|  | 149 | */ | 
| Linus Walleij | ba327b1 | 2010-05-26 07:38:54 +0100 | [diff] [blame] | 150 | cr = MTU_CRn_32BITS;; | 
|  | 151 | rate = clk_get_rate(clk0); | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 152 | if (rate > 16 << 20) { | 
|  | 153 | rate /= 16; | 
|  | 154 | cr |= MTU_CRn_PRESCALE_16; | 
|  | 155 | } else { | 
|  | 156 | cr |= MTU_CRn_PRESCALE_1; | 
|  | 157 | } | 
| Linus Walleij | 2917947 | 2010-06-01 08:26:49 +0100 | [diff] [blame] | 158 | clocksource_calc_mult_shift(&nmdk_clksrc, rate, MTU_MIN_RANGE); | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 159 |  | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 160 | /* Timer 0 is the free running clocksource */ | 
|  | 161 | writel(cr, mtu_base + MTU_CR(0)); | 
|  | 162 | writel(0, mtu_base + MTU_LR(0)); | 
|  | 163 | writel(0, mtu_base + MTU_BGLR(0)); | 
|  | 164 | writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0)); | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 165 |  | 
| Linus Walleij | 2a84751 | 2010-05-07 10:03:02 +0100 | [diff] [blame] | 166 | /* Now the scheduling clock is ready */ | 
|  | 167 | nmdk_clksrc.read = nmdk_read_timer; | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 168 |  | 
| Srinidhi Kasagar | 59b559d | 2009-11-12 06:20:54 +0100 | [diff] [blame] | 169 | if (clocksource_register(&nmdk_clksrc)) | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 170 | pr_err("timer: failed to initialize clock source %s\n", | 
|  | 171 | nmdk_clksrc.name); | 
|  | 172 |  | 
|  | 173 | /* Timer 1 is used for events, fix according to rate */ | 
| Linus Walleij | ba327b1 | 2010-05-26 07:38:54 +0100 | [diff] [blame] | 174 | cr = MTU_CRn_32BITS; | 
|  | 175 | rate = clk_get_rate(clk1); | 
|  | 176 | if (rate > 16 << 20) { | 
|  | 177 | rate /= 16; | 
|  | 178 | cr |= MTU_CRn_PRESCALE_16; | 
|  | 179 | } else { | 
|  | 180 | cr |= MTU_CRn_PRESCALE_1; | 
|  | 181 | } | 
| Linus Walleij | 2917947 | 2010-06-01 08:26:49 +0100 | [diff] [blame] | 182 | clockevents_calc_mult_shift(&nmdk_clkevt, rate, MTU_MIN_RANGE); | 
|  | 183 |  | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 184 | writel(cr | MTU_CRn_ONESHOT, mtu_base + MTU_CR(1)); /* off, currently */ | 
| Linus Walleij | 2917947 | 2010-06-01 08:26:49 +0100 | [diff] [blame] | 185 |  | 
| Alessandro Rubini | b102c01 | 2010-03-05 12:38:51 +0100 | [diff] [blame] | 186 | nmdk_clkevt.max_delta_ns = | 
|  | 187 | clockevent_delta2ns(0xffffffff, &nmdk_clkevt); | 
|  | 188 | nmdk_clkevt.min_delta_ns = | 
|  | 189 | clockevent_delta2ns(0x00000002, &nmdk_clkevt); | 
|  | 190 | nmdk_clkevt.cpumask	= cpumask_of(0); | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 191 |  | 
|  | 192 | /* Register irq and clockevents */ | 
|  | 193 | setup_irq(IRQ_MTU0, &nmdk_timer_irq); | 
| Alessandro Rubini | 28ad94e | 2009-07-02 19:06:47 +0100 | [diff] [blame] | 194 | clockevents_register_device(&nmdk_clkevt); | 
|  | 195 | } |