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