18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/arch/arm/mach-omap1/timer32k.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * OMAP 32K Timer 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2004 - 2005 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 * OMAP Dual-mode timer framework support by Timo Teras 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * MPU timer code based on the older MPU timer code for OMAP 138c2ecf20Sopenharmony_ci * Copyright (C) 2000 RidgeRun, Inc. 148c2ecf20Sopenharmony_ci * Author: Greg Lonnon <glonnon@ridgerun.com> 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 178c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License as published by the 188c2ecf20Sopenharmony_ci * Free Software Foundation; either version 2 of the License, or (at your 198c2ecf20Sopenharmony_ci * option) any later version. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 228c2ecf20Sopenharmony_ci * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 238c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 248c2ecf20Sopenharmony_ci * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 258c2ecf20Sopenharmony_ci * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 268c2ecf20Sopenharmony_ci * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 278c2ecf20Sopenharmony_ci * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 288c2ecf20Sopenharmony_ci * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 298c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 308c2ecf20Sopenharmony_ci * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License along 338c2ecf20Sopenharmony_ci * with this program; if not, write to the Free Software Foundation, Inc., 348c2ecf20Sopenharmony_ci * 675 Mass Ave, Cambridge, MA 02139, USA. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include <linux/kernel.h> 388c2ecf20Sopenharmony_ci#include <linux/init.h> 398c2ecf20Sopenharmony_ci#include <linux/delay.h> 408c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 418c2ecf20Sopenharmony_ci#include <linux/sched.h> 428c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 438c2ecf20Sopenharmony_ci#include <linux/err.h> 448c2ecf20Sopenharmony_ci#include <linux/clk.h> 458c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 468c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 478c2ecf20Sopenharmony_ci#include <linux/io.h> 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#include <asm/irq.h> 508c2ecf20Sopenharmony_ci#include <asm/mach/irq.h> 518c2ecf20Sopenharmony_ci#include <asm/mach/time.h> 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#include <plat/counter-32k.h> 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#include <mach/hardware.h> 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#include "common.h" 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* 608c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 618c2ecf20Sopenharmony_ci * 32KHz OS timer 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * This currently works only on 16xx, as 1510 does not have the continuous 648c2ecf20Sopenharmony_ci * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track 658c2ecf20Sopenharmony_ci * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer 668c2ecf20Sopenharmony_ci * on 1510 would be possible, but the timer would not be as accurate as 678c2ecf20Sopenharmony_ci * with the 32KHz synchronized timer. 688c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 16xx specific defines */ 728c2ecf20Sopenharmony_ci#define OMAP1_32K_TIMER_BASE 0xfffb9000 738c2ecf20Sopenharmony_ci#define OMAP1_32KSYNC_TIMER_BASE 0xfffbc400 748c2ecf20Sopenharmony_ci#define OMAP1_32K_TIMER_CR 0x08 758c2ecf20Sopenharmony_ci#define OMAP1_32K_TIMER_TVR 0x00 768c2ecf20Sopenharmony_ci#define OMAP1_32K_TIMER_TCR 0x04 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define OMAP_32K_TICKS_PER_SEC (32768) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* 818c2ecf20Sopenharmony_ci * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 828c2ecf20Sopenharmony_ci * so with HZ = 128, TVR = 255. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci#define OMAP_32K_TIMER_TICK_PERIOD ((OMAP_32K_TICKS_PER_SEC / HZ) - 1) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ 878c2ecf20Sopenharmony_ci (((nr_jiffies) * (clock_rate)) / HZ) 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic inline void omap_32k_timer_write(int val, int reg) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci omap_writew(val, OMAP1_32K_TIMER_BASE + reg); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic inline void omap_32k_timer_start(unsigned long load_val) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci if (!load_val) 978c2ecf20Sopenharmony_ci load_val = 1; 988c2ecf20Sopenharmony_ci omap_32k_timer_write(load_val, OMAP1_32K_TIMER_TVR); 998c2ecf20Sopenharmony_ci omap_32k_timer_write(0x0f, OMAP1_32K_TIMER_CR); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic inline void omap_32k_timer_stop(void) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci omap_32k_timer_write(0x0, OMAP1_32K_TIMER_CR); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define omap_32k_timer_ack_irq() 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int omap_32k_timer_set_next_event(unsigned long delta, 1108c2ecf20Sopenharmony_ci struct clock_event_device *dev) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci omap_32k_timer_start(delta); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int omap_32k_timer_shutdown(struct clock_event_device *evt) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci omap_32k_timer_stop(); 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int omap_32k_timer_set_periodic(struct clock_event_device *evt) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci omap_32k_timer_stop(); 1268c2ecf20Sopenharmony_ci omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic struct clock_event_device clockevent_32k_timer = { 1318c2ecf20Sopenharmony_ci .name = "32k-timer", 1328c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | 1338c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 1348c2ecf20Sopenharmony_ci .set_next_event = omap_32k_timer_set_next_event, 1358c2ecf20Sopenharmony_ci .set_state_shutdown = omap_32k_timer_shutdown, 1368c2ecf20Sopenharmony_ci .set_state_periodic = omap_32k_timer_set_periodic, 1378c2ecf20Sopenharmony_ci .set_state_oneshot = omap_32k_timer_shutdown, 1388c2ecf20Sopenharmony_ci .tick_resume = omap_32k_timer_shutdown, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct clock_event_device *evt = &clockevent_32k_timer; 1448c2ecf20Sopenharmony_ci omap_32k_timer_ack_irq(); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci evt->event_handler(evt); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic __init void omap_init_32k_timer(void) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci if (request_irq(INT_OS_TIMER, omap_32k_timer_interrupt, 1548c2ecf20Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL, "32KHz timer", NULL)) 1558c2ecf20Sopenharmony_ci pr_err("Failed to request irq %d(32KHz timer)\n", INT_OS_TIMER); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci clockevent_32k_timer.cpumask = cpumask_of(0); 1588c2ecf20Sopenharmony_ci clockevents_config_and_register(&clockevent_32k_timer, 1598c2ecf20Sopenharmony_ci OMAP_32K_TICKS_PER_SEC, 1, 0xfffffffe); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* 1638c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 1648c2ecf20Sopenharmony_ci * Timer initialization 1658c2ecf20Sopenharmony_ci * --------------------------------------------------------------------------- 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ciint __init omap_32k_timer_init(void) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int ret = -ENODEV; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (cpu_is_omap16xx()) { 1728c2ecf20Sopenharmony_ci void __iomem *base; 1738c2ecf20Sopenharmony_ci struct clk *sync32k_ick; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci base = ioremap(OMAP1_32KSYNC_TIMER_BASE, SZ_1K); 1768c2ecf20Sopenharmony_ci if (!base) { 1778c2ecf20Sopenharmony_ci pr_err("32k_counter: failed to map base addr\n"); 1788c2ecf20Sopenharmony_ci return -ENODEV; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci sync32k_ick = clk_get(NULL, "omap_32ksync_ick"); 1828c2ecf20Sopenharmony_ci if (!IS_ERR(sync32k_ick)) 1838c2ecf20Sopenharmony_ci clk_enable(sync32k_ick); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ret = omap_init_clocksource_32k(base); 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (!ret) 1898c2ecf20Sopenharmony_ci omap_init_32k_timer(); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return ret; 1928c2ecf20Sopenharmony_ci} 193