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
local_timer_ctrl_clrset(u32 clr,u32 set)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
armada_370_xp_read_sched_clock(void)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
armada_370_xp_clkevt_next_event(unsigned long delta,struct clock_event_device * dev)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
armada_370_xp_clkevt_shutdown(struct clock_event_device * evt)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
armada_370_xp_clkevt_set_periodic(struct clock_event_device * evt)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
armada_370_xp_timer_interrupt(int irq,void * dev_id)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 */
armada_370_xp_timer_starting_cpu(unsigned int cpu)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
armada_370_xp_timer_dying_cpu(unsigned int cpu)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
armada_370_xp_timer_suspend(void)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
armada_370_xp_timer_resume(void)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
armada_370_delay_timer_read(void)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
armada_370_xp_timer_common_init(struct device_node * np)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
armada_xp_timer_init(struct device_node * np)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
armada_375_timer_init(struct device_node * np)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
armada_370_timer_init(struct device_node * np)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