linux/arch/arm/mach-s5pv310/time.c
<<
>>
Prefs
   1/* linux/arch/arm/mach-s5pv310/time.c
   2 *
   3 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
   4 *              http://www.samsung.com
   5 *
   6 * S5PV310 (and compatible) HRT support
   7 * PWM 2/4 is used for this feature
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License version 2 as
  11 * published by the Free Software Foundation.
  12*/
  13
  14#include <linux/sched.h>
  15#include <linux/interrupt.h>
  16#include <linux/irq.h>
  17#include <linux/err.h>
  18#include <linux/clk.h>
  19#include <linux/clockchips.h>
  20#include <linux/platform_device.h>
  21
  22#include <asm/smp_twd.h>
  23
  24#include <mach/map.h>
  25#include <plat/regs-timer.h>
  26#include <asm/mach/time.h>
  27
  28static unsigned long clock_count_per_tick;
  29
  30static struct clk *tin2;
  31static struct clk *tin4;
  32static struct clk *tdiv2;
  33static struct clk *tdiv4;
  34static struct clk *timerclk;
  35
  36static void s5pv310_pwm_stop(unsigned int pwm_id)
  37{
  38        unsigned long tcon;
  39
  40        tcon = __raw_readl(S3C2410_TCON);
  41
  42        switch (pwm_id) {
  43        case 2:
  44                tcon &= ~S3C2410_TCON_T2START;
  45                break;
  46        case 4:
  47                tcon &= ~S3C2410_TCON_T4START;
  48                break;
  49        default:
  50                break;
  51        }
  52        __raw_writel(tcon, S3C2410_TCON);
  53}
  54
  55static void s5pv310_pwm_init(unsigned int pwm_id, unsigned long tcnt)
  56{
  57        unsigned long tcon;
  58
  59        tcon = __raw_readl(S3C2410_TCON);
  60
  61        /* timers reload after counting zero, so reduce the count by 1 */
  62        tcnt--;
  63
  64        /* ensure timer is stopped... */
  65        switch (pwm_id) {
  66        case 2:
  67                tcon &= ~(0xf<<12);
  68                tcon |= S3C2410_TCON_T2MANUALUPD;
  69
  70                __raw_writel(tcnt, S3C2410_TCNTB(2));
  71                __raw_writel(tcnt, S3C2410_TCMPB(2));
  72                __raw_writel(tcon, S3C2410_TCON);
  73
  74                break;
  75        case 4:
  76                tcon &= ~(7<<20);
  77                tcon |= S3C2410_TCON_T4MANUALUPD;
  78
  79                __raw_writel(tcnt, S3C2410_TCNTB(4));
  80                __raw_writel(tcnt, S3C2410_TCMPB(4));
  81                __raw_writel(tcon, S3C2410_TCON);
  82
  83                break;
  84        default:
  85                break;
  86        }
  87}
  88
  89static inline void s5pv310_pwm_start(unsigned int pwm_id, bool periodic)
  90{
  91        unsigned long tcon;
  92
  93        tcon  = __raw_readl(S3C2410_TCON);
  94
  95        switch (pwm_id) {
  96        case 2:
  97                tcon |= S3C2410_TCON_T2START;
  98                tcon &= ~S3C2410_TCON_T2MANUALUPD;
  99
 100                if (periodic)
 101                        tcon |= S3C2410_TCON_T2RELOAD;
 102                else
 103                        tcon &= ~S3C2410_TCON_T2RELOAD;
 104                break;
 105        case 4:
 106                tcon |= S3C2410_TCON_T4START;
 107                tcon &= ~S3C2410_TCON_T4MANUALUPD;
 108
 109                if (periodic)
 110                        tcon |= S3C2410_TCON_T4RELOAD;
 111                else
 112                        tcon &= ~S3C2410_TCON_T4RELOAD;
 113                break;
 114        default:
 115                break;
 116        }
 117        __raw_writel(tcon, S3C2410_TCON);
 118}
 119
 120static int s5pv310_pwm_set_next_event(unsigned long cycles,
 121                                        struct clock_event_device *evt)
 122{
 123        s5pv310_pwm_init(2, cycles);
 124        s5pv310_pwm_start(2, 0);
 125        return 0;
 126}
 127
 128static void s5pv310_pwm_set_mode(enum clock_event_mode mode,
 129                                struct clock_event_device *evt)
 130{
 131        s5pv310_pwm_stop(2);
 132
 133        switch (mode) {
 134        case CLOCK_EVT_MODE_PERIODIC:
 135                s5pv310_pwm_init(2, clock_count_per_tick);
 136                s5pv310_pwm_start(2, 1);
 137                break;
 138        case CLOCK_EVT_MODE_ONESHOT:
 139                break;
 140        case CLOCK_EVT_MODE_UNUSED:
 141        case CLOCK_EVT_MODE_SHUTDOWN:
 142        case CLOCK_EVT_MODE_RESUME:
 143                break;
 144        }
 145}
 146
 147static struct clock_event_device pwm_event_device = {
 148        .name           = "pwm_timer2",
 149        .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
 150        .rating         = 200,
 151        .shift          = 32,
 152        .set_next_event = s5pv310_pwm_set_next_event,
 153        .set_mode       = s5pv310_pwm_set_mode,
 154};
 155
 156irqreturn_t s5pv310_clock_event_isr(int irq, void *dev_id)
 157{
 158        struct clock_event_device *evt = &pwm_event_device;
 159
 160        evt->event_handler(evt);
 161
 162        return IRQ_HANDLED;
 163}
 164
 165static struct irqaction s5pv310_clock_event_irq = {
 166        .name           = "pwm_timer2_irq",
 167        .flags          = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
 168        .handler        = s5pv310_clock_event_isr,
 169};
 170
 171static void __init s5pv310_clockevent_init(void)
 172{
 173        unsigned long pclk;
 174        unsigned long clock_rate;
 175        struct clk *tscaler;
 176
 177        pclk = clk_get_rate(timerclk);
 178
 179        /* configure clock tick */
 180
 181        tscaler = clk_get_parent(tdiv2);
 182
 183        clk_set_rate(tscaler, pclk / 2);
 184        clk_set_rate(tdiv2, pclk / 2);
 185        clk_set_parent(tin2, tdiv2);
 186
 187        clock_rate = clk_get_rate(tin2);
 188
 189        clock_count_per_tick = clock_rate / HZ;
 190
 191        pwm_event_device.mult =
 192                div_sc(clock_rate, NSEC_PER_SEC, pwm_event_device.shift);
 193        pwm_event_device.max_delta_ns =
 194                clockevent_delta2ns(-1, &pwm_event_device);
 195        pwm_event_device.min_delta_ns =
 196                clockevent_delta2ns(1, &pwm_event_device);
 197
 198        pwm_event_device.cpumask = cpumask_of(0);
 199        clockevents_register_device(&pwm_event_device);
 200
 201        setup_irq(IRQ_TIMER2, &s5pv310_clock_event_irq);
 202}
 203
 204static cycle_t s5pv310_pwm4_read(struct clocksource *cs)
 205{
 206        return (cycle_t) ~__raw_readl(S3C_TIMERREG(0x40));
 207}
 208
 209struct clocksource pwm_clocksource = {
 210        .name           = "pwm_timer4",
 211        .rating         = 250,
 212        .read           = s5pv310_pwm4_read,
 213        .mask           = CLOCKSOURCE_MASK(32),
 214        .flags          = CLOCK_SOURCE_IS_CONTINUOUS ,
 215};
 216
 217static void __init s5pv310_clocksource_init(void)
 218{
 219        unsigned long pclk;
 220        unsigned long clock_rate;
 221
 222        pclk = clk_get_rate(timerclk);
 223
 224        clk_set_rate(tdiv4, pclk / 2);
 225        clk_set_parent(tin4, tdiv4);
 226
 227        clock_rate = clk_get_rate(tin4);
 228
 229        s5pv310_pwm_init(4, ~0);
 230        s5pv310_pwm_start(4, 1);
 231
 232        if (clocksource_register_hz(&pwm_clocksource, clock_rate))
 233                panic("%s: can't register clocksource\n", pwm_clocksource.name);
 234}
 235
 236static void __init s5pv310_timer_resources(void)
 237{
 238        struct platform_device tmpdev;
 239
 240        tmpdev.dev.bus = &platform_bus_type;
 241
 242        timerclk = clk_get(NULL, "timers");
 243        if (IS_ERR(timerclk))
 244                panic("failed to get timers clock for system timer");
 245
 246        clk_enable(timerclk);
 247
 248        tmpdev.id = 2;
 249        tin2 = clk_get(&tmpdev.dev, "pwm-tin");
 250        if (IS_ERR(tin2))
 251                panic("failed to get pwm-tin2 clock for system timer");
 252
 253        tdiv2 = clk_get(&tmpdev.dev, "pwm-tdiv");
 254        if (IS_ERR(tdiv2))
 255                panic("failed to get pwm-tdiv2 clock for system timer");
 256        clk_enable(tin2);
 257
 258        tmpdev.id = 4;
 259        tin4 = clk_get(&tmpdev.dev, "pwm-tin");
 260        if (IS_ERR(tin4))
 261                panic("failed to get pwm-tin4 clock for system timer");
 262
 263        tdiv4 = clk_get(&tmpdev.dev, "pwm-tdiv");
 264        if (IS_ERR(tdiv4))
 265                panic("failed to get pwm-tdiv4 clock for system timer");
 266
 267        clk_enable(tin4);
 268}
 269
 270static void __init s5pv310_timer_init(void)
 271{
 272#ifdef CONFIG_LOCAL_TIMERS
 273        twd_base = S5P_VA_TWD;
 274#endif
 275
 276        s5pv310_timer_resources();
 277        s5pv310_clockevent_init();
 278        s5pv310_clocksource_init();
 279}
 280
 281struct sys_timer s5pv310_timer = {
 282        .init           = s5pv310_timer_init,
 283};
 284