18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Marvell Orion SoC timer handling. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 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/bitops.h> 168c2ecf20Sopenharmony_ci#include <linux/clk.h> 178c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 228c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 238c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define TIMER_CTRL 0x00 268c2ecf20Sopenharmony_ci#define TIMER0_EN BIT(0) 278c2ecf20Sopenharmony_ci#define TIMER0_RELOAD_EN BIT(1) 288c2ecf20Sopenharmony_ci#define TIMER1_EN BIT(2) 298c2ecf20Sopenharmony_ci#define TIMER1_RELOAD_EN BIT(3) 308c2ecf20Sopenharmony_ci#define TIMER0_RELOAD 0x10 318c2ecf20Sopenharmony_ci#define TIMER0_VAL 0x14 328c2ecf20Sopenharmony_ci#define TIMER1_RELOAD 0x18 338c2ecf20Sopenharmony_ci#define TIMER1_VAL 0x1c 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define ORION_ONESHOT_MIN 1 368c2ecf20Sopenharmony_ci#define ORION_ONESHOT_MAX 0xfffffffe 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void __iomem *timer_base; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic unsigned long notrace orion_read_timer(void) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci return ~readl(timer_base + TIMER0_VAL); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic struct delay_timer orion_delay_timer = { 468c2ecf20Sopenharmony_ci .read_current_timer = orion_read_timer, 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic void orion_delay_timer_init(unsigned long rate) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci orion_delay_timer.freq = rate; 528c2ecf20Sopenharmony_ci register_current_timer_delay(&orion_delay_timer); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * Free-running clocksource handling. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_cistatic u64 notrace orion_read_sched_clock(void) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci return ~readl(timer_base + TIMER0_VAL); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * Clockevent handling. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_cistatic u32 ticks_per_jiffy; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int orion_clkevt_next_event(unsigned long delta, 698c2ecf20Sopenharmony_ci struct clock_event_device *dev) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci /* setup and enable one-shot timer */ 728c2ecf20Sopenharmony_ci writel(delta, timer_base + TIMER1_VAL); 738c2ecf20Sopenharmony_ci atomic_io_modify(timer_base + TIMER_CTRL, 748c2ecf20Sopenharmony_ci TIMER1_RELOAD_EN | TIMER1_EN, TIMER1_EN); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int orion_clkevt_shutdown(struct clock_event_device *dev) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci /* disable timer */ 828c2ecf20Sopenharmony_ci atomic_io_modify(timer_base + TIMER_CTRL, 838c2ecf20Sopenharmony_ci TIMER1_RELOAD_EN | TIMER1_EN, 0); 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int orion_clkevt_set_periodic(struct clock_event_device *dev) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci /* setup and enable periodic timer at 1/HZ intervals */ 908c2ecf20Sopenharmony_ci writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD); 918c2ecf20Sopenharmony_ci writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL); 928c2ecf20Sopenharmony_ci atomic_io_modify(timer_base + TIMER_CTRL, 938c2ecf20Sopenharmony_ci TIMER1_RELOAD_EN | TIMER1_EN, 948c2ecf20Sopenharmony_ci TIMER1_RELOAD_EN | TIMER1_EN); 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic struct clock_event_device orion_clkevt = { 998c2ecf20Sopenharmony_ci .name = "orion_event", 1008c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_ONESHOT | 1018c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_PERIODIC, 1028c2ecf20Sopenharmony_ci .shift = 32, 1038c2ecf20Sopenharmony_ci .rating = 300, 1048c2ecf20Sopenharmony_ci .set_next_event = orion_clkevt_next_event, 1058c2ecf20Sopenharmony_ci .set_state_shutdown = orion_clkevt_shutdown, 1068c2ecf20Sopenharmony_ci .set_state_periodic = orion_clkevt_set_periodic, 1078c2ecf20Sopenharmony_ci .set_state_oneshot = orion_clkevt_shutdown, 1088c2ecf20Sopenharmony_ci .tick_resume = orion_clkevt_shutdown, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci orion_clkevt.event_handler(&orion_clkevt); 1148c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int __init orion_timer_init(struct device_node *np) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci unsigned long rate; 1208c2ecf20Sopenharmony_ci struct clk *clk; 1218c2ecf20Sopenharmony_ci int irq, ret; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* timer registers are shared with watchdog timer */ 1248c2ecf20Sopenharmony_ci timer_base = of_iomap(np, 0); 1258c2ecf20Sopenharmony_ci if (!timer_base) { 1268c2ecf20Sopenharmony_ci pr_err("%pOFn: unable to map resource\n", np); 1278c2ecf20Sopenharmony_ci return -ENXIO; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci clk = of_clk_get(np, 0); 1318c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 1328c2ecf20Sopenharmony_ci pr_err("%pOFn: unable to get clk\n", np); 1338c2ecf20Sopenharmony_ci return PTR_ERR(clk); 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ret = clk_prepare_enable(clk); 1378c2ecf20Sopenharmony_ci if (ret) { 1388c2ecf20Sopenharmony_ci pr_err("Failed to prepare clock\n"); 1398c2ecf20Sopenharmony_ci return ret; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* we are only interested in timer1 irq */ 1438c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(np, 1); 1448c2ecf20Sopenharmony_ci if (irq <= 0) { 1458c2ecf20Sopenharmony_ci pr_err("%pOFn: unable to parse timer1 irq\n", np); 1468c2ecf20Sopenharmony_ci ret = -EINVAL; 1478c2ecf20Sopenharmony_ci goto out_unprep_clk; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci rate = clk_get_rate(clk); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* setup timer0 as free-running clocksource */ 1538c2ecf20Sopenharmony_ci writel(~0, timer_base + TIMER0_VAL); 1548c2ecf20Sopenharmony_ci writel(~0, timer_base + TIMER0_RELOAD); 1558c2ecf20Sopenharmony_ci atomic_io_modify(timer_base + TIMER_CTRL, 1568c2ecf20Sopenharmony_ci TIMER0_RELOAD_EN | TIMER0_EN, 1578c2ecf20Sopenharmony_ci TIMER0_RELOAD_EN | TIMER0_EN); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ret = clocksource_mmio_init(timer_base + TIMER0_VAL, 1608c2ecf20Sopenharmony_ci "orion_clocksource", rate, 300, 32, 1618c2ecf20Sopenharmony_ci clocksource_mmio_readl_down); 1628c2ecf20Sopenharmony_ci if (ret) { 1638c2ecf20Sopenharmony_ci pr_err("Failed to initialize mmio timer\n"); 1648c2ecf20Sopenharmony_ci goto out_unprep_clk; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci sched_clock_register(orion_read_sched_clock, 32, rate); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* setup timer1 as clockevent timer */ 1708c2ecf20Sopenharmony_ci ret = request_irq(irq, orion_clkevt_irq_handler, IRQF_TIMER, 1718c2ecf20Sopenharmony_ci "orion_event", NULL); 1728c2ecf20Sopenharmony_ci if (ret) { 1738c2ecf20Sopenharmony_ci pr_err("%pOFn: unable to setup irq\n", np); 1748c2ecf20Sopenharmony_ci goto out_unprep_clk; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci ticks_per_jiffy = (clk_get_rate(clk) + HZ/2) / HZ; 1788c2ecf20Sopenharmony_ci orion_clkevt.cpumask = cpumask_of(0); 1798c2ecf20Sopenharmony_ci orion_clkevt.irq = irq; 1808c2ecf20Sopenharmony_ci clockevents_config_and_register(&orion_clkevt, rate, 1818c2ecf20Sopenharmony_ci ORION_ONESHOT_MIN, ORION_ONESHOT_MAX); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci orion_delay_timer_init(rate); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ciout_unprep_clk: 1898c2ecf20Sopenharmony_ci clk_disable_unprepare(clk); 1908c2ecf20Sopenharmony_ci return ret; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init); 193