18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2012 Broadcom Corporation 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 68c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 98c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 108c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 118c2ecf20Sopenharmony_ci * GNU General Public License for more details. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/irq.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 188c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 198c2ecf20Sopenharmony_ci#include <linux/types.h> 208c2ecf20Sopenharmony_ci#include <linux/clk.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/io.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/of.h> 258c2ecf20Sopenharmony_ci#include <linux/of_address.h> 268c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define KONA_GPTIMER_STCS_OFFSET 0x00000000 308c2ecf20Sopenharmony_ci#define KONA_GPTIMER_STCLO_OFFSET 0x00000004 318c2ecf20Sopenharmony_ci#define KONA_GPTIMER_STCHI_OFFSET 0x00000008 328c2ecf20Sopenharmony_ci#define KONA_GPTIMER_STCM0_OFFSET 0x0000000C 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT 0 358c2ecf20Sopenharmony_ci#define KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT 4 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct kona_bcm_timers { 388c2ecf20Sopenharmony_ci int tmr_irq; 398c2ecf20Sopenharmony_ci void __iomem *tmr_regs; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic struct kona_bcm_timers timers; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic u32 arch_timer_rate; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * We use the peripheral timers for system tick, the cpu global timer for 488c2ecf20Sopenharmony_ci * profile tick 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistatic void kona_timer_disable_and_clear(void __iomem *base) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci uint32_t reg; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* 558c2ecf20Sopenharmony_ci * clear and disable interrupts 568c2ecf20Sopenharmony_ci * We are using compare/match register 0 for our system interrupts 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci reg = readl(base + KONA_GPTIMER_STCS_OFFSET); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* Clear compare (0) interrupt */ 618c2ecf20Sopenharmony_ci reg |= 1 << KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT; 628c2ecf20Sopenharmony_ci /* disable compare */ 638c2ecf20Sopenharmony_ci reg &= ~(1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci writel(reg, base + KONA_GPTIMER_STCS_OFFSET); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int 708c2ecf20Sopenharmony_cikona_timer_get_counter(void __iomem *timer_base, uint32_t *msw, uint32_t *lsw) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci int loop_limit = 3; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* 758c2ecf20Sopenharmony_ci * Read 64-bit free running counter 768c2ecf20Sopenharmony_ci * 1. Read hi-word 778c2ecf20Sopenharmony_ci * 2. Read low-word 788c2ecf20Sopenharmony_ci * 3. Read hi-word again 798c2ecf20Sopenharmony_ci * 4.1 808c2ecf20Sopenharmony_ci * if new hi-word is not equal to previously read hi-word, then 818c2ecf20Sopenharmony_ci * start from #1 828c2ecf20Sopenharmony_ci * 4.2 838c2ecf20Sopenharmony_ci * if new hi-word is equal to previously read hi-word then stop. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci do { 878c2ecf20Sopenharmony_ci *msw = readl(timer_base + KONA_GPTIMER_STCHI_OFFSET); 888c2ecf20Sopenharmony_ci *lsw = readl(timer_base + KONA_GPTIMER_STCLO_OFFSET); 898c2ecf20Sopenharmony_ci if (*msw == readl(timer_base + KONA_GPTIMER_STCHI_OFFSET)) 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci } while (--loop_limit); 928c2ecf20Sopenharmony_ci if (!loop_limit) { 938c2ecf20Sopenharmony_ci pr_err("bcm_kona_timer: getting counter failed.\n"); 948c2ecf20Sopenharmony_ci pr_err(" Timer will be impacted\n"); 958c2ecf20Sopenharmony_ci return -ETIMEDOUT; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int kona_timer_set_next_event(unsigned long clc, 1028c2ecf20Sopenharmony_ci struct clock_event_device *unused) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci /* 1058c2ecf20Sopenharmony_ci * timer (0) is disabled by the timer interrupt already 1068c2ecf20Sopenharmony_ci * so, here we reload the next event value and re-enable 1078c2ecf20Sopenharmony_ci * the timer. 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * This way, we are potentially losing the time between 1108c2ecf20Sopenharmony_ci * timer-interrupt->set_next_event. CPU local timers, when 1118c2ecf20Sopenharmony_ci * they come in should get rid of skew. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci uint32_t lsw, msw; 1158c2ecf20Sopenharmony_ci uint32_t reg; 1168c2ecf20Sopenharmony_ci int ret; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = kona_timer_get_counter(timers.tmr_regs, &msw, &lsw); 1198c2ecf20Sopenharmony_ci if (ret) 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* Load the "next" event tick value */ 1238c2ecf20Sopenharmony_ci writel(lsw + clc, timers.tmr_regs + KONA_GPTIMER_STCM0_OFFSET); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Enable compare */ 1268c2ecf20Sopenharmony_ci reg = readl(timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET); 1278c2ecf20Sopenharmony_ci reg |= (1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT); 1288c2ecf20Sopenharmony_ci writel(reg, timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int kona_timer_shutdown(struct clock_event_device *evt) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci kona_timer_disable_and_clear(timers.tmr_regs); 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic struct clock_event_device kona_clockevent_timer = { 1408c2ecf20Sopenharmony_ci .name = "timer 1", 1418c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT, 1428c2ecf20Sopenharmony_ci .set_next_event = kona_timer_set_next_event, 1438c2ecf20Sopenharmony_ci .set_state_shutdown = kona_timer_shutdown, 1448c2ecf20Sopenharmony_ci .tick_resume = kona_timer_shutdown, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void __init kona_timer_clockevents_init(void) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci kona_clockevent_timer.cpumask = cpumask_of(0); 1508c2ecf20Sopenharmony_ci clockevents_config_and_register(&kona_clockevent_timer, 1518c2ecf20Sopenharmony_ci arch_timer_rate, 6, 0xffffffff); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic irqreturn_t kona_timer_interrupt(int irq, void *dev_id) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct clock_event_device *evt = &kona_clockevent_timer; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci kona_timer_disable_and_clear(timers.tmr_regs); 1598c2ecf20Sopenharmony_ci evt->event_handler(evt); 1608c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int __init kona_timer_init(struct device_node *node) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci u32 freq; 1668c2ecf20Sopenharmony_ci struct clk *external_clk; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci external_clk = of_clk_get_by_name(node, NULL); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (!IS_ERR(external_clk)) { 1718c2ecf20Sopenharmony_ci arch_timer_rate = clk_get_rate(external_clk); 1728c2ecf20Sopenharmony_ci clk_prepare_enable(external_clk); 1738c2ecf20Sopenharmony_ci } else if (!of_property_read_u32(node, "clock-frequency", &freq)) { 1748c2ecf20Sopenharmony_ci arch_timer_rate = freq; 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci pr_err("Kona Timer v1 unable to determine clock-frequency\n"); 1778c2ecf20Sopenharmony_ci return -EINVAL; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Setup IRQ numbers */ 1818c2ecf20Sopenharmony_ci timers.tmr_irq = irq_of_parse_and_map(node, 0); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* Setup IO addresses */ 1848c2ecf20Sopenharmony_ci timers.tmr_regs = of_iomap(node, 0); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci kona_timer_disable_and_clear(timers.tmr_regs); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci kona_timer_clockevents_init(); 1898c2ecf20Sopenharmony_ci if (request_irq(timers.tmr_irq, kona_timer_interrupt, IRQF_TIMER, 1908c2ecf20Sopenharmony_ci "Kona Timer Tick", NULL)) 1918c2ecf20Sopenharmony_ci pr_err("%s: request_irq() failed\n", "Kona Timer Tick"); 1928c2ecf20Sopenharmony_ci kona_timer_set_next_event((arch_timer_rate / HZ), NULL); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(brcm_kona, "brcm,kona-timer", kona_timer_init); 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * bcm,kona-timer is deprecated by brcm,kona-timer 2008c2ecf20Sopenharmony_ci * being kept here for driver compatibility 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(bcm_kona, "bcm,kona-timer", kona_timer_init); 203