18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/arch/arm/mach-omap1/time.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * OMAP Timers 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2004 Nokia Corporation 78c2ecf20Sopenharmony_ci * Partial timer rewrite and additional dynamic tick timer support by 88c2ecf20Sopenharmony_ci * Tony Lindgen <tony@atomide.com> and 98c2ecf20Sopenharmony_ci * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * MPU timer code based on the older MPU timer code for OMAP 128c2ecf20Sopenharmony_ci * Copyright (C) 2000 RidgeRun, Inc. 138c2ecf20Sopenharmony_ci * Author: Greg Lonnon <glonnon@ridgerun.com> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 168c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the 178c2ecf20Sopenharmony_ci * Free Software Foundation; either version 2 of the License, or (at your 188c2ecf20Sopenharmony_ci * option) any later version. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 218c2ecf20Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 228c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 238c2ecf20Sopenharmony_ci * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 248c2ecf20Sopenharmony_ci * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 258c2ecf20Sopenharmony_ci * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 268c2ecf20Sopenharmony_ci * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 278c2ecf20Sopenharmony_ci * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 288c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 298c2ecf20Sopenharmony_ci * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License along 328c2ecf20Sopenharmony_ci * with this program; if not, write to the Free Software Foundation, Inc., 338c2ecf20Sopenharmony_ci * 675 Mass Ave, Cambridge, MA 02139, USA. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include <linux/kernel.h> 378c2ecf20Sopenharmony_ci#include <linux/init.h> 388c2ecf20Sopenharmony_ci#include <linux/delay.h> 398c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 408c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 418c2ecf20Sopenharmony_ci#include <linux/clk.h> 428c2ecf20Sopenharmony_ci#include <linux/err.h> 438c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 448c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 458c2ecf20Sopenharmony_ci#include <linux/io.h> 468c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <asm/irq.h> 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#include <mach/hardware.h> 518c2ecf20Sopenharmony_ci#include <asm/mach/irq.h> 528c2ecf20Sopenharmony_ci#include <asm/mach/time.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include "iomap.h" 558c2ecf20Sopenharmony_ci#include "common.h" 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#ifdef CONFIG_OMAP_MPU_TIMER 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE 608c2ecf20Sopenharmony_ci#define OMAP_MPU_TIMER_OFFSET 0x100 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_citypedef struct { 638c2ecf20Sopenharmony_ci u32 cntl; /* CNTL_TIMER, R/W */ 648c2ecf20Sopenharmony_ci u32 load_tim; /* LOAD_TIM, W */ 658c2ecf20Sopenharmony_ci u32 read_tim; /* READ_TIM, R */ 668c2ecf20Sopenharmony_ci} omap_mpu_timer_regs_t; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define omap_mpu_timer_base(n) \ 698c2ecf20Sopenharmony_ci((omap_mpu_timer_regs_t __iomem *)OMAP1_IO_ADDRESS(OMAP_MPU_TIMER_BASE + \ 708c2ecf20Sopenharmony_ci (n)*OMAP_MPU_TIMER_OFFSET)) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic inline unsigned long notrace omap_mpu_timer_read(int nr) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); 758c2ecf20Sopenharmony_ci return readl(&timer->read_tim); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic inline void omap_mpu_set_autoreset(int nr) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci writel(readl(&timer->cntl) | MPU_TIMER_AR, &timer->cntl); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic inline void omap_mpu_remove_autoreset(int nr) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci writel(readl(&timer->cntl) & ~MPU_TIMER_AR, &timer->cntl); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic inline void omap_mpu_timer_start(int nr, unsigned long load_val, 938c2ecf20Sopenharmony_ci int autoreset) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); 968c2ecf20Sopenharmony_ci unsigned int timerflags = MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_ST; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (autoreset) 998c2ecf20Sopenharmony_ci timerflags |= MPU_TIMER_AR; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci writel(MPU_TIMER_CLOCK_ENABLE, &timer->cntl); 1028c2ecf20Sopenharmony_ci udelay(1); 1038c2ecf20Sopenharmony_ci writel(load_val, &timer->load_tim); 1048c2ecf20Sopenharmony_ci udelay(1); 1058c2ecf20Sopenharmony_ci writel(timerflags, &timer->cntl); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic inline void omap_mpu_timer_stop(int nr) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(nr); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci writel(readl(&timer->cntl) & ~MPU_TIMER_ST, &timer->cntl); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 1178c2ecf20Sopenharmony_ci * MPU timer 1 ... count down to zero, interrupt, reload 1188c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_cistatic int omap_mpu_set_next_event(unsigned long cycles, 1218c2ecf20Sopenharmony_ci struct clock_event_device *evt) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci omap_mpu_timer_start(0, cycles, 0); 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int omap_mpu_set_oneshot(struct clock_event_device *evt) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci omap_mpu_timer_stop(0); 1308c2ecf20Sopenharmony_ci omap_mpu_remove_autoreset(0); 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int omap_mpu_set_periodic(struct clock_event_device *evt) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci omap_mpu_set_autoreset(0); 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic struct clock_event_device clockevent_mpu_timer1 = { 1418c2ecf20Sopenharmony_ci .name = "mpu_timer1", 1428c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | 1438c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 1448c2ecf20Sopenharmony_ci .set_next_event = omap_mpu_set_next_event, 1458c2ecf20Sopenharmony_ci .set_state_periodic = omap_mpu_set_periodic, 1468c2ecf20Sopenharmony_ci .set_state_oneshot = omap_mpu_set_oneshot, 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic irqreturn_t omap_mpu_timer1_interrupt(int irq, void *dev_id) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct clock_event_device *evt = &clockevent_mpu_timer1; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci evt->event_handler(evt); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic __init void omap_init_mpu_timer(unsigned long rate) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci if (request_irq(INT_TIMER1, omap_mpu_timer1_interrupt, 1618c2ecf20Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL, "mpu_timer1", NULL)) 1628c2ecf20Sopenharmony_ci pr_err("Failed to request irq %d (mpu_timer1)\n", INT_TIMER1); 1638c2ecf20Sopenharmony_ci omap_mpu_timer_start(0, (rate / HZ) - 1, 1); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci clockevent_mpu_timer1.cpumask = cpumask_of(0); 1668c2ecf20Sopenharmony_ci clockevents_config_and_register(&clockevent_mpu_timer1, rate, 1678c2ecf20Sopenharmony_ci 1, -1); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* 1728c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 1738c2ecf20Sopenharmony_ci * MPU timer 2 ... free running 32-bit clock source and scheduler clock 1748c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic u64 notrace omap_mpu_read_sched_clock(void) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci return ~omap_mpu_timer_read(1); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic void __init omap_init_clocksource(unsigned long rate) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci omap_mpu_timer_regs_t __iomem *timer = omap_mpu_timer_base(1); 1858c2ecf20Sopenharmony_ci static char err[] __initdata = KERN_ERR 1868c2ecf20Sopenharmony_ci "%s: can't register clocksource!\n"; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci omap_mpu_timer_start(1, ~0, 1); 1898c2ecf20Sopenharmony_ci sched_clock_register(omap_mpu_read_sched_clock, 32, rate); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (clocksource_mmio_init(&timer->read_tim, "mpu_timer2", rate, 1928c2ecf20Sopenharmony_ci 300, 32, clocksource_mmio_readl_down)) 1938c2ecf20Sopenharmony_ci printk(err, "mpu_timer2"); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void __init omap_mpu_timer_init(void) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct clk *ck_ref = clk_get(NULL, "ck_ref"); 1998c2ecf20Sopenharmony_ci unsigned long rate; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci BUG_ON(IS_ERR(ck_ref)); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci rate = clk_get_rate(ck_ref); 2048c2ecf20Sopenharmony_ci clk_put(ck_ref); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* PTV = 0 */ 2078c2ecf20Sopenharmony_ci rate /= 2; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci omap_init_mpu_timer(rate); 2108c2ecf20Sopenharmony_ci omap_init_clocksource(rate); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci#else 2148c2ecf20Sopenharmony_cistatic inline void omap_mpu_timer_init(void) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci pr_err("Bogus timer, should not happen\n"); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci#endif /* CONFIG_OMAP_MPU_TIMER */ 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/* 2218c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 2228c2ecf20Sopenharmony_ci * Timer initialization 2238c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_civoid __init omap1_timer_init(void) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci if (omap_32k_timer_init() != 0) 2288c2ecf20Sopenharmony_ci omap_mpu_timer_init(); 2298c2ecf20Sopenharmony_ci} 230