136ca09beSDongsheng.wang@freescale.com /* 236ca09beSDongsheng.wang@freescale.com * MPIC timer driver 336ca09beSDongsheng.wang@freescale.com * 436ca09beSDongsheng.wang@freescale.com * Copyright 2013 Freescale Semiconductor, Inc. 536ca09beSDongsheng.wang@freescale.com * Author: Dongsheng Wang <Dongsheng.Wang@freescale.com> 636ca09beSDongsheng.wang@freescale.com * Li Yang <leoli@freescale.com> 736ca09beSDongsheng.wang@freescale.com * 836ca09beSDongsheng.wang@freescale.com * This program is free software; you can redistribute it and/or modify it 936ca09beSDongsheng.wang@freescale.com * under the terms of the GNU General Public License as published by the 1036ca09beSDongsheng.wang@freescale.com * Free Software Foundation; either version 2 of the License, or (at your 1136ca09beSDongsheng.wang@freescale.com * option) any later version. 1236ca09beSDongsheng.wang@freescale.com */ 1336ca09beSDongsheng.wang@freescale.com 1436ca09beSDongsheng.wang@freescale.com #include <linux/kernel.h> 1536ca09beSDongsheng.wang@freescale.com #include <linux/init.h> 1636ca09beSDongsheng.wang@freescale.com #include <linux/module.h> 1736ca09beSDongsheng.wang@freescale.com #include <linux/errno.h> 1836ca09beSDongsheng.wang@freescale.com #include <linux/mm.h> 1936ca09beSDongsheng.wang@freescale.com #include <linux/interrupt.h> 2036ca09beSDongsheng.wang@freescale.com #include <linux/slab.h> 2136ca09beSDongsheng.wang@freescale.com #include <linux/of.h> 2226a2056eSRob Herring #include <linux/of_address.h> 2336ca09beSDongsheng.wang@freescale.com #include <linux/of_device.h> 2426a2056eSRob Herring #include <linux/of_irq.h> 2536ca09beSDongsheng.wang@freescale.com #include <linux/syscore_ops.h> 2636ca09beSDongsheng.wang@freescale.com #include <sysdev/fsl_soc.h> 2736ca09beSDongsheng.wang@freescale.com #include <asm/io.h> 2836ca09beSDongsheng.wang@freescale.com 2936ca09beSDongsheng.wang@freescale.com #include <asm/mpic_timer.h> 3036ca09beSDongsheng.wang@freescale.com 3136ca09beSDongsheng.wang@freescale.com #define FSL_GLOBAL_TIMER 0x1 3236ca09beSDongsheng.wang@freescale.com 3336ca09beSDongsheng.wang@freescale.com /* Clock Ratio 3436ca09beSDongsheng.wang@freescale.com * Divide by 64 0x00000300 3536ca09beSDongsheng.wang@freescale.com * Divide by 32 0x00000200 3636ca09beSDongsheng.wang@freescale.com * Divide by 16 0x00000100 3736ca09beSDongsheng.wang@freescale.com * Divide by 8 0x00000000 (Hardware default div) 3836ca09beSDongsheng.wang@freescale.com */ 3936ca09beSDongsheng.wang@freescale.com #define MPIC_TIMER_TCR_CLKDIV 0x00000300 4036ca09beSDongsheng.wang@freescale.com 4136ca09beSDongsheng.wang@freescale.com #define MPIC_TIMER_TCR_ROVR_OFFSET 24 4236ca09beSDongsheng.wang@freescale.com 4336ca09beSDongsheng.wang@freescale.com #define TIMER_STOP 0x80000000 440fd79588SWang Dongsheng #define GTCCR_TOG 0x80000000 4536ca09beSDongsheng.wang@freescale.com #define TIMERS_PER_GROUP 4 4636ca09beSDongsheng.wang@freescale.com #define MAX_TICKS (~0U >> 1) 4736ca09beSDongsheng.wang@freescale.com #define MAX_TICKS_CASCADE (~0U) 4836ca09beSDongsheng.wang@freescale.com #define TIMER_OFFSET(num) (1 << (TIMERS_PER_GROUP - 1 - num)) 4936ca09beSDongsheng.wang@freescale.com 5036ca09beSDongsheng.wang@freescale.com /* tv_usec should be less than ONE_SECOND, otherwise use tv_sec */ 5136ca09beSDongsheng.wang@freescale.com #define ONE_SECOND 1000000 5236ca09beSDongsheng.wang@freescale.com 5336ca09beSDongsheng.wang@freescale.com struct timer_regs { 5436ca09beSDongsheng.wang@freescale.com u32 gtccr; 5536ca09beSDongsheng.wang@freescale.com u32 res0[3]; 5636ca09beSDongsheng.wang@freescale.com u32 gtbcr; 5736ca09beSDongsheng.wang@freescale.com u32 res1[3]; 5836ca09beSDongsheng.wang@freescale.com u32 gtvpr; 5936ca09beSDongsheng.wang@freescale.com u32 res2[3]; 6036ca09beSDongsheng.wang@freescale.com u32 gtdr; 6136ca09beSDongsheng.wang@freescale.com u32 res3[3]; 6236ca09beSDongsheng.wang@freescale.com }; 6336ca09beSDongsheng.wang@freescale.com 6436ca09beSDongsheng.wang@freescale.com struct cascade_priv { 6536ca09beSDongsheng.wang@freescale.com u32 tcr_value; /* TCR register: CASC & ROVR value */ 6636ca09beSDongsheng.wang@freescale.com unsigned int cascade_map; /* cascade map */ 6736ca09beSDongsheng.wang@freescale.com unsigned int timer_num; /* cascade control timer */ 6836ca09beSDongsheng.wang@freescale.com }; 6936ca09beSDongsheng.wang@freescale.com 7036ca09beSDongsheng.wang@freescale.com struct timer_group_priv { 7136ca09beSDongsheng.wang@freescale.com struct timer_regs __iomem *regs; 7236ca09beSDongsheng.wang@freescale.com struct mpic_timer timer[TIMERS_PER_GROUP]; 7336ca09beSDongsheng.wang@freescale.com struct list_head node; 7436ca09beSDongsheng.wang@freescale.com unsigned int timerfreq; 7536ca09beSDongsheng.wang@freescale.com unsigned int idle; 7636ca09beSDongsheng.wang@freescale.com unsigned int flags; 7736ca09beSDongsheng.wang@freescale.com spinlock_t lock; 7836ca09beSDongsheng.wang@freescale.com void __iomem *group_tcr; 7936ca09beSDongsheng.wang@freescale.com }; 8036ca09beSDongsheng.wang@freescale.com 8136ca09beSDongsheng.wang@freescale.com static struct cascade_priv cascade_timer[] = { 8236ca09beSDongsheng.wang@freescale.com /* cascade timer 0 and 1 */ 8336ca09beSDongsheng.wang@freescale.com {0x1, 0xc, 0x1}, 8436ca09beSDongsheng.wang@freescale.com /* cascade timer 1 and 2 */ 8536ca09beSDongsheng.wang@freescale.com {0x2, 0x6, 0x2}, 8636ca09beSDongsheng.wang@freescale.com /* cascade timer 2 and 3 */ 8736ca09beSDongsheng.wang@freescale.com {0x4, 0x3, 0x3} 8836ca09beSDongsheng.wang@freescale.com }; 8936ca09beSDongsheng.wang@freescale.com 9036ca09beSDongsheng.wang@freescale.com static LIST_HEAD(timer_group_list); 9136ca09beSDongsheng.wang@freescale.com 9236ca09beSDongsheng.wang@freescale.com static void convert_ticks_to_time(struct timer_group_priv *priv, 9336ca09beSDongsheng.wang@freescale.com const u64 ticks, struct timeval *time) 9436ca09beSDongsheng.wang@freescale.com { 9536ca09beSDongsheng.wang@freescale.com u64 tmp_sec; 9636ca09beSDongsheng.wang@freescale.com 9736ca09beSDongsheng.wang@freescale.com time->tv_sec = (__kernel_time_t)div_u64(ticks, priv->timerfreq); 9836ca09beSDongsheng.wang@freescale.com tmp_sec = (u64)time->tv_sec * (u64)priv->timerfreq; 9936ca09beSDongsheng.wang@freescale.com 10036ca09beSDongsheng.wang@freescale.com time->tv_usec = (__kernel_suseconds_t) 10136ca09beSDongsheng.wang@freescale.com div_u64((ticks - tmp_sec) * 1000000, priv->timerfreq); 10236ca09beSDongsheng.wang@freescale.com 10336ca09beSDongsheng.wang@freescale.com return; 10436ca09beSDongsheng.wang@freescale.com } 10536ca09beSDongsheng.wang@freescale.com 10636ca09beSDongsheng.wang@freescale.com /* the time set by the user is converted to "ticks" */ 10736ca09beSDongsheng.wang@freescale.com static int convert_time_to_ticks(struct timer_group_priv *priv, 10836ca09beSDongsheng.wang@freescale.com const struct timeval *time, u64 *ticks) 10936ca09beSDongsheng.wang@freescale.com { 11036ca09beSDongsheng.wang@freescale.com u64 max_value; /* prevent u64 overflow */ 11136ca09beSDongsheng.wang@freescale.com u64 tmp = 0; 11236ca09beSDongsheng.wang@freescale.com 11336ca09beSDongsheng.wang@freescale.com u64 tmp_sec; 11436ca09beSDongsheng.wang@freescale.com u64 tmp_ms; 11536ca09beSDongsheng.wang@freescale.com u64 tmp_us; 11636ca09beSDongsheng.wang@freescale.com 11736ca09beSDongsheng.wang@freescale.com max_value = div_u64(ULLONG_MAX, priv->timerfreq); 11836ca09beSDongsheng.wang@freescale.com 11936ca09beSDongsheng.wang@freescale.com if (time->tv_sec > max_value || 12036ca09beSDongsheng.wang@freescale.com (time->tv_sec == max_value && time->tv_usec > 0)) 12136ca09beSDongsheng.wang@freescale.com return -EINVAL; 12236ca09beSDongsheng.wang@freescale.com 12336ca09beSDongsheng.wang@freescale.com tmp_sec = (u64)time->tv_sec * (u64)priv->timerfreq; 12436ca09beSDongsheng.wang@freescale.com tmp += tmp_sec; 12536ca09beSDongsheng.wang@freescale.com 12636ca09beSDongsheng.wang@freescale.com tmp_ms = time->tv_usec / 1000; 12736ca09beSDongsheng.wang@freescale.com tmp_ms = div_u64((u64)tmp_ms * (u64)priv->timerfreq, 1000); 12836ca09beSDongsheng.wang@freescale.com tmp += tmp_ms; 12936ca09beSDongsheng.wang@freescale.com 13036ca09beSDongsheng.wang@freescale.com tmp_us = time->tv_usec % 1000; 13136ca09beSDongsheng.wang@freescale.com tmp_us = div_u64((u64)tmp_us * (u64)priv->timerfreq, 1000000); 13236ca09beSDongsheng.wang@freescale.com tmp += tmp_us; 13336ca09beSDongsheng.wang@freescale.com 13436ca09beSDongsheng.wang@freescale.com *ticks = tmp; 13536ca09beSDongsheng.wang@freescale.com 13636ca09beSDongsheng.wang@freescale.com return 0; 13736ca09beSDongsheng.wang@freescale.com } 13836ca09beSDongsheng.wang@freescale.com 13936ca09beSDongsheng.wang@freescale.com /* detect whether there is a cascade timer available */ 14036ca09beSDongsheng.wang@freescale.com static struct mpic_timer *detect_idle_cascade_timer( 14136ca09beSDongsheng.wang@freescale.com struct timer_group_priv *priv) 14236ca09beSDongsheng.wang@freescale.com { 14336ca09beSDongsheng.wang@freescale.com struct cascade_priv *casc_priv; 14436ca09beSDongsheng.wang@freescale.com unsigned int map; 14536ca09beSDongsheng.wang@freescale.com unsigned int array_size = ARRAY_SIZE(cascade_timer); 14636ca09beSDongsheng.wang@freescale.com unsigned int num; 14736ca09beSDongsheng.wang@freescale.com unsigned int i; 14836ca09beSDongsheng.wang@freescale.com unsigned long flags; 14936ca09beSDongsheng.wang@freescale.com 15036ca09beSDongsheng.wang@freescale.com casc_priv = cascade_timer; 15136ca09beSDongsheng.wang@freescale.com for (i = 0; i < array_size; i++) { 15236ca09beSDongsheng.wang@freescale.com spin_lock_irqsave(&priv->lock, flags); 15336ca09beSDongsheng.wang@freescale.com map = casc_priv->cascade_map & priv->idle; 15436ca09beSDongsheng.wang@freescale.com if (map == casc_priv->cascade_map) { 15536ca09beSDongsheng.wang@freescale.com num = casc_priv->timer_num; 15636ca09beSDongsheng.wang@freescale.com priv->timer[num].cascade_handle = casc_priv; 15736ca09beSDongsheng.wang@freescale.com 15836ca09beSDongsheng.wang@freescale.com /* set timer busy */ 15936ca09beSDongsheng.wang@freescale.com priv->idle &= ~casc_priv->cascade_map; 16036ca09beSDongsheng.wang@freescale.com spin_unlock_irqrestore(&priv->lock, flags); 16136ca09beSDongsheng.wang@freescale.com return &priv->timer[num]; 16236ca09beSDongsheng.wang@freescale.com } 16336ca09beSDongsheng.wang@freescale.com spin_unlock_irqrestore(&priv->lock, flags); 16436ca09beSDongsheng.wang@freescale.com casc_priv++; 16536ca09beSDongsheng.wang@freescale.com } 16636ca09beSDongsheng.wang@freescale.com 16736ca09beSDongsheng.wang@freescale.com return NULL; 16836ca09beSDongsheng.wang@freescale.com } 16936ca09beSDongsheng.wang@freescale.com 17036ca09beSDongsheng.wang@freescale.com static int set_cascade_timer(struct timer_group_priv *priv, u64 ticks, 17136ca09beSDongsheng.wang@freescale.com unsigned int num) 17236ca09beSDongsheng.wang@freescale.com { 17336ca09beSDongsheng.wang@freescale.com struct cascade_priv *casc_priv; 17436ca09beSDongsheng.wang@freescale.com u32 tcr; 17536ca09beSDongsheng.wang@freescale.com u32 tmp_ticks; 17636ca09beSDongsheng.wang@freescale.com u32 rem_ticks; 17736ca09beSDongsheng.wang@freescale.com 17836ca09beSDongsheng.wang@freescale.com /* set group tcr reg for cascade */ 17936ca09beSDongsheng.wang@freescale.com casc_priv = priv->timer[num].cascade_handle; 18036ca09beSDongsheng.wang@freescale.com if (!casc_priv) 18136ca09beSDongsheng.wang@freescale.com return -EINVAL; 18236ca09beSDongsheng.wang@freescale.com 18336ca09beSDongsheng.wang@freescale.com tcr = casc_priv->tcr_value | 18436ca09beSDongsheng.wang@freescale.com (casc_priv->tcr_value << MPIC_TIMER_TCR_ROVR_OFFSET); 18536ca09beSDongsheng.wang@freescale.com setbits32(priv->group_tcr, tcr); 18636ca09beSDongsheng.wang@freescale.com 18736ca09beSDongsheng.wang@freescale.com tmp_ticks = div_u64_rem(ticks, MAX_TICKS_CASCADE, &rem_ticks); 18836ca09beSDongsheng.wang@freescale.com 18936ca09beSDongsheng.wang@freescale.com out_be32(&priv->regs[num].gtccr, 0); 19036ca09beSDongsheng.wang@freescale.com out_be32(&priv->regs[num].gtbcr, tmp_ticks | TIMER_STOP); 19136ca09beSDongsheng.wang@freescale.com 19236ca09beSDongsheng.wang@freescale.com out_be32(&priv->regs[num - 1].gtccr, 0); 19336ca09beSDongsheng.wang@freescale.com out_be32(&priv->regs[num - 1].gtbcr, rem_ticks); 19436ca09beSDongsheng.wang@freescale.com 19536ca09beSDongsheng.wang@freescale.com return 0; 19636ca09beSDongsheng.wang@freescale.com } 19736ca09beSDongsheng.wang@freescale.com 19836ca09beSDongsheng.wang@freescale.com static struct mpic_timer *get_cascade_timer(struct timer_group_priv *priv, 19936ca09beSDongsheng.wang@freescale.com u64 ticks) 20036ca09beSDongsheng.wang@freescale.com { 20136ca09beSDongsheng.wang@freescale.com struct mpic_timer *allocated_timer; 20236ca09beSDongsheng.wang@freescale.com 20336ca09beSDongsheng.wang@freescale.com /* Two cascade timers: Support the maximum time */ 20436ca09beSDongsheng.wang@freescale.com const u64 max_ticks = (u64)MAX_TICKS * (u64)MAX_TICKS_CASCADE; 20536ca09beSDongsheng.wang@freescale.com int ret; 20636ca09beSDongsheng.wang@freescale.com 20736ca09beSDongsheng.wang@freescale.com if (ticks > max_ticks) 20836ca09beSDongsheng.wang@freescale.com return NULL; 20936ca09beSDongsheng.wang@freescale.com 21036ca09beSDongsheng.wang@freescale.com /* detect idle timer */ 21136ca09beSDongsheng.wang@freescale.com allocated_timer = detect_idle_cascade_timer(priv); 21236ca09beSDongsheng.wang@freescale.com if (!allocated_timer) 21336ca09beSDongsheng.wang@freescale.com return NULL; 21436ca09beSDongsheng.wang@freescale.com 21536ca09beSDongsheng.wang@freescale.com /* set ticks to timer */ 21636ca09beSDongsheng.wang@freescale.com ret = set_cascade_timer(priv, ticks, allocated_timer->num); 21736ca09beSDongsheng.wang@freescale.com if (ret < 0) 21836ca09beSDongsheng.wang@freescale.com return NULL; 21936ca09beSDongsheng.wang@freescale.com 22036ca09beSDongsheng.wang@freescale.com return allocated_timer; 22136ca09beSDongsheng.wang@freescale.com } 22236ca09beSDongsheng.wang@freescale.com 22336ca09beSDongsheng.wang@freescale.com static struct mpic_timer *get_timer(const struct timeval *time) 22436ca09beSDongsheng.wang@freescale.com { 22536ca09beSDongsheng.wang@freescale.com struct timer_group_priv *priv; 22636ca09beSDongsheng.wang@freescale.com struct mpic_timer *timer; 22736ca09beSDongsheng.wang@freescale.com 22836ca09beSDongsheng.wang@freescale.com u64 ticks; 22936ca09beSDongsheng.wang@freescale.com unsigned int num; 23036ca09beSDongsheng.wang@freescale.com unsigned int i; 23136ca09beSDongsheng.wang@freescale.com unsigned long flags; 23236ca09beSDongsheng.wang@freescale.com int ret; 23336ca09beSDongsheng.wang@freescale.com 23436ca09beSDongsheng.wang@freescale.com list_for_each_entry(priv, &timer_group_list, node) { 23536ca09beSDongsheng.wang@freescale.com ret = convert_time_to_ticks(priv, time, &ticks); 23636ca09beSDongsheng.wang@freescale.com if (ret < 0) 23736ca09beSDongsheng.wang@freescale.com return NULL; 23836ca09beSDongsheng.wang@freescale.com 23936ca09beSDongsheng.wang@freescale.com if (ticks > MAX_TICKS) { 24036ca09beSDongsheng.wang@freescale.com if (!(priv->flags & FSL_GLOBAL_TIMER)) 24136ca09beSDongsheng.wang@freescale.com return NULL; 24236ca09beSDongsheng.wang@freescale.com 24336ca09beSDongsheng.wang@freescale.com timer = get_cascade_timer(priv, ticks); 24436ca09beSDongsheng.wang@freescale.com if (!timer) 24536ca09beSDongsheng.wang@freescale.com continue; 24636ca09beSDongsheng.wang@freescale.com 24736ca09beSDongsheng.wang@freescale.com return timer; 24836ca09beSDongsheng.wang@freescale.com } 24936ca09beSDongsheng.wang@freescale.com 25036ca09beSDongsheng.wang@freescale.com for (i = 0; i < TIMERS_PER_GROUP; i++) { 25136ca09beSDongsheng.wang@freescale.com /* one timer: Reverse allocation */ 25236ca09beSDongsheng.wang@freescale.com num = TIMERS_PER_GROUP - 1 - i; 25336ca09beSDongsheng.wang@freescale.com spin_lock_irqsave(&priv->lock, flags); 25436ca09beSDongsheng.wang@freescale.com if (priv->idle & (1 << i)) { 25536ca09beSDongsheng.wang@freescale.com /* set timer busy */ 25636ca09beSDongsheng.wang@freescale.com priv->idle &= ~(1 << i); 25736ca09beSDongsheng.wang@freescale.com /* set ticks & stop timer */ 25836ca09beSDongsheng.wang@freescale.com out_be32(&priv->regs[num].gtbcr, 25936ca09beSDongsheng.wang@freescale.com ticks | TIMER_STOP); 26036ca09beSDongsheng.wang@freescale.com out_be32(&priv->regs[num].gtccr, 0); 26136ca09beSDongsheng.wang@freescale.com priv->timer[num].cascade_handle = NULL; 26236ca09beSDongsheng.wang@freescale.com spin_unlock_irqrestore(&priv->lock, flags); 26336ca09beSDongsheng.wang@freescale.com return &priv->timer[num]; 26436ca09beSDongsheng.wang@freescale.com } 26536ca09beSDongsheng.wang@freescale.com spin_unlock_irqrestore(&priv->lock, flags); 26636ca09beSDongsheng.wang@freescale.com } 26736ca09beSDongsheng.wang@freescale.com } 26836ca09beSDongsheng.wang@freescale.com 26936ca09beSDongsheng.wang@freescale.com return NULL; 27036ca09beSDongsheng.wang@freescale.com } 27136ca09beSDongsheng.wang@freescale.com 27236ca09beSDongsheng.wang@freescale.com /** 27336ca09beSDongsheng.wang@freescale.com * mpic_start_timer - start hardware timer 27436ca09beSDongsheng.wang@freescale.com * @handle: the timer to be started. 27536ca09beSDongsheng.wang@freescale.com * 27636ca09beSDongsheng.wang@freescale.com * It will do ->fn(->dev) callback from the hardware interrupt at 27736ca09beSDongsheng.wang@freescale.com * the ->timeval point in the future. 27836ca09beSDongsheng.wang@freescale.com */ 27936ca09beSDongsheng.wang@freescale.com void mpic_start_timer(struct mpic_timer *handle) 28036ca09beSDongsheng.wang@freescale.com { 28136ca09beSDongsheng.wang@freescale.com struct timer_group_priv *priv = container_of(handle, 28236ca09beSDongsheng.wang@freescale.com struct timer_group_priv, timer[handle->num]); 28336ca09beSDongsheng.wang@freescale.com 28436ca09beSDongsheng.wang@freescale.com clrbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP); 28536ca09beSDongsheng.wang@freescale.com } 28636ca09beSDongsheng.wang@freescale.com EXPORT_SYMBOL(mpic_start_timer); 28736ca09beSDongsheng.wang@freescale.com 28836ca09beSDongsheng.wang@freescale.com /** 28936ca09beSDongsheng.wang@freescale.com * mpic_stop_timer - stop hardware timer 29036ca09beSDongsheng.wang@freescale.com * @handle: the timer to be stoped 29136ca09beSDongsheng.wang@freescale.com * 29236ca09beSDongsheng.wang@freescale.com * The timer periodically generates an interrupt. Unless user stops the timer. 29336ca09beSDongsheng.wang@freescale.com */ 29436ca09beSDongsheng.wang@freescale.com void mpic_stop_timer(struct mpic_timer *handle) 29536ca09beSDongsheng.wang@freescale.com { 29636ca09beSDongsheng.wang@freescale.com struct timer_group_priv *priv = container_of(handle, 29736ca09beSDongsheng.wang@freescale.com struct timer_group_priv, timer[handle->num]); 29836ca09beSDongsheng.wang@freescale.com struct cascade_priv *casc_priv; 29936ca09beSDongsheng.wang@freescale.com 30036ca09beSDongsheng.wang@freescale.com setbits32(&priv->regs[handle->num].gtbcr, TIMER_STOP); 30136ca09beSDongsheng.wang@freescale.com 30236ca09beSDongsheng.wang@freescale.com casc_priv = priv->timer[handle->num].cascade_handle; 30336ca09beSDongsheng.wang@freescale.com if (casc_priv) { 30436ca09beSDongsheng.wang@freescale.com out_be32(&priv->regs[handle->num].gtccr, 0); 30536ca09beSDongsheng.wang@freescale.com out_be32(&priv->regs[handle->num - 1].gtccr, 0); 30636ca09beSDongsheng.wang@freescale.com } else { 30736ca09beSDongsheng.wang@freescale.com out_be32(&priv->regs[handle->num].gtccr, 0); 30836ca09beSDongsheng.wang@freescale.com } 30936ca09beSDongsheng.wang@freescale.com } 31036ca09beSDongsheng.wang@freescale.com EXPORT_SYMBOL(mpic_stop_timer); 31136ca09beSDongsheng.wang@freescale.com 31236ca09beSDongsheng.wang@freescale.com /** 31336ca09beSDongsheng.wang@freescale.com * mpic_get_remain_time - get timer time 31436ca09beSDongsheng.wang@freescale.com * @handle: the timer to be selected. 31536ca09beSDongsheng.wang@freescale.com * @time: time for timer 31636ca09beSDongsheng.wang@freescale.com * 31736ca09beSDongsheng.wang@freescale.com * Query timer remaining time. 31836ca09beSDongsheng.wang@freescale.com */ 31936ca09beSDongsheng.wang@freescale.com void mpic_get_remain_time(struct mpic_timer *handle, struct timeval *time) 32036ca09beSDongsheng.wang@freescale.com { 32136ca09beSDongsheng.wang@freescale.com struct timer_group_priv *priv = container_of(handle, 32236ca09beSDongsheng.wang@freescale.com struct timer_group_priv, timer[handle->num]); 32336ca09beSDongsheng.wang@freescale.com struct cascade_priv *casc_priv; 32436ca09beSDongsheng.wang@freescale.com 32536ca09beSDongsheng.wang@freescale.com u64 ticks; 32636ca09beSDongsheng.wang@freescale.com u32 tmp_ticks; 32736ca09beSDongsheng.wang@freescale.com 32836ca09beSDongsheng.wang@freescale.com casc_priv = priv->timer[handle->num].cascade_handle; 32936ca09beSDongsheng.wang@freescale.com if (casc_priv) { 33036ca09beSDongsheng.wang@freescale.com tmp_ticks = in_be32(&priv->regs[handle->num].gtccr); 3310fd79588SWang Dongsheng tmp_ticks &= ~GTCCR_TOG; 33236ca09beSDongsheng.wang@freescale.com ticks = ((u64)tmp_ticks & UINT_MAX) * (u64)MAX_TICKS_CASCADE; 33336ca09beSDongsheng.wang@freescale.com tmp_ticks = in_be32(&priv->regs[handle->num - 1].gtccr); 33436ca09beSDongsheng.wang@freescale.com ticks += tmp_ticks; 33536ca09beSDongsheng.wang@freescale.com } else { 33636ca09beSDongsheng.wang@freescale.com ticks = in_be32(&priv->regs[handle->num].gtccr); 3370fd79588SWang Dongsheng ticks &= ~GTCCR_TOG; 33836ca09beSDongsheng.wang@freescale.com } 33936ca09beSDongsheng.wang@freescale.com 34036ca09beSDongsheng.wang@freescale.com convert_ticks_to_time(priv, ticks, time); 34136ca09beSDongsheng.wang@freescale.com } 34236ca09beSDongsheng.wang@freescale.com EXPORT_SYMBOL(mpic_get_remain_time); 34336ca09beSDongsheng.wang@freescale.com 34436ca09beSDongsheng.wang@freescale.com /** 34536ca09beSDongsheng.wang@freescale.com * mpic_free_timer - free hardware timer 34636ca09beSDongsheng.wang@freescale.com * @handle: the timer to be removed. 34736ca09beSDongsheng.wang@freescale.com * 34836ca09beSDongsheng.wang@freescale.com * Free the timer. 34936ca09beSDongsheng.wang@freescale.com * 35036ca09beSDongsheng.wang@freescale.com * Note: can not be used in interrupt context. 35136ca09beSDongsheng.wang@freescale.com */ 35236ca09beSDongsheng.wang@freescale.com void mpic_free_timer(struct mpic_timer *handle) 35336ca09beSDongsheng.wang@freescale.com { 35436ca09beSDongsheng.wang@freescale.com struct timer_group_priv *priv = container_of(handle, 35536ca09beSDongsheng.wang@freescale.com struct timer_group_priv, timer[handle->num]); 35636ca09beSDongsheng.wang@freescale.com 35736ca09beSDongsheng.wang@freescale.com struct cascade_priv *casc_priv; 35836ca09beSDongsheng.wang@freescale.com unsigned long flags; 35936ca09beSDongsheng.wang@freescale.com 36036ca09beSDongsheng.wang@freescale.com mpic_stop_timer(handle); 36136ca09beSDongsheng.wang@freescale.com 36236ca09beSDongsheng.wang@freescale.com casc_priv = priv->timer[handle->num].cascade_handle; 36336ca09beSDongsheng.wang@freescale.com 36436ca09beSDongsheng.wang@freescale.com free_irq(priv->timer[handle->num].irq, priv->timer[handle->num].dev); 36536ca09beSDongsheng.wang@freescale.com 36636ca09beSDongsheng.wang@freescale.com spin_lock_irqsave(&priv->lock, flags); 36736ca09beSDongsheng.wang@freescale.com if (casc_priv) { 36836ca09beSDongsheng.wang@freescale.com u32 tcr; 36936ca09beSDongsheng.wang@freescale.com tcr = casc_priv->tcr_value | (casc_priv->tcr_value << 37036ca09beSDongsheng.wang@freescale.com MPIC_TIMER_TCR_ROVR_OFFSET); 37136ca09beSDongsheng.wang@freescale.com clrbits32(priv->group_tcr, tcr); 37236ca09beSDongsheng.wang@freescale.com priv->idle |= casc_priv->cascade_map; 37336ca09beSDongsheng.wang@freescale.com priv->timer[handle->num].cascade_handle = NULL; 37436ca09beSDongsheng.wang@freescale.com } else { 37536ca09beSDongsheng.wang@freescale.com priv->idle |= TIMER_OFFSET(handle->num); 37636ca09beSDongsheng.wang@freescale.com } 37736ca09beSDongsheng.wang@freescale.com spin_unlock_irqrestore(&priv->lock, flags); 37836ca09beSDongsheng.wang@freescale.com } 37936ca09beSDongsheng.wang@freescale.com EXPORT_SYMBOL(mpic_free_timer); 38036ca09beSDongsheng.wang@freescale.com 38136ca09beSDongsheng.wang@freescale.com /** 38236ca09beSDongsheng.wang@freescale.com * mpic_request_timer - get a hardware timer 38336ca09beSDongsheng.wang@freescale.com * @fn: interrupt handler function 38436ca09beSDongsheng.wang@freescale.com * @dev: callback function of the data 38536ca09beSDongsheng.wang@freescale.com * @time: time for timer 38636ca09beSDongsheng.wang@freescale.com * 38736ca09beSDongsheng.wang@freescale.com * This executes the "request_irq", returning NULL 38836ca09beSDongsheng.wang@freescale.com * else "handle" on success. 38936ca09beSDongsheng.wang@freescale.com */ 39036ca09beSDongsheng.wang@freescale.com struct mpic_timer *mpic_request_timer(irq_handler_t fn, void *dev, 39136ca09beSDongsheng.wang@freescale.com const struct timeval *time) 39236ca09beSDongsheng.wang@freescale.com { 39336ca09beSDongsheng.wang@freescale.com struct mpic_timer *allocated_timer; 39436ca09beSDongsheng.wang@freescale.com int ret; 39536ca09beSDongsheng.wang@freescale.com 39636ca09beSDongsheng.wang@freescale.com if (list_empty(&timer_group_list)) 39736ca09beSDongsheng.wang@freescale.com return NULL; 39836ca09beSDongsheng.wang@freescale.com 39936ca09beSDongsheng.wang@freescale.com if (!(time->tv_sec + time->tv_usec) || 40036ca09beSDongsheng.wang@freescale.com time->tv_sec < 0 || time->tv_usec < 0) 40136ca09beSDongsheng.wang@freescale.com return NULL; 40236ca09beSDongsheng.wang@freescale.com 40336ca09beSDongsheng.wang@freescale.com if (time->tv_usec > ONE_SECOND) 40436ca09beSDongsheng.wang@freescale.com return NULL; 40536ca09beSDongsheng.wang@freescale.com 40636ca09beSDongsheng.wang@freescale.com allocated_timer = get_timer(time); 40736ca09beSDongsheng.wang@freescale.com if (!allocated_timer) 40836ca09beSDongsheng.wang@freescale.com return NULL; 40936ca09beSDongsheng.wang@freescale.com 41036ca09beSDongsheng.wang@freescale.com ret = request_irq(allocated_timer->irq, fn, 41136ca09beSDongsheng.wang@freescale.com IRQF_TRIGGER_LOW, "global-timer", dev); 41236ca09beSDongsheng.wang@freescale.com if (ret) { 41336ca09beSDongsheng.wang@freescale.com mpic_free_timer(allocated_timer); 41436ca09beSDongsheng.wang@freescale.com return NULL; 41536ca09beSDongsheng.wang@freescale.com } 41636ca09beSDongsheng.wang@freescale.com 41736ca09beSDongsheng.wang@freescale.com allocated_timer->dev = dev; 41836ca09beSDongsheng.wang@freescale.com 41936ca09beSDongsheng.wang@freescale.com return allocated_timer; 42036ca09beSDongsheng.wang@freescale.com } 42136ca09beSDongsheng.wang@freescale.com EXPORT_SYMBOL(mpic_request_timer); 42236ca09beSDongsheng.wang@freescale.com 42336ca09beSDongsheng.wang@freescale.com static int timer_group_get_freq(struct device_node *np, 42436ca09beSDongsheng.wang@freescale.com struct timer_group_priv *priv) 42536ca09beSDongsheng.wang@freescale.com { 42636ca09beSDongsheng.wang@freescale.com u32 div; 42736ca09beSDongsheng.wang@freescale.com 42836ca09beSDongsheng.wang@freescale.com if (priv->flags & FSL_GLOBAL_TIMER) { 42936ca09beSDongsheng.wang@freescale.com struct device_node *dn; 43036ca09beSDongsheng.wang@freescale.com 43136ca09beSDongsheng.wang@freescale.com dn = of_find_compatible_node(NULL, NULL, "fsl,mpic"); 43236ca09beSDongsheng.wang@freescale.com if (dn) { 43336ca09beSDongsheng.wang@freescale.com of_property_read_u32(dn, "clock-frequency", 43436ca09beSDongsheng.wang@freescale.com &priv->timerfreq); 43536ca09beSDongsheng.wang@freescale.com of_node_put(dn); 43636ca09beSDongsheng.wang@freescale.com } 43736ca09beSDongsheng.wang@freescale.com } 43836ca09beSDongsheng.wang@freescale.com 43936ca09beSDongsheng.wang@freescale.com if (priv->timerfreq <= 0) 44036ca09beSDongsheng.wang@freescale.com return -EINVAL; 44136ca09beSDongsheng.wang@freescale.com 44236ca09beSDongsheng.wang@freescale.com if (priv->flags & FSL_GLOBAL_TIMER) { 44336ca09beSDongsheng.wang@freescale.com div = (1 << (MPIC_TIMER_TCR_CLKDIV >> 8)) * 8; 44436ca09beSDongsheng.wang@freescale.com priv->timerfreq /= div; 44536ca09beSDongsheng.wang@freescale.com } 44636ca09beSDongsheng.wang@freescale.com 44736ca09beSDongsheng.wang@freescale.com return 0; 44836ca09beSDongsheng.wang@freescale.com } 44936ca09beSDongsheng.wang@freescale.com 45036ca09beSDongsheng.wang@freescale.com static int timer_group_get_irq(struct device_node *np, 45136ca09beSDongsheng.wang@freescale.com struct timer_group_priv *priv) 45236ca09beSDongsheng.wang@freescale.com { 45336ca09beSDongsheng.wang@freescale.com const u32 all_timer[] = { 0, TIMERS_PER_GROUP }; 45436ca09beSDongsheng.wang@freescale.com const u32 *p; 45536ca09beSDongsheng.wang@freescale.com u32 offset; 45636ca09beSDongsheng.wang@freescale.com u32 count; 45736ca09beSDongsheng.wang@freescale.com 45836ca09beSDongsheng.wang@freescale.com unsigned int i; 45936ca09beSDongsheng.wang@freescale.com unsigned int j; 46036ca09beSDongsheng.wang@freescale.com unsigned int irq_index = 0; 46136ca09beSDongsheng.wang@freescale.com unsigned int irq; 46236ca09beSDongsheng.wang@freescale.com int len; 46336ca09beSDongsheng.wang@freescale.com 46436ca09beSDongsheng.wang@freescale.com p = of_get_property(np, "fsl,available-ranges", &len); 46536ca09beSDongsheng.wang@freescale.com if (p && len % (2 * sizeof(u32)) != 0) { 46636ca09beSDongsheng.wang@freescale.com pr_err("%s: malformed available-ranges property.\n", 46736ca09beSDongsheng.wang@freescale.com np->full_name); 46836ca09beSDongsheng.wang@freescale.com return -EINVAL; 46936ca09beSDongsheng.wang@freescale.com } 47036ca09beSDongsheng.wang@freescale.com 47136ca09beSDongsheng.wang@freescale.com if (!p) { 47236ca09beSDongsheng.wang@freescale.com p = all_timer; 47336ca09beSDongsheng.wang@freescale.com len = sizeof(all_timer); 47436ca09beSDongsheng.wang@freescale.com } 47536ca09beSDongsheng.wang@freescale.com 47636ca09beSDongsheng.wang@freescale.com len /= 2 * sizeof(u32); 47736ca09beSDongsheng.wang@freescale.com 47836ca09beSDongsheng.wang@freescale.com for (i = 0; i < len; i++) { 47936ca09beSDongsheng.wang@freescale.com offset = p[i * 2]; 48036ca09beSDongsheng.wang@freescale.com count = p[i * 2 + 1]; 48136ca09beSDongsheng.wang@freescale.com for (j = 0; j < count; j++) { 48236ca09beSDongsheng.wang@freescale.com irq = irq_of_parse_and_map(np, irq_index); 48336ca09beSDongsheng.wang@freescale.com if (!irq) { 48436ca09beSDongsheng.wang@freescale.com pr_err("%s: irq parse and map failed.\n", 48536ca09beSDongsheng.wang@freescale.com np->full_name); 48636ca09beSDongsheng.wang@freescale.com return -EINVAL; 48736ca09beSDongsheng.wang@freescale.com } 48836ca09beSDongsheng.wang@freescale.com 48936ca09beSDongsheng.wang@freescale.com /* Set timer idle */ 49036ca09beSDongsheng.wang@freescale.com priv->idle |= TIMER_OFFSET((offset + j)); 49136ca09beSDongsheng.wang@freescale.com priv->timer[offset + j].irq = irq; 49236ca09beSDongsheng.wang@freescale.com priv->timer[offset + j].num = offset + j; 49336ca09beSDongsheng.wang@freescale.com irq_index++; 49436ca09beSDongsheng.wang@freescale.com } 49536ca09beSDongsheng.wang@freescale.com } 49636ca09beSDongsheng.wang@freescale.com 49736ca09beSDongsheng.wang@freescale.com return 0; 49836ca09beSDongsheng.wang@freescale.com } 49936ca09beSDongsheng.wang@freescale.com 50036ca09beSDongsheng.wang@freescale.com static void timer_group_init(struct device_node *np) 50136ca09beSDongsheng.wang@freescale.com { 50236ca09beSDongsheng.wang@freescale.com struct timer_group_priv *priv; 50336ca09beSDongsheng.wang@freescale.com unsigned int i = 0; 50436ca09beSDongsheng.wang@freescale.com int ret; 50536ca09beSDongsheng.wang@freescale.com 50636ca09beSDongsheng.wang@freescale.com priv = kzalloc(sizeof(struct timer_group_priv), GFP_KERNEL); 50736ca09beSDongsheng.wang@freescale.com if (!priv) { 50836ca09beSDongsheng.wang@freescale.com pr_err("%s: cannot allocate memory for group.\n", 50936ca09beSDongsheng.wang@freescale.com np->full_name); 51036ca09beSDongsheng.wang@freescale.com return; 51136ca09beSDongsheng.wang@freescale.com } 51236ca09beSDongsheng.wang@freescale.com 51336ca09beSDongsheng.wang@freescale.com if (of_device_is_compatible(np, "fsl,mpic-global-timer")) 51436ca09beSDongsheng.wang@freescale.com priv->flags |= FSL_GLOBAL_TIMER; 51536ca09beSDongsheng.wang@freescale.com 51636ca09beSDongsheng.wang@freescale.com priv->regs = of_iomap(np, i++); 51736ca09beSDongsheng.wang@freescale.com if (!priv->regs) { 51836ca09beSDongsheng.wang@freescale.com pr_err("%s: cannot ioremap timer register address.\n", 51936ca09beSDongsheng.wang@freescale.com np->full_name); 52036ca09beSDongsheng.wang@freescale.com goto out; 52136ca09beSDongsheng.wang@freescale.com } 52236ca09beSDongsheng.wang@freescale.com 52336ca09beSDongsheng.wang@freescale.com if (priv->flags & FSL_GLOBAL_TIMER) { 52436ca09beSDongsheng.wang@freescale.com priv->group_tcr = of_iomap(np, i++); 52536ca09beSDongsheng.wang@freescale.com if (!priv->group_tcr) { 52636ca09beSDongsheng.wang@freescale.com pr_err("%s: cannot ioremap tcr address.\n", 52736ca09beSDongsheng.wang@freescale.com np->full_name); 52836ca09beSDongsheng.wang@freescale.com goto out; 52936ca09beSDongsheng.wang@freescale.com } 53036ca09beSDongsheng.wang@freescale.com } 53136ca09beSDongsheng.wang@freescale.com 53236ca09beSDongsheng.wang@freescale.com ret = timer_group_get_freq(np, priv); 53336ca09beSDongsheng.wang@freescale.com if (ret < 0) { 53436ca09beSDongsheng.wang@freescale.com pr_err("%s: cannot get timer frequency.\n", np->full_name); 53536ca09beSDongsheng.wang@freescale.com goto out; 53636ca09beSDongsheng.wang@freescale.com } 53736ca09beSDongsheng.wang@freescale.com 53836ca09beSDongsheng.wang@freescale.com ret = timer_group_get_irq(np, priv); 53936ca09beSDongsheng.wang@freescale.com if (ret < 0) { 54036ca09beSDongsheng.wang@freescale.com pr_err("%s: cannot get timer irqs.\n", np->full_name); 54136ca09beSDongsheng.wang@freescale.com goto out; 54236ca09beSDongsheng.wang@freescale.com } 54336ca09beSDongsheng.wang@freescale.com 54436ca09beSDongsheng.wang@freescale.com spin_lock_init(&priv->lock); 54536ca09beSDongsheng.wang@freescale.com 54636ca09beSDongsheng.wang@freescale.com /* Init FSL timer hardware */ 54736ca09beSDongsheng.wang@freescale.com if (priv->flags & FSL_GLOBAL_TIMER) 54836ca09beSDongsheng.wang@freescale.com setbits32(priv->group_tcr, MPIC_TIMER_TCR_CLKDIV); 54936ca09beSDongsheng.wang@freescale.com 55036ca09beSDongsheng.wang@freescale.com list_add_tail(&priv->node, &timer_group_list); 55136ca09beSDongsheng.wang@freescale.com 55236ca09beSDongsheng.wang@freescale.com return; 55336ca09beSDongsheng.wang@freescale.com 55436ca09beSDongsheng.wang@freescale.com out: 55536ca09beSDongsheng.wang@freescale.com if (priv->regs) 55636ca09beSDongsheng.wang@freescale.com iounmap(priv->regs); 55736ca09beSDongsheng.wang@freescale.com 55836ca09beSDongsheng.wang@freescale.com if (priv->group_tcr) 55936ca09beSDongsheng.wang@freescale.com iounmap(priv->group_tcr); 56036ca09beSDongsheng.wang@freescale.com 56136ca09beSDongsheng.wang@freescale.com kfree(priv); 56236ca09beSDongsheng.wang@freescale.com } 56336ca09beSDongsheng.wang@freescale.com 56436ca09beSDongsheng.wang@freescale.com static void mpic_timer_resume(void) 56536ca09beSDongsheng.wang@freescale.com { 56636ca09beSDongsheng.wang@freescale.com struct timer_group_priv *priv; 56736ca09beSDongsheng.wang@freescale.com 56836ca09beSDongsheng.wang@freescale.com list_for_each_entry(priv, &timer_group_list, node) { 56936ca09beSDongsheng.wang@freescale.com /* Init FSL timer hardware */ 57036ca09beSDongsheng.wang@freescale.com if (priv->flags & FSL_GLOBAL_TIMER) 57136ca09beSDongsheng.wang@freescale.com setbits32(priv->group_tcr, MPIC_TIMER_TCR_CLKDIV); 57236ca09beSDongsheng.wang@freescale.com } 57336ca09beSDongsheng.wang@freescale.com } 57436ca09beSDongsheng.wang@freescale.com 57536ca09beSDongsheng.wang@freescale.com static const struct of_device_id mpic_timer_ids[] = { 57636ca09beSDongsheng.wang@freescale.com { .compatible = "fsl,mpic-global-timer", }, 57736ca09beSDongsheng.wang@freescale.com {}, 57836ca09beSDongsheng.wang@freescale.com }; 57936ca09beSDongsheng.wang@freescale.com 58036ca09beSDongsheng.wang@freescale.com static struct syscore_ops mpic_timer_syscore_ops = { 58136ca09beSDongsheng.wang@freescale.com .resume = mpic_timer_resume, 58236ca09beSDongsheng.wang@freescale.com }; 58336ca09beSDongsheng.wang@freescale.com 58436ca09beSDongsheng.wang@freescale.com static int __init mpic_timer_init(void) 58536ca09beSDongsheng.wang@freescale.com { 58636ca09beSDongsheng.wang@freescale.com struct device_node *np = NULL; 58736ca09beSDongsheng.wang@freescale.com 58836ca09beSDongsheng.wang@freescale.com for_each_matching_node(np, mpic_timer_ids) 58936ca09beSDongsheng.wang@freescale.com timer_group_init(np); 59036ca09beSDongsheng.wang@freescale.com 59136ca09beSDongsheng.wang@freescale.com register_syscore_ops(&mpic_timer_syscore_ops); 59236ca09beSDongsheng.wang@freescale.com 59336ca09beSDongsheng.wang@freescale.com if (list_empty(&timer_group_list)) 59436ca09beSDongsheng.wang@freescale.com return -ENODEV; 59536ca09beSDongsheng.wang@freescale.com 59636ca09beSDongsheng.wang@freescale.com return 0; 59736ca09beSDongsheng.wang@freescale.com } 59836ca09beSDongsheng.wang@freescale.com subsys_initcall(mpic_timer_init); 599