19d8d47eaSDaniel Lezcano /* 29d8d47eaSDaniel Lezcano * Marvell Orion SoC timer handling. 39d8d47eaSDaniel Lezcano * 49d8d47eaSDaniel Lezcano * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 59d8d47eaSDaniel Lezcano * 69d8d47eaSDaniel Lezcano * This file is licensed under the terms of the GNU General Public 79d8d47eaSDaniel Lezcano * License version 2. This program is licensed "as is" without any 89d8d47eaSDaniel Lezcano * warranty of any kind, whether express or implied. 99d8d47eaSDaniel Lezcano * 109d8d47eaSDaniel Lezcano * Timer 0 is used as free-running clocksource, while timer 1 is 119d8d47eaSDaniel Lezcano * used as clock_event_device. 129d8d47eaSDaniel Lezcano */ 139d8d47eaSDaniel Lezcano 149d8d47eaSDaniel Lezcano #include <linux/kernel.h> 159d8d47eaSDaniel Lezcano #include <linux/bitops.h> 169d8d47eaSDaniel Lezcano #include <linux/clk.h> 179d8d47eaSDaniel Lezcano #include <linux/clockchips.h> 189d8d47eaSDaniel Lezcano #include <linux/delay.h> 199d8d47eaSDaniel Lezcano #include <linux/interrupt.h> 209d8d47eaSDaniel Lezcano #include <linux/of_address.h> 219d8d47eaSDaniel Lezcano #include <linux/of_irq.h> 229d8d47eaSDaniel Lezcano #include <linux/spinlock.h> 239d8d47eaSDaniel Lezcano #include <linux/sched_clock.h> 249d8d47eaSDaniel Lezcano 259d8d47eaSDaniel Lezcano #define TIMER_CTRL 0x00 269d8d47eaSDaniel Lezcano #define TIMER0_EN BIT(0) 279d8d47eaSDaniel Lezcano #define TIMER0_RELOAD_EN BIT(1) 289d8d47eaSDaniel Lezcano #define TIMER1_EN BIT(2) 299d8d47eaSDaniel Lezcano #define TIMER1_RELOAD_EN BIT(3) 309d8d47eaSDaniel Lezcano #define TIMER0_RELOAD 0x10 319d8d47eaSDaniel Lezcano #define TIMER0_VAL 0x14 329d8d47eaSDaniel Lezcano #define TIMER1_RELOAD 0x18 339d8d47eaSDaniel Lezcano #define TIMER1_VAL 0x1c 349d8d47eaSDaniel Lezcano 359d8d47eaSDaniel Lezcano #define ORION_ONESHOT_MIN 1 369d8d47eaSDaniel Lezcano #define ORION_ONESHOT_MAX 0xfffffffe 379d8d47eaSDaniel Lezcano 389d8d47eaSDaniel Lezcano static void __iomem *timer_base; 399d8d47eaSDaniel Lezcano 409d8d47eaSDaniel Lezcano static unsigned long notrace orion_read_timer(void) 419d8d47eaSDaniel Lezcano { 429d8d47eaSDaniel Lezcano return ~readl(timer_base + TIMER0_VAL); 439d8d47eaSDaniel Lezcano } 449d8d47eaSDaniel Lezcano 459d8d47eaSDaniel Lezcano static struct delay_timer orion_delay_timer = { 469d8d47eaSDaniel Lezcano .read_current_timer = orion_read_timer, 479d8d47eaSDaniel Lezcano }; 489d8d47eaSDaniel Lezcano 499d8d47eaSDaniel Lezcano static void orion_delay_timer_init(unsigned long rate) 509d8d47eaSDaniel Lezcano { 519d8d47eaSDaniel Lezcano orion_delay_timer.freq = rate; 529d8d47eaSDaniel Lezcano register_current_timer_delay(&orion_delay_timer); 539d8d47eaSDaniel Lezcano } 549d8d47eaSDaniel Lezcano 559d8d47eaSDaniel Lezcano /* 569d8d47eaSDaniel Lezcano * Free-running clocksource handling. 579d8d47eaSDaniel Lezcano */ 589d8d47eaSDaniel Lezcano static u64 notrace orion_read_sched_clock(void) 599d8d47eaSDaniel Lezcano { 609d8d47eaSDaniel Lezcano return ~readl(timer_base + TIMER0_VAL); 619d8d47eaSDaniel Lezcano } 629d8d47eaSDaniel Lezcano 639d8d47eaSDaniel Lezcano /* 649d8d47eaSDaniel Lezcano * Clockevent handling. 659d8d47eaSDaniel Lezcano */ 669d8d47eaSDaniel Lezcano static u32 ticks_per_jiffy; 679d8d47eaSDaniel Lezcano 689d8d47eaSDaniel Lezcano static int orion_clkevt_next_event(unsigned long delta, 699d8d47eaSDaniel Lezcano struct clock_event_device *dev) 709d8d47eaSDaniel Lezcano { 719d8d47eaSDaniel Lezcano /* setup and enable one-shot timer */ 729d8d47eaSDaniel Lezcano writel(delta, timer_base + TIMER1_VAL); 739d8d47eaSDaniel Lezcano atomic_io_modify(timer_base + TIMER_CTRL, 749d8d47eaSDaniel Lezcano TIMER1_RELOAD_EN | TIMER1_EN, TIMER1_EN); 759d8d47eaSDaniel Lezcano 769d8d47eaSDaniel Lezcano return 0; 779d8d47eaSDaniel Lezcano } 789d8d47eaSDaniel Lezcano 799d8d47eaSDaniel Lezcano static int orion_clkevt_shutdown(struct clock_event_device *dev) 809d8d47eaSDaniel Lezcano { 819d8d47eaSDaniel Lezcano /* disable timer */ 829d8d47eaSDaniel Lezcano atomic_io_modify(timer_base + TIMER_CTRL, 839d8d47eaSDaniel Lezcano TIMER1_RELOAD_EN | TIMER1_EN, 0); 849d8d47eaSDaniel Lezcano return 0; 859d8d47eaSDaniel Lezcano } 869d8d47eaSDaniel Lezcano 879d8d47eaSDaniel Lezcano static int orion_clkevt_set_periodic(struct clock_event_device *dev) 889d8d47eaSDaniel Lezcano { 899d8d47eaSDaniel Lezcano /* setup and enable periodic timer at 1/HZ intervals */ 909d8d47eaSDaniel Lezcano writel(ticks_per_jiffy - 1, timer_base + TIMER1_RELOAD); 919d8d47eaSDaniel Lezcano writel(ticks_per_jiffy - 1, timer_base + TIMER1_VAL); 929d8d47eaSDaniel Lezcano atomic_io_modify(timer_base + TIMER_CTRL, 939d8d47eaSDaniel Lezcano TIMER1_RELOAD_EN | TIMER1_EN, 949d8d47eaSDaniel Lezcano TIMER1_RELOAD_EN | TIMER1_EN); 959d8d47eaSDaniel Lezcano return 0; 969d8d47eaSDaniel Lezcano } 979d8d47eaSDaniel Lezcano 989d8d47eaSDaniel Lezcano static struct clock_event_device orion_clkevt = { 999d8d47eaSDaniel Lezcano .name = "orion_event", 1009d8d47eaSDaniel Lezcano .features = CLOCK_EVT_FEAT_ONESHOT | 1019d8d47eaSDaniel Lezcano CLOCK_EVT_FEAT_PERIODIC, 1029d8d47eaSDaniel Lezcano .shift = 32, 1039d8d47eaSDaniel Lezcano .rating = 300, 1049d8d47eaSDaniel Lezcano .set_next_event = orion_clkevt_next_event, 1059d8d47eaSDaniel Lezcano .set_state_shutdown = orion_clkevt_shutdown, 1069d8d47eaSDaniel Lezcano .set_state_periodic = orion_clkevt_set_periodic, 1079d8d47eaSDaniel Lezcano .set_state_oneshot = orion_clkevt_shutdown, 1089d8d47eaSDaniel Lezcano .tick_resume = orion_clkevt_shutdown, 1099d8d47eaSDaniel Lezcano }; 1109d8d47eaSDaniel Lezcano 1119d8d47eaSDaniel Lezcano static irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id) 1129d8d47eaSDaniel Lezcano { 1139d8d47eaSDaniel Lezcano orion_clkevt.event_handler(&orion_clkevt); 1149d8d47eaSDaniel Lezcano return IRQ_HANDLED; 1159d8d47eaSDaniel Lezcano } 1169d8d47eaSDaniel Lezcano 1179d8d47eaSDaniel Lezcano static int __init orion_timer_init(struct device_node *np) 1189d8d47eaSDaniel Lezcano { 1199d8d47eaSDaniel Lezcano unsigned long rate; 1209d8d47eaSDaniel Lezcano struct clk *clk; 1219d8d47eaSDaniel Lezcano int irq, ret; 1229d8d47eaSDaniel Lezcano 1239d8d47eaSDaniel Lezcano /* timer registers are shared with watchdog timer */ 1249d8d47eaSDaniel Lezcano timer_base = of_iomap(np, 0); 1259d8d47eaSDaniel Lezcano if (!timer_base) { 1269d8d47eaSDaniel Lezcano pr_err("%pOFn: unable to map resource\n", np); 1279d8d47eaSDaniel Lezcano return -ENXIO; 1289d8d47eaSDaniel Lezcano } 1299d8d47eaSDaniel Lezcano 1309d8d47eaSDaniel Lezcano clk = of_clk_get(np, 0); 1319d8d47eaSDaniel Lezcano if (IS_ERR(clk)) { 1329d8d47eaSDaniel Lezcano pr_err("%pOFn: unable to get clk\n", np); 1339d8d47eaSDaniel Lezcano return PTR_ERR(clk); 1349d8d47eaSDaniel Lezcano } 1359d8d47eaSDaniel Lezcano 1369d8d47eaSDaniel Lezcano ret = clk_prepare_enable(clk); 1379d8d47eaSDaniel Lezcano if (ret) { 1389d8d47eaSDaniel Lezcano pr_err("Failed to prepare clock\n"); 1399d8d47eaSDaniel Lezcano return ret; 1409d8d47eaSDaniel Lezcano } 1419d8d47eaSDaniel Lezcano 1429d8d47eaSDaniel Lezcano /* we are only interested in timer1 irq */ 1439d8d47eaSDaniel Lezcano irq = irq_of_parse_and_map(np, 1); 1449d8d47eaSDaniel Lezcano if (irq <= 0) { 1459d8d47eaSDaniel Lezcano pr_err("%pOFn: unable to parse timer1 irq\n", np); 1469d8d47eaSDaniel Lezcano return -EINVAL; 1479d8d47eaSDaniel Lezcano } 1489d8d47eaSDaniel Lezcano 1499d8d47eaSDaniel Lezcano rate = clk_get_rate(clk); 1509d8d47eaSDaniel Lezcano 1519d8d47eaSDaniel Lezcano /* setup timer0 as free-running clocksource */ 1529d8d47eaSDaniel Lezcano writel(~0, timer_base + TIMER0_VAL); 1539d8d47eaSDaniel Lezcano writel(~0, timer_base + TIMER0_RELOAD); 1549d8d47eaSDaniel Lezcano atomic_io_modify(timer_base + TIMER_CTRL, 1559d8d47eaSDaniel Lezcano TIMER0_RELOAD_EN | TIMER0_EN, 1569d8d47eaSDaniel Lezcano TIMER0_RELOAD_EN | TIMER0_EN); 1579d8d47eaSDaniel Lezcano 1589d8d47eaSDaniel Lezcano ret = clocksource_mmio_init(timer_base + TIMER0_VAL, 1599d8d47eaSDaniel Lezcano "orion_clocksource", rate, 300, 32, 1609d8d47eaSDaniel Lezcano clocksource_mmio_readl_down); 1619d8d47eaSDaniel Lezcano if (ret) { 1629d8d47eaSDaniel Lezcano pr_err("Failed to initialize mmio timer\n"); 1639d8d47eaSDaniel Lezcano return ret; 1649d8d47eaSDaniel Lezcano } 1659d8d47eaSDaniel Lezcano 1669d8d47eaSDaniel Lezcano sched_clock_register(orion_read_sched_clock, 32, rate); 1679d8d47eaSDaniel Lezcano 1689d8d47eaSDaniel Lezcano /* setup timer1 as clockevent timer */ 169*cc2550b4Safzal mohammed ret = request_irq(irq, orion_clkevt_irq_handler, IRQF_TIMER, 170*cc2550b4Safzal mohammed "orion_event", NULL); 1719d8d47eaSDaniel Lezcano if (ret) { 1729d8d47eaSDaniel Lezcano pr_err("%pOFn: unable to setup irq\n", np); 1739d8d47eaSDaniel Lezcano return ret; 1749d8d47eaSDaniel Lezcano } 1759d8d47eaSDaniel Lezcano 1769d8d47eaSDaniel Lezcano ticks_per_jiffy = (clk_get_rate(clk) + HZ/2) / HZ; 1779d8d47eaSDaniel Lezcano orion_clkevt.cpumask = cpumask_of(0); 1789d8d47eaSDaniel Lezcano orion_clkevt.irq = irq; 1799d8d47eaSDaniel Lezcano clockevents_config_and_register(&orion_clkevt, rate, 1809d8d47eaSDaniel Lezcano ORION_ONESHOT_MIN, ORION_ONESHOT_MAX); 1819d8d47eaSDaniel Lezcano 1829d8d47eaSDaniel Lezcano 1839d8d47eaSDaniel Lezcano orion_delay_timer_init(rate); 1849d8d47eaSDaniel Lezcano 1859d8d47eaSDaniel Lezcano return 0; 1869d8d47eaSDaniel Lezcano } 1879d8d47eaSDaniel Lezcano TIMER_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init); 188