19d8d47eaSDaniel Lezcano /* 29d8d47eaSDaniel Lezcano * Marvell Armada 370/XP SoC timer handling. 39d8d47eaSDaniel Lezcano * 49d8d47eaSDaniel Lezcano * Copyright (C) 2012 Marvell 59d8d47eaSDaniel Lezcano * 69d8d47eaSDaniel Lezcano * Lior Amsalem <alior@marvell.com> 79d8d47eaSDaniel Lezcano * Gregory CLEMENT <gregory.clement@free-electrons.com> 89d8d47eaSDaniel Lezcano * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 99d8d47eaSDaniel Lezcano * 109d8d47eaSDaniel Lezcano * This file is licensed under the terms of the GNU General Public 119d8d47eaSDaniel Lezcano * License version 2. This program is licensed "as is" without any 129d8d47eaSDaniel Lezcano * warranty of any kind, whether express or implied. 139d8d47eaSDaniel Lezcano * 149d8d47eaSDaniel Lezcano * Timer 0 is used as free-running clocksource, while timer 1 is 159d8d47eaSDaniel Lezcano * used as clock_event_device. 169d8d47eaSDaniel Lezcano * 179d8d47eaSDaniel Lezcano * --- 189d8d47eaSDaniel Lezcano * Clocksource driver for Armada 370 and Armada XP SoC. 199d8d47eaSDaniel Lezcano * This driver implements one compatible string for each SoC, given 209d8d47eaSDaniel Lezcano * each has its own characteristics: 219d8d47eaSDaniel Lezcano * 229d8d47eaSDaniel Lezcano * * Armada 370 has no 25 MHz fixed timer. 239d8d47eaSDaniel Lezcano * 249d8d47eaSDaniel Lezcano * * Armada XP cannot work properly without such 25 MHz fixed timer as 259d8d47eaSDaniel Lezcano * doing otherwise leads to using a clocksource whose frequency varies 269d8d47eaSDaniel Lezcano * when doing cpufreq frequency changes. 279d8d47eaSDaniel Lezcano * 289d8d47eaSDaniel Lezcano * See Documentation/devicetree/bindings/timer/marvell,armada-370-xp-timer.txt 299d8d47eaSDaniel Lezcano */ 309d8d47eaSDaniel Lezcano 319d8d47eaSDaniel Lezcano #include <linux/init.h> 329d8d47eaSDaniel Lezcano #include <linux/platform_device.h> 339d8d47eaSDaniel Lezcano #include <linux/kernel.h> 349d8d47eaSDaniel Lezcano #include <linux/clk.h> 359d8d47eaSDaniel Lezcano #include <linux/cpu.h> 369d8d47eaSDaniel Lezcano #include <linux/timer.h> 379d8d47eaSDaniel Lezcano #include <linux/clockchips.h> 389d8d47eaSDaniel Lezcano #include <linux/interrupt.h> 399d8d47eaSDaniel Lezcano #include <linux/of.h> 409d8d47eaSDaniel Lezcano #include <linux/of_irq.h> 419d8d47eaSDaniel Lezcano #include <linux/of_address.h> 429d8d47eaSDaniel Lezcano #include <linux/irq.h> 439d8d47eaSDaniel Lezcano #include <linux/module.h> 449d8d47eaSDaniel Lezcano #include <linux/sched_clock.h> 459d8d47eaSDaniel Lezcano #include <linux/percpu.h> 469d8d47eaSDaniel Lezcano #include <linux/syscore_ops.h> 479d8d47eaSDaniel Lezcano 489d8d47eaSDaniel Lezcano #include <asm/delay.h> 499d8d47eaSDaniel Lezcano 509d8d47eaSDaniel Lezcano /* 519d8d47eaSDaniel Lezcano * Timer block registers. 529d8d47eaSDaniel Lezcano */ 539d8d47eaSDaniel Lezcano #define TIMER_CTRL_OFF 0x0000 549d8d47eaSDaniel Lezcano #define TIMER0_EN BIT(0) 559d8d47eaSDaniel Lezcano #define TIMER0_RELOAD_EN BIT(1) 569d8d47eaSDaniel Lezcano #define TIMER0_25MHZ BIT(11) 579d8d47eaSDaniel Lezcano #define TIMER0_DIV(div) ((div) << 19) 589d8d47eaSDaniel Lezcano #define TIMER1_EN BIT(2) 599d8d47eaSDaniel Lezcano #define TIMER1_RELOAD_EN BIT(3) 609d8d47eaSDaniel Lezcano #define TIMER1_25MHZ BIT(12) 619d8d47eaSDaniel Lezcano #define TIMER1_DIV(div) ((div) << 22) 629d8d47eaSDaniel Lezcano #define TIMER_EVENTS_STATUS 0x0004 639d8d47eaSDaniel Lezcano #define TIMER0_CLR_MASK (~0x1) 649d8d47eaSDaniel Lezcano #define TIMER1_CLR_MASK (~0x100) 659d8d47eaSDaniel Lezcano #define TIMER0_RELOAD_OFF 0x0010 669d8d47eaSDaniel Lezcano #define TIMER0_VAL_OFF 0x0014 679d8d47eaSDaniel Lezcano #define TIMER1_RELOAD_OFF 0x0018 689d8d47eaSDaniel Lezcano #define TIMER1_VAL_OFF 0x001c 699d8d47eaSDaniel Lezcano 709d8d47eaSDaniel Lezcano #define LCL_TIMER_EVENTS_STATUS 0x0028 719d8d47eaSDaniel Lezcano /* Global timers are connected to the coherency fabric clock, and the 729d8d47eaSDaniel Lezcano below divider reduces their incrementing frequency. */ 739d8d47eaSDaniel Lezcano #define TIMER_DIVIDER_SHIFT 5 749d8d47eaSDaniel Lezcano #define TIMER_DIVIDER (1 << TIMER_DIVIDER_SHIFT) 759d8d47eaSDaniel Lezcano 769d8d47eaSDaniel Lezcano /* 779d8d47eaSDaniel Lezcano * SoC-specific data. 789d8d47eaSDaniel Lezcano */ 799d8d47eaSDaniel Lezcano static void __iomem *timer_base, *local_base; 809d8d47eaSDaniel Lezcano static unsigned int timer_clk; 819d8d47eaSDaniel Lezcano static bool timer25Mhz = true; 829d8d47eaSDaniel Lezcano static u32 enable_mask; 839d8d47eaSDaniel Lezcano 849d8d47eaSDaniel Lezcano /* 859d8d47eaSDaniel Lezcano * Number of timer ticks per jiffy. 869d8d47eaSDaniel Lezcano */ 879d8d47eaSDaniel Lezcano static u32 ticks_per_jiffy; 889d8d47eaSDaniel Lezcano 899d8d47eaSDaniel Lezcano static struct clock_event_device __percpu *armada_370_xp_evt; 909d8d47eaSDaniel Lezcano 919d8d47eaSDaniel Lezcano static void local_timer_ctrl_clrset(u32 clr, u32 set) 929d8d47eaSDaniel Lezcano { 939d8d47eaSDaniel Lezcano writel((readl(local_base + TIMER_CTRL_OFF) & ~clr) | set, 949d8d47eaSDaniel Lezcano local_base + TIMER_CTRL_OFF); 959d8d47eaSDaniel Lezcano } 969d8d47eaSDaniel Lezcano 979d8d47eaSDaniel Lezcano static u64 notrace armada_370_xp_read_sched_clock(void) 989d8d47eaSDaniel Lezcano { 999d8d47eaSDaniel Lezcano return ~readl(timer_base + TIMER0_VAL_OFF); 1009d8d47eaSDaniel Lezcano } 1019d8d47eaSDaniel Lezcano 1029d8d47eaSDaniel Lezcano /* 1039d8d47eaSDaniel Lezcano * Clockevent handling. 1049d8d47eaSDaniel Lezcano */ 1059d8d47eaSDaniel Lezcano static int 1069d8d47eaSDaniel Lezcano armada_370_xp_clkevt_next_event(unsigned long delta, 1079d8d47eaSDaniel Lezcano struct clock_event_device *dev) 1089d8d47eaSDaniel Lezcano { 1099d8d47eaSDaniel Lezcano /* 1109d8d47eaSDaniel Lezcano * Clear clockevent timer interrupt. 1119d8d47eaSDaniel Lezcano */ 1129d8d47eaSDaniel Lezcano writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS); 1139d8d47eaSDaniel Lezcano 1149d8d47eaSDaniel Lezcano /* 1159d8d47eaSDaniel Lezcano * Setup new clockevent timer value. 1169d8d47eaSDaniel Lezcano */ 1179d8d47eaSDaniel Lezcano writel(delta, local_base + TIMER0_VAL_OFF); 1189d8d47eaSDaniel Lezcano 1199d8d47eaSDaniel Lezcano /* 1209d8d47eaSDaniel Lezcano * Enable the timer. 1219d8d47eaSDaniel Lezcano */ 1229d8d47eaSDaniel Lezcano local_timer_ctrl_clrset(TIMER0_RELOAD_EN, enable_mask); 1239d8d47eaSDaniel Lezcano return 0; 1249d8d47eaSDaniel Lezcano } 1259d8d47eaSDaniel Lezcano 1269d8d47eaSDaniel Lezcano static int armada_370_xp_clkevt_shutdown(struct clock_event_device *evt) 1279d8d47eaSDaniel Lezcano { 1289d8d47eaSDaniel Lezcano /* 1299d8d47eaSDaniel Lezcano * Disable timer. 1309d8d47eaSDaniel Lezcano */ 1319d8d47eaSDaniel Lezcano local_timer_ctrl_clrset(TIMER0_EN, 0); 1329d8d47eaSDaniel Lezcano 1339d8d47eaSDaniel Lezcano /* 1349d8d47eaSDaniel Lezcano * ACK pending timer interrupt. 1359d8d47eaSDaniel Lezcano */ 1369d8d47eaSDaniel Lezcano writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS); 1379d8d47eaSDaniel Lezcano return 0; 1389d8d47eaSDaniel Lezcano } 1399d8d47eaSDaniel Lezcano 1409d8d47eaSDaniel Lezcano static int armada_370_xp_clkevt_set_periodic(struct clock_event_device *evt) 1419d8d47eaSDaniel Lezcano { 1429d8d47eaSDaniel Lezcano /* 1439d8d47eaSDaniel Lezcano * Setup timer to fire at 1/HZ intervals. 1449d8d47eaSDaniel Lezcano */ 1459d8d47eaSDaniel Lezcano writel(ticks_per_jiffy - 1, local_base + TIMER0_RELOAD_OFF); 1469d8d47eaSDaniel Lezcano writel(ticks_per_jiffy - 1, local_base + TIMER0_VAL_OFF); 1479d8d47eaSDaniel Lezcano 1489d8d47eaSDaniel Lezcano /* 1499d8d47eaSDaniel Lezcano * Enable timer. 1509d8d47eaSDaniel Lezcano */ 1519d8d47eaSDaniel Lezcano local_timer_ctrl_clrset(0, TIMER0_RELOAD_EN | enable_mask); 1529d8d47eaSDaniel Lezcano return 0; 1539d8d47eaSDaniel Lezcano } 1549d8d47eaSDaniel Lezcano 1559d8d47eaSDaniel Lezcano static int armada_370_xp_clkevt_irq; 1569d8d47eaSDaniel Lezcano 1579d8d47eaSDaniel Lezcano static irqreturn_t armada_370_xp_timer_interrupt(int irq, void *dev_id) 1589d8d47eaSDaniel Lezcano { 1599d8d47eaSDaniel Lezcano /* 1609d8d47eaSDaniel Lezcano * ACK timer interrupt and call event handler. 1619d8d47eaSDaniel Lezcano */ 1629d8d47eaSDaniel Lezcano struct clock_event_device *evt = dev_id; 1639d8d47eaSDaniel Lezcano 1649d8d47eaSDaniel Lezcano writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS); 1659d8d47eaSDaniel Lezcano evt->event_handler(evt); 1669d8d47eaSDaniel Lezcano 1679d8d47eaSDaniel Lezcano return IRQ_HANDLED; 1689d8d47eaSDaniel Lezcano } 1699d8d47eaSDaniel Lezcano 1709d8d47eaSDaniel Lezcano /* 1719d8d47eaSDaniel Lezcano * Setup the local clock events for a CPU. 1729d8d47eaSDaniel Lezcano */ 1739d8d47eaSDaniel Lezcano static int armada_370_xp_timer_starting_cpu(unsigned int cpu) 1749d8d47eaSDaniel Lezcano { 1759d8d47eaSDaniel Lezcano struct clock_event_device *evt = per_cpu_ptr(armada_370_xp_evt, cpu); 1769d8d47eaSDaniel Lezcano u32 clr = 0, set = 0; 1779d8d47eaSDaniel Lezcano 1789d8d47eaSDaniel Lezcano if (timer25Mhz) 1799d8d47eaSDaniel Lezcano set = TIMER0_25MHZ; 1809d8d47eaSDaniel Lezcano else 1819d8d47eaSDaniel Lezcano clr = TIMER0_25MHZ; 1829d8d47eaSDaniel Lezcano local_timer_ctrl_clrset(clr, set); 1839d8d47eaSDaniel Lezcano 184*1b80043eSJulia Lawall evt->name = "armada_370_xp_per_cpu_tick"; 1859d8d47eaSDaniel Lezcano evt->features = CLOCK_EVT_FEAT_ONESHOT | 1869d8d47eaSDaniel Lezcano CLOCK_EVT_FEAT_PERIODIC; 187*1b80043eSJulia Lawall evt->shift = 32; 188*1b80043eSJulia Lawall evt->rating = 300; 189*1b80043eSJulia Lawall evt->set_next_event = armada_370_xp_clkevt_next_event; 1909d8d47eaSDaniel Lezcano evt->set_state_shutdown = armada_370_xp_clkevt_shutdown; 1919d8d47eaSDaniel Lezcano evt->set_state_periodic = armada_370_xp_clkevt_set_periodic; 1929d8d47eaSDaniel Lezcano evt->set_state_oneshot = armada_370_xp_clkevt_shutdown; 1939d8d47eaSDaniel Lezcano evt->tick_resume = armada_370_xp_clkevt_shutdown; 1949d8d47eaSDaniel Lezcano evt->irq = armada_370_xp_clkevt_irq; 1959d8d47eaSDaniel Lezcano evt->cpumask = cpumask_of(cpu); 1969d8d47eaSDaniel Lezcano 1979d8d47eaSDaniel Lezcano clockevents_config_and_register(evt, timer_clk, 1, 0xfffffffe); 1989d8d47eaSDaniel Lezcano enable_percpu_irq(evt->irq, 0); 1999d8d47eaSDaniel Lezcano 2009d8d47eaSDaniel Lezcano return 0; 2019d8d47eaSDaniel Lezcano } 2029d8d47eaSDaniel Lezcano 2039d8d47eaSDaniel Lezcano static int armada_370_xp_timer_dying_cpu(unsigned int cpu) 2049d8d47eaSDaniel Lezcano { 2059d8d47eaSDaniel Lezcano struct clock_event_device *evt = per_cpu_ptr(armada_370_xp_evt, cpu); 2069d8d47eaSDaniel Lezcano 2079d8d47eaSDaniel Lezcano evt->set_state_shutdown(evt); 2089d8d47eaSDaniel Lezcano disable_percpu_irq(evt->irq); 2099d8d47eaSDaniel Lezcano return 0; 2109d8d47eaSDaniel Lezcano } 2119d8d47eaSDaniel Lezcano 2129d8d47eaSDaniel Lezcano static u32 timer0_ctrl_reg, timer0_local_ctrl_reg; 2139d8d47eaSDaniel Lezcano 2149d8d47eaSDaniel Lezcano static int armada_370_xp_timer_suspend(void) 2159d8d47eaSDaniel Lezcano { 2169d8d47eaSDaniel Lezcano timer0_ctrl_reg = readl(timer_base + TIMER_CTRL_OFF); 2179d8d47eaSDaniel Lezcano timer0_local_ctrl_reg = readl(local_base + TIMER_CTRL_OFF); 2189d8d47eaSDaniel Lezcano return 0; 2199d8d47eaSDaniel Lezcano } 2209d8d47eaSDaniel Lezcano 2219d8d47eaSDaniel Lezcano static void armada_370_xp_timer_resume(void) 2229d8d47eaSDaniel Lezcano { 2239d8d47eaSDaniel Lezcano writel(0xffffffff, timer_base + TIMER0_VAL_OFF); 2249d8d47eaSDaniel Lezcano writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF); 2259d8d47eaSDaniel Lezcano writel(timer0_ctrl_reg, timer_base + TIMER_CTRL_OFF); 2269d8d47eaSDaniel Lezcano writel(timer0_local_ctrl_reg, local_base + TIMER_CTRL_OFF); 2279d8d47eaSDaniel Lezcano } 2289d8d47eaSDaniel Lezcano 2299d8d47eaSDaniel Lezcano static struct syscore_ops armada_370_xp_timer_syscore_ops = { 2309d8d47eaSDaniel Lezcano .suspend = armada_370_xp_timer_suspend, 2319d8d47eaSDaniel Lezcano .resume = armada_370_xp_timer_resume, 2329d8d47eaSDaniel Lezcano }; 2339d8d47eaSDaniel Lezcano 2349d8d47eaSDaniel Lezcano static unsigned long armada_370_delay_timer_read(void) 2359d8d47eaSDaniel Lezcano { 2369d8d47eaSDaniel Lezcano return ~readl(timer_base + TIMER0_VAL_OFF); 2379d8d47eaSDaniel Lezcano } 2389d8d47eaSDaniel Lezcano 2399d8d47eaSDaniel Lezcano static struct delay_timer armada_370_delay_timer = { 2409d8d47eaSDaniel Lezcano .read_current_timer = armada_370_delay_timer_read, 2419d8d47eaSDaniel Lezcano }; 2429d8d47eaSDaniel Lezcano 2439d8d47eaSDaniel Lezcano static int __init armada_370_xp_timer_common_init(struct device_node *np) 2449d8d47eaSDaniel Lezcano { 2459d8d47eaSDaniel Lezcano u32 clr = 0, set = 0; 2469d8d47eaSDaniel Lezcano int res; 2479d8d47eaSDaniel Lezcano 2489d8d47eaSDaniel Lezcano timer_base = of_iomap(np, 0); 2499d8d47eaSDaniel Lezcano if (!timer_base) { 2509d8d47eaSDaniel Lezcano pr_err("Failed to iomap\n"); 2519d8d47eaSDaniel Lezcano return -ENXIO; 2529d8d47eaSDaniel Lezcano } 2539d8d47eaSDaniel Lezcano 2549d8d47eaSDaniel Lezcano local_base = of_iomap(np, 1); 2559d8d47eaSDaniel Lezcano if (!local_base) { 2569d8d47eaSDaniel Lezcano pr_err("Failed to iomap\n"); 2579d8d47eaSDaniel Lezcano return -ENXIO; 2589d8d47eaSDaniel Lezcano } 2599d8d47eaSDaniel Lezcano 2609d8d47eaSDaniel Lezcano if (timer25Mhz) { 2619d8d47eaSDaniel Lezcano set = TIMER0_25MHZ; 2629d8d47eaSDaniel Lezcano enable_mask = TIMER0_EN; 2639d8d47eaSDaniel Lezcano } else { 2649d8d47eaSDaniel Lezcano clr = TIMER0_25MHZ; 2659d8d47eaSDaniel Lezcano enable_mask = TIMER0_EN | TIMER0_DIV(TIMER_DIVIDER_SHIFT); 2669d8d47eaSDaniel Lezcano } 2679d8d47eaSDaniel Lezcano atomic_io_modify(timer_base + TIMER_CTRL_OFF, clr | set, set); 2689d8d47eaSDaniel Lezcano local_timer_ctrl_clrset(clr, set); 2699d8d47eaSDaniel Lezcano 2709d8d47eaSDaniel Lezcano /* 2719d8d47eaSDaniel Lezcano * We use timer 0 as clocksource, and private(local) timer 0 2729d8d47eaSDaniel Lezcano * for clockevents 2739d8d47eaSDaniel Lezcano */ 2749d8d47eaSDaniel Lezcano armada_370_xp_clkevt_irq = irq_of_parse_and_map(np, 4); 2759d8d47eaSDaniel Lezcano 2769d8d47eaSDaniel Lezcano ticks_per_jiffy = (timer_clk + HZ / 2) / HZ; 2779d8d47eaSDaniel Lezcano 2789d8d47eaSDaniel Lezcano /* 2799d8d47eaSDaniel Lezcano * Setup free-running clocksource timer (interrupts 2809d8d47eaSDaniel Lezcano * disabled). 2819d8d47eaSDaniel Lezcano */ 2829d8d47eaSDaniel Lezcano writel(0xffffffff, timer_base + TIMER0_VAL_OFF); 2839d8d47eaSDaniel Lezcano writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF); 2849d8d47eaSDaniel Lezcano 2859d8d47eaSDaniel Lezcano atomic_io_modify(timer_base + TIMER_CTRL_OFF, 2869d8d47eaSDaniel Lezcano TIMER0_RELOAD_EN | enable_mask, 2879d8d47eaSDaniel Lezcano TIMER0_RELOAD_EN | enable_mask); 2889d8d47eaSDaniel Lezcano 2899d8d47eaSDaniel Lezcano armada_370_delay_timer.freq = timer_clk; 2909d8d47eaSDaniel Lezcano register_current_timer_delay(&armada_370_delay_timer); 2919d8d47eaSDaniel Lezcano 2929d8d47eaSDaniel Lezcano /* 2939d8d47eaSDaniel Lezcano * Set scale and timer for sched_clock. 2949d8d47eaSDaniel Lezcano */ 2959d8d47eaSDaniel Lezcano sched_clock_register(armada_370_xp_read_sched_clock, 32, timer_clk); 2969d8d47eaSDaniel Lezcano 2979d8d47eaSDaniel Lezcano res = clocksource_mmio_init(timer_base + TIMER0_VAL_OFF, 2989d8d47eaSDaniel Lezcano "armada_370_xp_clocksource", 2999d8d47eaSDaniel Lezcano timer_clk, 300, 32, clocksource_mmio_readl_down); 3009d8d47eaSDaniel Lezcano if (res) { 3019d8d47eaSDaniel Lezcano pr_err("Failed to initialize clocksource mmio\n"); 3029d8d47eaSDaniel Lezcano return res; 3039d8d47eaSDaniel Lezcano } 3049d8d47eaSDaniel Lezcano 3059d8d47eaSDaniel Lezcano armada_370_xp_evt = alloc_percpu(struct clock_event_device); 3069d8d47eaSDaniel Lezcano if (!armada_370_xp_evt) 3079d8d47eaSDaniel Lezcano return -ENOMEM; 3089d8d47eaSDaniel Lezcano 3099d8d47eaSDaniel Lezcano /* 3109d8d47eaSDaniel Lezcano * Setup clockevent timer (interrupt-driven). 3119d8d47eaSDaniel Lezcano */ 3129d8d47eaSDaniel Lezcano res = request_percpu_irq(armada_370_xp_clkevt_irq, 3139d8d47eaSDaniel Lezcano armada_370_xp_timer_interrupt, 3149d8d47eaSDaniel Lezcano "armada_370_xp_per_cpu_tick", 3159d8d47eaSDaniel Lezcano armada_370_xp_evt); 3169d8d47eaSDaniel Lezcano /* Immediately configure the timer on the boot CPU */ 3179d8d47eaSDaniel Lezcano if (res) { 3189d8d47eaSDaniel Lezcano pr_err("Failed to request percpu irq\n"); 3199d8d47eaSDaniel Lezcano return res; 3209d8d47eaSDaniel Lezcano } 3219d8d47eaSDaniel Lezcano 3229d8d47eaSDaniel Lezcano res = cpuhp_setup_state(CPUHP_AP_ARMADA_TIMER_STARTING, 3239d8d47eaSDaniel Lezcano "clockevents/armada:starting", 3249d8d47eaSDaniel Lezcano armada_370_xp_timer_starting_cpu, 3259d8d47eaSDaniel Lezcano armada_370_xp_timer_dying_cpu); 3269d8d47eaSDaniel Lezcano if (res) { 3279d8d47eaSDaniel Lezcano pr_err("Failed to setup hotplug state and timer\n"); 3289d8d47eaSDaniel Lezcano return res; 3299d8d47eaSDaniel Lezcano } 3309d8d47eaSDaniel Lezcano 3319d8d47eaSDaniel Lezcano register_syscore_ops(&armada_370_xp_timer_syscore_ops); 3329d8d47eaSDaniel Lezcano 3339d8d47eaSDaniel Lezcano return 0; 3349d8d47eaSDaniel Lezcano } 3359d8d47eaSDaniel Lezcano 3369d8d47eaSDaniel Lezcano static int __init armada_xp_timer_init(struct device_node *np) 3379d8d47eaSDaniel Lezcano { 3389d8d47eaSDaniel Lezcano struct clk *clk = of_clk_get_by_name(np, "fixed"); 3399d8d47eaSDaniel Lezcano int ret; 3409d8d47eaSDaniel Lezcano 3419d8d47eaSDaniel Lezcano if (IS_ERR(clk)) { 3429d8d47eaSDaniel Lezcano pr_err("Failed to get clock\n"); 3439d8d47eaSDaniel Lezcano return PTR_ERR(clk); 3449d8d47eaSDaniel Lezcano } 3459d8d47eaSDaniel Lezcano 3469d8d47eaSDaniel Lezcano ret = clk_prepare_enable(clk); 3479d8d47eaSDaniel Lezcano if (ret) 3489d8d47eaSDaniel Lezcano return ret; 3499d8d47eaSDaniel Lezcano 3509d8d47eaSDaniel Lezcano timer_clk = clk_get_rate(clk); 3519d8d47eaSDaniel Lezcano 3529d8d47eaSDaniel Lezcano return armada_370_xp_timer_common_init(np); 3539d8d47eaSDaniel Lezcano } 3549d8d47eaSDaniel Lezcano TIMER_OF_DECLARE(armada_xp, "marvell,armada-xp-timer", 3559d8d47eaSDaniel Lezcano armada_xp_timer_init); 3569d8d47eaSDaniel Lezcano 3579d8d47eaSDaniel Lezcano static int __init armada_375_timer_init(struct device_node *np) 3589d8d47eaSDaniel Lezcano { 3599d8d47eaSDaniel Lezcano struct clk *clk; 3609d8d47eaSDaniel Lezcano int ret; 3619d8d47eaSDaniel Lezcano 3629d8d47eaSDaniel Lezcano clk = of_clk_get_by_name(np, "fixed"); 3639d8d47eaSDaniel Lezcano if (!IS_ERR(clk)) { 3649d8d47eaSDaniel Lezcano ret = clk_prepare_enable(clk); 3659d8d47eaSDaniel Lezcano if (ret) 3669d8d47eaSDaniel Lezcano return ret; 3679d8d47eaSDaniel Lezcano timer_clk = clk_get_rate(clk); 3689d8d47eaSDaniel Lezcano } else { 3699d8d47eaSDaniel Lezcano 3709d8d47eaSDaniel Lezcano /* 3719d8d47eaSDaniel Lezcano * This fallback is required in order to retain proper 3729d8d47eaSDaniel Lezcano * devicetree backwards compatibility. 3739d8d47eaSDaniel Lezcano */ 3749d8d47eaSDaniel Lezcano clk = of_clk_get(np, 0); 3759d8d47eaSDaniel Lezcano 3769d8d47eaSDaniel Lezcano /* Must have at least a clock */ 3779d8d47eaSDaniel Lezcano if (IS_ERR(clk)) { 3789d8d47eaSDaniel Lezcano pr_err("Failed to get clock\n"); 3799d8d47eaSDaniel Lezcano return PTR_ERR(clk); 3809d8d47eaSDaniel Lezcano } 3819d8d47eaSDaniel Lezcano 3829d8d47eaSDaniel Lezcano ret = clk_prepare_enable(clk); 3839d8d47eaSDaniel Lezcano if (ret) 3849d8d47eaSDaniel Lezcano return ret; 3859d8d47eaSDaniel Lezcano 3869d8d47eaSDaniel Lezcano timer_clk = clk_get_rate(clk) / TIMER_DIVIDER; 3879d8d47eaSDaniel Lezcano timer25Mhz = false; 3889d8d47eaSDaniel Lezcano } 3899d8d47eaSDaniel Lezcano 3909d8d47eaSDaniel Lezcano return armada_370_xp_timer_common_init(np); 3919d8d47eaSDaniel Lezcano } 3929d8d47eaSDaniel Lezcano TIMER_OF_DECLARE(armada_375, "marvell,armada-375-timer", 3939d8d47eaSDaniel Lezcano armada_375_timer_init); 3949d8d47eaSDaniel Lezcano 3959d8d47eaSDaniel Lezcano static int __init armada_370_timer_init(struct device_node *np) 3969d8d47eaSDaniel Lezcano { 3979d8d47eaSDaniel Lezcano struct clk *clk; 3989d8d47eaSDaniel Lezcano int ret; 3999d8d47eaSDaniel Lezcano 4009d8d47eaSDaniel Lezcano clk = of_clk_get(np, 0); 4019d8d47eaSDaniel Lezcano if (IS_ERR(clk)) { 4029d8d47eaSDaniel Lezcano pr_err("Failed to get clock\n"); 4039d8d47eaSDaniel Lezcano return PTR_ERR(clk); 4049d8d47eaSDaniel Lezcano } 4059d8d47eaSDaniel Lezcano 4069d8d47eaSDaniel Lezcano ret = clk_prepare_enable(clk); 4079d8d47eaSDaniel Lezcano if (ret) 4089d8d47eaSDaniel Lezcano return ret; 4099d8d47eaSDaniel Lezcano 4109d8d47eaSDaniel Lezcano timer_clk = clk_get_rate(clk) / TIMER_DIVIDER; 4119d8d47eaSDaniel Lezcano timer25Mhz = false; 4129d8d47eaSDaniel Lezcano 4139d8d47eaSDaniel Lezcano return armada_370_xp_timer_common_init(np); 4149d8d47eaSDaniel Lezcano } 4159d8d47eaSDaniel Lezcano TIMER_OF_DECLARE(armada_370, "marvell,armada-370-timer", 4169d8d47eaSDaniel Lezcano armada_370_timer_init); 417