18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/arch/arm/mach-mmp/time.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Support for clocksource and clockevents 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2008 Marvell International Ltd. 88c2ecf20Sopenharmony_ci * All rights reserved. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * 2008-04-11: Jason Chagas <Jason.chagas@marvell.com> 118c2ecf20Sopenharmony_ci * 2008-10-08: Bin Yang <bin.yang@marvell.com> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * The timers module actually includes three timers, each timer with up to 148c2ecf20Sopenharmony_ci * three match comparators. Timer #0 is used here in free-running mode as 158c2ecf20Sopenharmony_ci * the clock source, and match comparator #1 used as clock event device. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 218c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 228c2ecf20Sopenharmony_ci#include <linux/clk.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/io.h> 258c2ecf20Sopenharmony_ci#include <linux/irq.h> 268c2ecf20Sopenharmony_ci#include <linux/of.h> 278c2ecf20Sopenharmony_ci#include <linux/of_address.h> 288c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 298c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 308c2ecf20Sopenharmony_ci#include <asm/mach/time.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "addr-map.h" 338c2ecf20Sopenharmony_ci#include "regs-timers.h" 348c2ecf20Sopenharmony_ci#include "regs-apbc.h" 358c2ecf20Sopenharmony_ci#include "irqs.h" 368c2ecf20Sopenharmony_ci#include <linux/soc/mmp/cputype.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define TIMERS_VIRT_BASE TIMERS1_VIRT_BASE 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define MAX_DELTA (0xfffffffe) 418c2ecf20Sopenharmony_ci#define MIN_DELTA (16) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void __iomem *mmp_timer_base = TIMERS_VIRT_BASE; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * Read the timer through the CVWR register. Delay is required after requesting 478c2ecf20Sopenharmony_ci * a read. The CR register cannot be directly read due to metastability issues 488c2ecf20Sopenharmony_ci * documented in the PXA168 software manual. 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistatic inline uint32_t timer_read(void) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci uint32_t val; 538c2ecf20Sopenharmony_ci int delay = 3; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci __raw_writel(1, mmp_timer_base + TMR_CVWR(1)); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci while (delay--) 588c2ecf20Sopenharmony_ci val = __raw_readl(mmp_timer_base + TMR_CVWR(1)); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return val; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic u64 notrace mmp_read_sched_clock(void) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci return timer_read(); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic irqreturn_t timer_interrupt(int irq, void *dev_id) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct clock_event_device *c = dev_id; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* 738c2ecf20Sopenharmony_ci * Clear pending interrupt status. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci __raw_writel(0x01, mmp_timer_base + TMR_ICR(0)); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * Disable timer 0. 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci __raw_writel(0x02, mmp_timer_base + TMR_CER); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci c->event_handler(c); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return IRQ_HANDLED; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int timer_set_next_event(unsigned long delta, 888c2ecf20Sopenharmony_ci struct clock_event_device *dev) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci unsigned long flags; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci local_irq_save(flags); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* 958c2ecf20Sopenharmony_ci * Disable timer 0. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci __raw_writel(0x02, mmp_timer_base + TMR_CER); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * Clear and enable timer match 0 interrupt. 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci __raw_writel(0x01, mmp_timer_base + TMR_ICR(0)); 1038c2ecf20Sopenharmony_ci __raw_writel(0x01, mmp_timer_base + TMR_IER(0)); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* 1068c2ecf20Sopenharmony_ci * Setup new clockevent timer value. 1078c2ecf20Sopenharmony_ci */ 1088c2ecf20Sopenharmony_ci __raw_writel(delta - 1, mmp_timer_base + TMR_TN_MM(0, 0)); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* 1118c2ecf20Sopenharmony_ci * Enable timer 0. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci __raw_writel(0x03, mmp_timer_base + TMR_CER); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci local_irq_restore(flags); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int timer_set_shutdown(struct clock_event_device *evt) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci unsigned long flags; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci local_irq_save(flags); 1258c2ecf20Sopenharmony_ci /* disable the matching interrupt */ 1268c2ecf20Sopenharmony_ci __raw_writel(0x00, mmp_timer_base + TMR_IER(0)); 1278c2ecf20Sopenharmony_ci local_irq_restore(flags); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic struct clock_event_device ckevt = { 1338c2ecf20Sopenharmony_ci .name = "clockevent", 1348c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT, 1358c2ecf20Sopenharmony_ci .rating = 200, 1368c2ecf20Sopenharmony_ci .set_next_event = timer_set_next_event, 1378c2ecf20Sopenharmony_ci .set_state_shutdown = timer_set_shutdown, 1388c2ecf20Sopenharmony_ci .set_state_oneshot = timer_set_shutdown, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic u64 clksrc_read(struct clocksource *cs) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci return timer_read(); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic struct clocksource cksrc = { 1478c2ecf20Sopenharmony_ci .name = "clocksource", 1488c2ecf20Sopenharmony_ci .rating = 200, 1498c2ecf20Sopenharmony_ci .read = clksrc_read, 1508c2ecf20Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 1518c2ecf20Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void __init timer_config(void) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci uint32_t ccr = __raw_readl(mmp_timer_base + TMR_CCR); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci __raw_writel(0x0, mmp_timer_base + TMR_CER); /* disable */ 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci ccr &= (cpu_is_mmp2() || cpu_is_mmp3()) ? 1618c2ecf20Sopenharmony_ci (TMR_CCR_CS_0(0) | TMR_CCR_CS_1(0)) : 1628c2ecf20Sopenharmony_ci (TMR_CCR_CS_0(3) | TMR_CCR_CS_1(3)); 1638c2ecf20Sopenharmony_ci __raw_writel(ccr, mmp_timer_base + TMR_CCR); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* set timer 0 to periodic mode, and timer 1 to free-running mode */ 1668c2ecf20Sopenharmony_ci __raw_writel(0x2, mmp_timer_base + TMR_CMR); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci __raw_writel(0x1, mmp_timer_base + TMR_PLCR(0)); /* periodic */ 1698c2ecf20Sopenharmony_ci __raw_writel(0x7, mmp_timer_base + TMR_ICR(0)); /* clear status */ 1708c2ecf20Sopenharmony_ci __raw_writel(0x0, mmp_timer_base + TMR_IER(0)); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci __raw_writel(0x0, mmp_timer_base + TMR_PLCR(1)); /* free-running */ 1738c2ecf20Sopenharmony_ci __raw_writel(0x7, mmp_timer_base + TMR_ICR(1)); /* clear status */ 1748c2ecf20Sopenharmony_ci __raw_writel(0x0, mmp_timer_base + TMR_IER(1)); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* enable timer 1 counter */ 1778c2ecf20Sopenharmony_ci __raw_writel(0x2, mmp_timer_base + TMR_CER); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_civoid __init mmp_timer_init(int irq, unsigned long rate) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci timer_config(); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci sched_clock_register(mmp_read_sched_clock, 32, rate); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ckevt.cpumask = cpumask_of(0); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (request_irq(irq, timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, 1898c2ecf20Sopenharmony_ci "timer", &ckevt)) 1908c2ecf20Sopenharmony_ci pr_err("Failed to request irq %d (timer)\n", irq); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci clocksource_register_hz(&cksrc, rate); 1938c2ecf20Sopenharmony_ci clockevents_config_and_register(&ckevt, rate, MIN_DELTA, MAX_DELTA); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int __init mmp_dt_init_timer(struct device_node *np) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct clk *clk; 1998c2ecf20Sopenharmony_ci int irq, ret; 2008c2ecf20Sopenharmony_ci unsigned long rate; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci clk = of_clk_get(np, 0); 2038c2ecf20Sopenharmony_ci if (!IS_ERR(clk)) { 2048c2ecf20Sopenharmony_ci ret = clk_prepare_enable(clk); 2058c2ecf20Sopenharmony_ci if (ret) 2068c2ecf20Sopenharmony_ci return ret; 2078c2ecf20Sopenharmony_ci rate = clk_get_rate(clk); 2088c2ecf20Sopenharmony_ci } else if (cpu_is_pj4()) { 2098c2ecf20Sopenharmony_ci rate = 6500000; 2108c2ecf20Sopenharmony_ci } else { 2118c2ecf20Sopenharmony_ci rate = 3250000; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(np, 0); 2158c2ecf20Sopenharmony_ci if (!irq) 2168c2ecf20Sopenharmony_ci return -EINVAL; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci mmp_timer_base = of_iomap(np, 0); 2198c2ecf20Sopenharmony_ci if (!mmp_timer_base) 2208c2ecf20Sopenharmony_ci return -ENOMEM; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci mmp_timer_init(irq, rate); 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(mmp_timer, "mrvl,mmp-timer", mmp_dt_init_timer); 227