18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * arch/arm/plat-orion/time.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Marvell Orion SoC timer handling. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 78c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 88c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Timer 0 is used as free-running clocksource, while timer 1 is 118c2ecf20Sopenharmony_ci * used as clock_event_device. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/timer.h> 168c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/irq.h> 198c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 208c2ecf20Sopenharmony_ci#include <plat/time.h> 218c2ecf20Sopenharmony_ci#include <asm/delay.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * MBus bridge block registers. 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ci#define BRIDGE_CAUSE_OFF 0x0110 278c2ecf20Sopenharmony_ci#define BRIDGE_MASK_OFF 0x0114 288c2ecf20Sopenharmony_ci#define BRIDGE_INT_TIMER0 0x0002 298c2ecf20Sopenharmony_ci#define BRIDGE_INT_TIMER1 0x0004 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* 338c2ecf20Sopenharmony_ci * Timer block registers. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci#define TIMER_CTRL_OFF 0x0000 368c2ecf20Sopenharmony_ci#define TIMER0_EN 0x0001 378c2ecf20Sopenharmony_ci#define TIMER0_RELOAD_EN 0x0002 388c2ecf20Sopenharmony_ci#define TIMER1_EN 0x0004 398c2ecf20Sopenharmony_ci#define TIMER1_RELOAD_EN 0x0008 408c2ecf20Sopenharmony_ci#define TIMER0_RELOAD_OFF 0x0010 418c2ecf20Sopenharmony_ci#define TIMER0_VAL_OFF 0x0014 428c2ecf20Sopenharmony_ci#define TIMER1_RELOAD_OFF 0x0018 438c2ecf20Sopenharmony_ci#define TIMER1_VAL_OFF 0x001c 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * SoC-specific data. 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_cistatic void __iomem *bridge_base; 508c2ecf20Sopenharmony_cistatic u32 bridge_timer1_clr_mask; 518c2ecf20Sopenharmony_cistatic void __iomem *timer_base; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 558c2ecf20Sopenharmony_ci * Number of timer ticks per jiffy. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_cistatic u32 ticks_per_jiffy; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Orion's sched_clock implementation. It has a resolution of 628c2ecf20Sopenharmony_ci * at least 7.5ns (133MHz TCLK). 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic u64 notrace orion_read_sched_clock(void) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci return ~readl(timer_base + TIMER0_VAL_OFF); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* 718c2ecf20Sopenharmony_ci * Clockevent handling. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistatic int 748c2ecf20Sopenharmony_ciorion_clkevt_next_event(unsigned long delta, struct clock_event_device *dev) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci unsigned long flags; 778c2ecf20Sopenharmony_ci u32 u; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (delta == 0) 808c2ecf20Sopenharmony_ci return -ETIME; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci local_irq_save(flags); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* 858c2ecf20Sopenharmony_ci * Clear and enable clockevent timer interrupt. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci u = readl(bridge_base + BRIDGE_MASK_OFF); 908c2ecf20Sopenharmony_ci u |= BRIDGE_INT_TIMER1; 918c2ecf20Sopenharmony_ci writel(u, bridge_base + BRIDGE_MASK_OFF); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * Setup new clockevent timer value. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci writel(delta, timer_base + TIMER1_VAL_OFF); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* 998c2ecf20Sopenharmony_ci * Enable the timer. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci u = readl(timer_base + TIMER_CTRL_OFF); 1028c2ecf20Sopenharmony_ci u = (u & ~TIMER1_RELOAD_EN) | TIMER1_EN; 1038c2ecf20Sopenharmony_ci writel(u, timer_base + TIMER_CTRL_OFF); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci local_irq_restore(flags); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int orion_clkevt_shutdown(struct clock_event_device *evt) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci unsigned long flags; 1138c2ecf20Sopenharmony_ci u32 u; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci local_irq_save(flags); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Disable timer */ 1188c2ecf20Sopenharmony_ci u = readl(timer_base + TIMER_CTRL_OFF); 1198c2ecf20Sopenharmony_ci writel(u & ~TIMER1_EN, timer_base + TIMER_CTRL_OFF); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* Disable timer interrupt */ 1228c2ecf20Sopenharmony_ci u = readl(bridge_base + BRIDGE_MASK_OFF); 1238c2ecf20Sopenharmony_ci writel(u & ~BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* ACK pending timer interrupt */ 1268c2ecf20Sopenharmony_ci writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci local_irq_restore(flags); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int orion_clkevt_set_periodic(struct clock_event_device *evt) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci unsigned long flags; 1368c2ecf20Sopenharmony_ci u32 u; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci local_irq_save(flags); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Setup timer to fire at 1/HZ intervals */ 1418c2ecf20Sopenharmony_ci writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD_OFF); 1428c2ecf20Sopenharmony_ci writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL_OFF); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Enable timer interrupt */ 1458c2ecf20Sopenharmony_ci u = readl(bridge_base + BRIDGE_MASK_OFF); 1468c2ecf20Sopenharmony_ci writel(u | BRIDGE_INT_TIMER1, bridge_base + BRIDGE_MASK_OFF); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* Enable timer */ 1498c2ecf20Sopenharmony_ci u = readl(timer_base + TIMER_CTRL_OFF); 1508c2ecf20Sopenharmony_ci writel(u | TIMER1_EN | TIMER1_RELOAD_EN, timer_base + TIMER_CTRL_OFF); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci local_irq_restore(flags); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic struct clock_event_device orion_clkevt = { 1588c2ecf20Sopenharmony_ci .name = "orion_tick", 1598c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT | 1608c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_PERIODIC, 1618c2ecf20Sopenharmony_ci .rating = 300, 1628c2ecf20Sopenharmony_ci .set_next_event = orion_clkevt_next_event, 1638c2ecf20Sopenharmony_ci .set_state_shutdown = orion_clkevt_shutdown, 1648c2ecf20Sopenharmony_ci .set_state_periodic = orion_clkevt_set_periodic, 1658c2ecf20Sopenharmony_ci .set_state_oneshot = orion_clkevt_shutdown, 1668c2ecf20Sopenharmony_ci .tick_resume = orion_clkevt_shutdown, 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic irqreturn_t orion_timer_interrupt(int irq, void *dev_id) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci /* 1728c2ecf20Sopenharmony_ci * ACK timer interrupt and call event handler. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci writel(bridge_timer1_clr_mask, bridge_base + BRIDGE_CAUSE_OFF); 1758c2ecf20Sopenharmony_ci orion_clkevt.event_handler(&orion_clkevt); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_civoid __init 1818c2ecf20Sopenharmony_ciorion_time_set_base(void __iomem *_timer_base) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci timer_base = _timer_base; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic unsigned long orion_delay_timer_read(void) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci return ~readl(timer_base + TIMER0_VAL_OFF); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic struct delay_timer orion_delay_timer = { 1928c2ecf20Sopenharmony_ci .read_current_timer = orion_delay_timer_read, 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_civoid __init 1968c2ecf20Sopenharmony_ciorion_time_init(void __iomem *_bridge_base, u32 _bridge_timer1_clr_mask, 1978c2ecf20Sopenharmony_ci unsigned int irq, unsigned int tclk) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci u32 u; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* 2028c2ecf20Sopenharmony_ci * Set SoC-specific data. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci bridge_base = _bridge_base; 2058c2ecf20Sopenharmony_ci bridge_timer1_clr_mask = _bridge_timer1_clr_mask; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ticks_per_jiffy = (tclk + HZ/2) / HZ; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci orion_delay_timer.freq = tclk; 2108c2ecf20Sopenharmony_ci register_current_timer_delay(&orion_delay_timer); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* 2138c2ecf20Sopenharmony_ci * Set scale and timer for sched_clock. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_ci sched_clock_register(orion_read_sched_clock, 32, tclk); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* 2188c2ecf20Sopenharmony_ci * Setup free-running clocksource timer (interrupts 2198c2ecf20Sopenharmony_ci * disabled). 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci writel(0xffffffff, timer_base + TIMER0_VAL_OFF); 2228c2ecf20Sopenharmony_ci writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF); 2238c2ecf20Sopenharmony_ci u = readl(bridge_base + BRIDGE_MASK_OFF); 2248c2ecf20Sopenharmony_ci writel(u & ~BRIDGE_INT_TIMER0, bridge_base + BRIDGE_MASK_OFF); 2258c2ecf20Sopenharmony_ci u = readl(timer_base + TIMER_CTRL_OFF); 2268c2ecf20Sopenharmony_ci writel(u | TIMER0_EN | TIMER0_RELOAD_EN, timer_base + TIMER_CTRL_OFF); 2278c2ecf20Sopenharmony_ci clocksource_mmio_init(timer_base + TIMER0_VAL_OFF, "orion_clocksource", 2288c2ecf20Sopenharmony_ci tclk, 300, 32, clocksource_mmio_readl_down); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* 2318c2ecf20Sopenharmony_ci * Setup clockevent timer (interrupt-driven). 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_ci if (request_irq(irq, orion_timer_interrupt, IRQF_TIMER, "orion_tick", 2348c2ecf20Sopenharmony_ci NULL)) 2358c2ecf20Sopenharmony_ci pr_err("Failed to request irq %u (orion_tick)\n", irq); 2368c2ecf20Sopenharmony_ci orion_clkevt.cpumask = cpumask_of(0); 2378c2ecf20Sopenharmony_ci clockevents_config_and_register(&orion_clkevt, tclk, 1, 0xfffffffe); 2388c2ecf20Sopenharmony_ci} 239