Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * linux/arch/arm/mach-omap1/timer32k.c
0003  *
0004  * OMAP 32K Timer
0005  *
0006  * Copyright (C) 2004 - 2005 Nokia Corporation
0007  * Partial timer rewrite and additional dynamic tick timer support by
0008  * Tony Lindgen <tony@atomide.com> and
0009  * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
0010  * OMAP Dual-mode timer framework support by Timo Teras
0011  *
0012  * MPU timer code based on the older MPU timer code for OMAP
0013  * Copyright (C) 2000 RidgeRun, Inc.
0014  * Author: Greg Lonnon <glonnon@ridgerun.com>
0015  *
0016  * This program is free software; you can redistribute it and/or modify it
0017  * under the terms of the GNU General Public License as published by the
0018  * Free Software Foundation; either version 2 of the License, or (at your
0019  * option) any later version.
0020  *
0021  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
0022  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
0023  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
0024  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0025  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0026  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0027  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
0028  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0029  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0030  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0031  *
0032  * You should have received a copy of the  GNU General Public License along
0033  * with this program; if not, write  to the Free Software Foundation, Inc.,
0034  * 675 Mass Ave, Cambridge, MA 02139, USA.
0035  */
0036 
0037 #include <linux/kernel.h>
0038 #include <linux/init.h>
0039 #include <linux/delay.h>
0040 #include <linux/interrupt.h>
0041 #include <linux/sched.h>
0042 #include <linux/spinlock.h>
0043 #include <linux/err.h>
0044 #include <linux/clk.h>
0045 #include <linux/clocksource.h>
0046 #include <linux/clockchips.h>
0047 #include <linux/io.h>
0048 #include <linux/sched_clock.h>
0049 
0050 #include <asm/irq.h>
0051 #include <asm/mach/irq.h>
0052 #include <asm/mach/time.h>
0053 
0054 #include "hardware.h"
0055 #include "common.h"
0056 
0057 /*
0058  * ---------------------------------------------------------------------------
0059  * 32KHz OS timer
0060  *
0061  * This currently works only on 16xx, as 1510 does not have the continuous
0062  * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track
0063  * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer
0064  * on 1510 would be possible, but the timer would not be as accurate as
0065  * with the 32KHz synchronized timer.
0066  * ---------------------------------------------------------------------------
0067  */
0068 
0069 /* 16xx specific defines */
0070 #define OMAP1_32K_TIMER_BASE        0xfffb9000
0071 #define OMAP1_32KSYNC_TIMER_BASE    0xfffbc400
0072 #define OMAP1_32K_TIMER_CR      0x08
0073 #define OMAP1_32K_TIMER_TVR     0x00
0074 #define OMAP1_32K_TIMER_TCR     0x04
0075 
0076 #define OMAP_32K_TICKS_PER_SEC      (32768)
0077 
0078 /*
0079  * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1
0080  * so with HZ = 128, TVR = 255.
0081  */
0082 #define OMAP_32K_TIMER_TICK_PERIOD  ((OMAP_32K_TICKS_PER_SEC / HZ) - 1)
0083 
0084 #define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate)         \
0085                 (((nr_jiffies) * (clock_rate)) / HZ)
0086 
0087 static inline void omap_32k_timer_write(int val, int reg)
0088 {
0089     omap_writew(val, OMAP1_32K_TIMER_BASE + reg);
0090 }
0091 
0092 static inline void omap_32k_timer_start(unsigned long load_val)
0093 {
0094     if (!load_val)
0095         load_val = 1;
0096     omap_32k_timer_write(load_val, OMAP1_32K_TIMER_TVR);
0097     omap_32k_timer_write(0x0f, OMAP1_32K_TIMER_CR);
0098 }
0099 
0100 static inline void omap_32k_timer_stop(void)
0101 {
0102     omap_32k_timer_write(0x0, OMAP1_32K_TIMER_CR);
0103 }
0104 
0105 #define omap_32k_timer_ack_irq()
0106 
0107 static int omap_32k_timer_set_next_event(unsigned long delta,
0108                      struct clock_event_device *dev)
0109 {
0110     omap_32k_timer_start(delta);
0111 
0112     return 0;
0113 }
0114 
0115 static int omap_32k_timer_shutdown(struct clock_event_device *evt)
0116 {
0117     omap_32k_timer_stop();
0118     return 0;
0119 }
0120 
0121 static int omap_32k_timer_set_periodic(struct clock_event_device *evt)
0122 {
0123     omap_32k_timer_stop();
0124     omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD);
0125     return 0;
0126 }
0127 
0128 static struct clock_event_device clockevent_32k_timer = {
0129     .name           = "32k-timer",
0130     .features       = CLOCK_EVT_FEAT_PERIODIC |
0131                   CLOCK_EVT_FEAT_ONESHOT,
0132     .set_next_event     = omap_32k_timer_set_next_event,
0133     .set_state_shutdown = omap_32k_timer_shutdown,
0134     .set_state_periodic = omap_32k_timer_set_periodic,
0135     .set_state_oneshot  = omap_32k_timer_shutdown,
0136     .tick_resume        = omap_32k_timer_shutdown,
0137 };
0138 
0139 static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id)
0140 {
0141     struct clock_event_device *evt = &clockevent_32k_timer;
0142     omap_32k_timer_ack_irq();
0143 
0144     evt->event_handler(evt);
0145 
0146     return IRQ_HANDLED;
0147 }
0148 
0149 static __init void omap_init_32k_timer(void)
0150 {
0151     if (request_irq(INT_OS_TIMER, omap_32k_timer_interrupt,
0152             IRQF_TIMER | IRQF_IRQPOLL, "32KHz timer", NULL))
0153         pr_err("Failed to request irq %d(32KHz timer)\n", INT_OS_TIMER);
0154 
0155     clockevent_32k_timer.cpumask = cpumask_of(0);
0156     clockevents_config_and_register(&clockevent_32k_timer,
0157                     OMAP_32K_TICKS_PER_SEC, 1, 0xfffffffe);
0158 }
0159 
0160 /* OMAP2_32KSYNCNT_CR_OFF: offset of 32ksync counter register */
0161 #define OMAP2_32KSYNCNT_REV_OFF     0x0
0162 #define OMAP2_32KSYNCNT_REV_SCHEME  (0x3 << 30)
0163 #define OMAP2_32KSYNCNT_CR_OFF_LOW  0x10
0164 #define OMAP2_32KSYNCNT_CR_OFF_HIGH 0x30
0165 
0166 /*
0167  * 32KHz clocksource ... always available, on pretty most chips except
0168  * OMAP 730 and 1510.  Other timers could be used as clocksources, with
0169  * higher resolution in free-running counter modes (e.g. 12 MHz xtal),
0170  * but systems won't necessarily want to spend resources that way.
0171  */
0172 static void __iomem *sync32k_cnt_reg;
0173 
0174 static u64 notrace omap_32k_read_sched_clock(void)
0175 {
0176     return sync32k_cnt_reg ? readl_relaxed(sync32k_cnt_reg) : 0;
0177 }
0178 
0179 /**
0180  * omap_read_persistent_clock64 -  Return time from a persistent clock.
0181  *
0182  * Reads the time from a source which isn't disabled during PM, the
0183  * 32k sync timer.  Convert the cycles elapsed since last read into
0184  * nsecs and adds to a monotonically increasing timespec64.
0185  */
0186 static struct timespec64 persistent_ts;
0187 static cycles_t cycles;
0188 static unsigned int persistent_mult, persistent_shift;
0189 
0190 static void omap_read_persistent_clock64(struct timespec64 *ts)
0191 {
0192     unsigned long long nsecs;
0193     cycles_t last_cycles;
0194 
0195     last_cycles = cycles;
0196     cycles = sync32k_cnt_reg ? readl_relaxed(sync32k_cnt_reg) : 0;
0197 
0198     nsecs = clocksource_cyc2ns(cycles - last_cycles,
0199                     persistent_mult, persistent_shift);
0200 
0201     timespec64_add_ns(&persistent_ts, nsecs);
0202 
0203     *ts = persistent_ts;
0204 }
0205 
0206 /**
0207  * omap_init_clocksource_32k - setup and register counter 32k as a
0208  * kernel clocksource
0209  * @pbase: base addr of counter_32k module
0210  * @size: size of counter_32k to map
0211  *
0212  * Returns 0 upon success or negative error code upon failure.
0213  *
0214  */
0215 static int __init omap_init_clocksource_32k(void __iomem *vbase)
0216 {
0217     int ret;
0218 
0219     /*
0220      * 32k sync Counter IP register offsets vary between the
0221      * highlander version and the legacy ones.
0222      * The 'SCHEME' bits(30-31) of the revision register is used
0223      * to identify the version.
0224      */
0225     if (readl_relaxed(vbase + OMAP2_32KSYNCNT_REV_OFF) &
0226                         OMAP2_32KSYNCNT_REV_SCHEME)
0227         sync32k_cnt_reg = vbase + OMAP2_32KSYNCNT_CR_OFF_HIGH;
0228     else
0229         sync32k_cnt_reg = vbase + OMAP2_32KSYNCNT_CR_OFF_LOW;
0230 
0231     /*
0232      * 120000 rough estimate from the calculations in
0233      * __clocksource_update_freq_scale.
0234      */
0235     clocks_calc_mult_shift(&persistent_mult, &persistent_shift,
0236             32768, NSEC_PER_SEC, 120000);
0237 
0238     ret = clocksource_mmio_init(sync32k_cnt_reg, "32k_counter", 32768,
0239                 250, 32, clocksource_mmio_readl_up);
0240     if (ret) {
0241         pr_err("32k_counter: can't register clocksource\n");
0242         return ret;
0243     }
0244 
0245     sched_clock_register(omap_32k_read_sched_clock, 32, 32768);
0246     register_persistent_clock(omap_read_persistent_clock64);
0247     pr_info("OMAP clocksource: 32k_counter at 32768 Hz\n");
0248 
0249     return 0;
0250 }
0251 
0252 /*
0253  * ---------------------------------------------------------------------------
0254  * Timer initialization
0255  * ---------------------------------------------------------------------------
0256  */
0257 int __init omap_32k_timer_init(void)
0258 {
0259     int ret = -ENODEV;
0260 
0261     if (cpu_is_omap16xx()) {
0262         void __iomem *base;
0263         struct clk *sync32k_ick;
0264 
0265         base = ioremap(OMAP1_32KSYNC_TIMER_BASE, SZ_1K);
0266         if (!base) {
0267             pr_err("32k_counter: failed to map base addr\n");
0268             return -ENODEV;
0269         }
0270 
0271         sync32k_ick = clk_get(NULL, "omap_32ksync_ick");
0272         if (!IS_ERR(sync32k_ick))
0273             clk_prepare_enable(sync32k_ick);
0274 
0275         ret = omap_init_clocksource_32k(base);
0276     }
0277 
0278     if (!ret)
0279         omap_init_32k_timer();
0280 
0281     return ret;
0282 }