162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Marvell Armada 370/XP SoC timer handling.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Marvell
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Lior Amsalem <alior@marvell.com>
862306a36Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com>
962306a36Sopenharmony_ci * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Timer 0 is used as free-running clocksource, while timer 1 is
1262306a36Sopenharmony_ci * used as clock_event_device.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * ---
1562306a36Sopenharmony_ci * Clocksource driver for Armada 370 and Armada XP SoC.
1662306a36Sopenharmony_ci * This driver implements one compatible string for each SoC, given
1762306a36Sopenharmony_ci * each has its own characteristics:
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *   * Armada 370 has no 25 MHz fixed timer.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci *   * Armada XP cannot work properly without such 25 MHz fixed timer as
2262306a36Sopenharmony_ci *     doing otherwise leads to using a clocksource whose frequency varies
2362306a36Sopenharmony_ci *     when doing cpufreq frequency changes.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * See Documentation/devicetree/bindings/timer/marvell,armada-370-xp-timer.txt
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <linux/init.h>
2962306a36Sopenharmony_ci#include <linux/platform_device.h>
3062306a36Sopenharmony_ci#include <linux/kernel.h>
3162306a36Sopenharmony_ci#include <linux/clk.h>
3262306a36Sopenharmony_ci#include <linux/cpu.h>
3362306a36Sopenharmony_ci#include <linux/timer.h>
3462306a36Sopenharmony_ci#include <linux/clockchips.h>
3562306a36Sopenharmony_ci#include <linux/interrupt.h>
3662306a36Sopenharmony_ci#include <linux/of.h>
3762306a36Sopenharmony_ci#include <linux/of_irq.h>
3862306a36Sopenharmony_ci#include <linux/of_address.h>
3962306a36Sopenharmony_ci#include <linux/irq.h>
4062306a36Sopenharmony_ci#include <linux/module.h>
4162306a36Sopenharmony_ci#include <linux/sched_clock.h>
4262306a36Sopenharmony_ci#include <linux/percpu.h>
4362306a36Sopenharmony_ci#include <linux/syscore_ops.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include <asm/delay.h>
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * Timer block registers.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ci#define TIMER_CTRL_OFF		0x0000
5162306a36Sopenharmony_ci#define  TIMER0_EN		 BIT(0)
5262306a36Sopenharmony_ci#define  TIMER0_RELOAD_EN	 BIT(1)
5362306a36Sopenharmony_ci#define  TIMER0_25MHZ            BIT(11)
5462306a36Sopenharmony_ci#define  TIMER0_DIV(div)         ((div) << 19)
5562306a36Sopenharmony_ci#define  TIMER1_EN		 BIT(2)
5662306a36Sopenharmony_ci#define  TIMER1_RELOAD_EN	 BIT(3)
5762306a36Sopenharmony_ci#define  TIMER1_25MHZ            BIT(12)
5862306a36Sopenharmony_ci#define  TIMER1_DIV(div)         ((div) << 22)
5962306a36Sopenharmony_ci#define TIMER_EVENTS_STATUS	0x0004
6062306a36Sopenharmony_ci#define  TIMER0_CLR_MASK         (~0x1)
6162306a36Sopenharmony_ci#define  TIMER1_CLR_MASK         (~0x100)
6262306a36Sopenharmony_ci#define TIMER0_RELOAD_OFF	0x0010
6362306a36Sopenharmony_ci#define TIMER0_VAL_OFF		0x0014
6462306a36Sopenharmony_ci#define TIMER1_RELOAD_OFF	0x0018
6562306a36Sopenharmony_ci#define TIMER1_VAL_OFF		0x001c
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define LCL_TIMER_EVENTS_STATUS	0x0028
6862306a36Sopenharmony_ci/* Global timers are connected to the coherency fabric clock, and the
6962306a36Sopenharmony_ci   below divider reduces their incrementing frequency. */
7062306a36Sopenharmony_ci#define TIMER_DIVIDER_SHIFT     5
7162306a36Sopenharmony_ci#define TIMER_DIVIDER           (1 << TIMER_DIVIDER_SHIFT)
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/*
7462306a36Sopenharmony_ci * SoC-specific data.
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_cistatic void __iomem *timer_base, *local_base;
7762306a36Sopenharmony_cistatic unsigned int timer_clk;
7862306a36Sopenharmony_cistatic bool timer25Mhz = true;
7962306a36Sopenharmony_cistatic u32 enable_mask;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/*
8262306a36Sopenharmony_ci * Number of timer ticks per jiffy.
8362306a36Sopenharmony_ci */
8462306a36Sopenharmony_cistatic u32 ticks_per_jiffy;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic struct clock_event_device __percpu *armada_370_xp_evt;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void local_timer_ctrl_clrset(u32 clr, u32 set)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	writel((readl(local_base + TIMER_CTRL_OFF) & ~clr) | set,
9162306a36Sopenharmony_ci		local_base + TIMER_CTRL_OFF);
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic u64 notrace armada_370_xp_read_sched_clock(void)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	return ~readl(timer_base + TIMER0_VAL_OFF);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * Clockevent handling.
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistatic int
10362306a36Sopenharmony_ciarmada_370_xp_clkevt_next_event(unsigned long delta,
10462306a36Sopenharmony_ci				struct clock_event_device *dev)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	/*
10762306a36Sopenharmony_ci	 * Clear clockevent timer interrupt.
10862306a36Sopenharmony_ci	 */
10962306a36Sopenharmony_ci	writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/*
11262306a36Sopenharmony_ci	 * Setup new clockevent timer value.
11362306a36Sopenharmony_ci	 */
11462306a36Sopenharmony_ci	writel(delta, local_base + TIMER0_VAL_OFF);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	/*
11762306a36Sopenharmony_ci	 * Enable the timer.
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci	local_timer_ctrl_clrset(TIMER0_RELOAD_EN, enable_mask);
12062306a36Sopenharmony_ci	return 0;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int armada_370_xp_clkevt_shutdown(struct clock_event_device *evt)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	/*
12662306a36Sopenharmony_ci	 * Disable timer.
12762306a36Sopenharmony_ci	 */
12862306a36Sopenharmony_ci	local_timer_ctrl_clrset(TIMER0_EN, 0);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	/*
13162306a36Sopenharmony_ci	 * ACK pending timer interrupt.
13262306a36Sopenharmony_ci	 */
13362306a36Sopenharmony_ci	writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int armada_370_xp_clkevt_set_periodic(struct clock_event_device *evt)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	/*
14062306a36Sopenharmony_ci	 * Setup timer to fire at 1/HZ intervals.
14162306a36Sopenharmony_ci	 */
14262306a36Sopenharmony_ci	writel(ticks_per_jiffy - 1, local_base + TIMER0_RELOAD_OFF);
14362306a36Sopenharmony_ci	writel(ticks_per_jiffy - 1, local_base + TIMER0_VAL_OFF);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/*
14662306a36Sopenharmony_ci	 * Enable timer.
14762306a36Sopenharmony_ci	 */
14862306a36Sopenharmony_ci	local_timer_ctrl_clrset(0, TIMER0_RELOAD_EN | enable_mask);
14962306a36Sopenharmony_ci	return 0;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int armada_370_xp_clkevt_irq;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic irqreturn_t armada_370_xp_timer_interrupt(int irq, void *dev_id)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	/*
15762306a36Sopenharmony_ci	 * ACK timer interrupt and call event handler.
15862306a36Sopenharmony_ci	 */
15962306a36Sopenharmony_ci	struct clock_event_device *evt = dev_id;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	writel(TIMER0_CLR_MASK, local_base + LCL_TIMER_EVENTS_STATUS);
16262306a36Sopenharmony_ci	evt->event_handler(evt);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return IRQ_HANDLED;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/*
16862306a36Sopenharmony_ci * Setup the local clock events for a CPU.
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_cistatic int armada_370_xp_timer_starting_cpu(unsigned int cpu)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct clock_event_device *evt = per_cpu_ptr(armada_370_xp_evt, cpu);
17362306a36Sopenharmony_ci	u32 clr = 0, set = 0;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (timer25Mhz)
17662306a36Sopenharmony_ci		set = TIMER0_25MHZ;
17762306a36Sopenharmony_ci	else
17862306a36Sopenharmony_ci		clr = TIMER0_25MHZ;
17962306a36Sopenharmony_ci	local_timer_ctrl_clrset(clr, set);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	evt->name		= "armada_370_xp_per_cpu_tick";
18262306a36Sopenharmony_ci	evt->features		= CLOCK_EVT_FEAT_ONESHOT |
18362306a36Sopenharmony_ci				  CLOCK_EVT_FEAT_PERIODIC;
18462306a36Sopenharmony_ci	evt->shift		= 32;
18562306a36Sopenharmony_ci	evt->rating		= 300;
18662306a36Sopenharmony_ci	evt->set_next_event	= armada_370_xp_clkevt_next_event;
18762306a36Sopenharmony_ci	evt->set_state_shutdown	= armada_370_xp_clkevt_shutdown;
18862306a36Sopenharmony_ci	evt->set_state_periodic	= armada_370_xp_clkevt_set_periodic;
18962306a36Sopenharmony_ci	evt->set_state_oneshot	= armada_370_xp_clkevt_shutdown;
19062306a36Sopenharmony_ci	evt->tick_resume	= armada_370_xp_clkevt_shutdown;
19162306a36Sopenharmony_ci	evt->irq		= armada_370_xp_clkevt_irq;
19262306a36Sopenharmony_ci	evt->cpumask		= cpumask_of(cpu);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	clockevents_config_and_register(evt, timer_clk, 1, 0xfffffffe);
19562306a36Sopenharmony_ci	enable_percpu_irq(evt->irq, 0);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic int armada_370_xp_timer_dying_cpu(unsigned int cpu)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct clock_event_device *evt = per_cpu_ptr(armada_370_xp_evt, cpu);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	evt->set_state_shutdown(evt);
20562306a36Sopenharmony_ci	disable_percpu_irq(evt->irq);
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic u32 timer0_ctrl_reg, timer0_local_ctrl_reg;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic int armada_370_xp_timer_suspend(void)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	timer0_ctrl_reg = readl(timer_base + TIMER_CTRL_OFF);
21462306a36Sopenharmony_ci	timer0_local_ctrl_reg = readl(local_base + TIMER_CTRL_OFF);
21562306a36Sopenharmony_ci	return 0;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void armada_370_xp_timer_resume(void)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
22162306a36Sopenharmony_ci	writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
22262306a36Sopenharmony_ci	writel(timer0_ctrl_reg, timer_base + TIMER_CTRL_OFF);
22362306a36Sopenharmony_ci	writel(timer0_local_ctrl_reg, local_base + TIMER_CTRL_OFF);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic struct syscore_ops armada_370_xp_timer_syscore_ops = {
22762306a36Sopenharmony_ci	.suspend	= armada_370_xp_timer_suspend,
22862306a36Sopenharmony_ci	.resume		= armada_370_xp_timer_resume,
22962306a36Sopenharmony_ci};
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic unsigned long armada_370_delay_timer_read(void)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	return ~readl(timer_base + TIMER0_VAL_OFF);
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic struct delay_timer armada_370_delay_timer = {
23762306a36Sopenharmony_ci	.read_current_timer = armada_370_delay_timer_read,
23862306a36Sopenharmony_ci};
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic int __init armada_370_xp_timer_common_init(struct device_node *np)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	u32 clr = 0, set = 0;
24362306a36Sopenharmony_ci	int res;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	timer_base = of_iomap(np, 0);
24662306a36Sopenharmony_ci	if (!timer_base) {
24762306a36Sopenharmony_ci		pr_err("Failed to iomap\n");
24862306a36Sopenharmony_ci		return -ENXIO;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	local_base = of_iomap(np, 1);
25262306a36Sopenharmony_ci	if (!local_base) {
25362306a36Sopenharmony_ci		pr_err("Failed to iomap\n");
25462306a36Sopenharmony_ci		return -ENXIO;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (timer25Mhz) {
25862306a36Sopenharmony_ci		set = TIMER0_25MHZ;
25962306a36Sopenharmony_ci		enable_mask = TIMER0_EN;
26062306a36Sopenharmony_ci	} else {
26162306a36Sopenharmony_ci		clr = TIMER0_25MHZ;
26262306a36Sopenharmony_ci		enable_mask = TIMER0_EN | TIMER0_DIV(TIMER_DIVIDER_SHIFT);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci	atomic_io_modify(timer_base + TIMER_CTRL_OFF, clr | set, set);
26562306a36Sopenharmony_ci	local_timer_ctrl_clrset(clr, set);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/*
26862306a36Sopenharmony_ci	 * We use timer 0 as clocksource, and private(local) timer 0
26962306a36Sopenharmony_ci	 * for clockevents
27062306a36Sopenharmony_ci	 */
27162306a36Sopenharmony_ci	armada_370_xp_clkevt_irq = irq_of_parse_and_map(np, 4);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	ticks_per_jiffy = (timer_clk + HZ / 2) / HZ;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/*
27662306a36Sopenharmony_ci	 * Setup free-running clocksource timer (interrupts
27762306a36Sopenharmony_ci	 * disabled).
27862306a36Sopenharmony_ci	 */
27962306a36Sopenharmony_ci	writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
28062306a36Sopenharmony_ci	writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	atomic_io_modify(timer_base + TIMER_CTRL_OFF,
28362306a36Sopenharmony_ci		TIMER0_RELOAD_EN | enable_mask,
28462306a36Sopenharmony_ci		TIMER0_RELOAD_EN | enable_mask);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	armada_370_delay_timer.freq = timer_clk;
28762306a36Sopenharmony_ci	register_current_timer_delay(&armada_370_delay_timer);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/*
29062306a36Sopenharmony_ci	 * Set scale and timer for sched_clock.
29162306a36Sopenharmony_ci	 */
29262306a36Sopenharmony_ci	sched_clock_register(armada_370_xp_read_sched_clock, 32, timer_clk);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	res = clocksource_mmio_init(timer_base + TIMER0_VAL_OFF,
29562306a36Sopenharmony_ci				    "armada_370_xp_clocksource",
29662306a36Sopenharmony_ci				    timer_clk, 300, 32, clocksource_mmio_readl_down);
29762306a36Sopenharmony_ci	if (res) {
29862306a36Sopenharmony_ci		pr_err("Failed to initialize clocksource mmio\n");
29962306a36Sopenharmony_ci		return res;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	armada_370_xp_evt = alloc_percpu(struct clock_event_device);
30362306a36Sopenharmony_ci	if (!armada_370_xp_evt)
30462306a36Sopenharmony_ci		return -ENOMEM;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/*
30762306a36Sopenharmony_ci	 * Setup clockevent timer (interrupt-driven).
30862306a36Sopenharmony_ci	 */
30962306a36Sopenharmony_ci	res = request_percpu_irq(armada_370_xp_clkevt_irq,
31062306a36Sopenharmony_ci				armada_370_xp_timer_interrupt,
31162306a36Sopenharmony_ci				"armada_370_xp_per_cpu_tick",
31262306a36Sopenharmony_ci				armada_370_xp_evt);
31362306a36Sopenharmony_ci	/* Immediately configure the timer on the boot CPU */
31462306a36Sopenharmony_ci	if (res) {
31562306a36Sopenharmony_ci		pr_err("Failed to request percpu irq\n");
31662306a36Sopenharmony_ci		return res;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	res = cpuhp_setup_state(CPUHP_AP_ARMADA_TIMER_STARTING,
32062306a36Sopenharmony_ci				"clockevents/armada:starting",
32162306a36Sopenharmony_ci				armada_370_xp_timer_starting_cpu,
32262306a36Sopenharmony_ci				armada_370_xp_timer_dying_cpu);
32362306a36Sopenharmony_ci	if (res) {
32462306a36Sopenharmony_ci		pr_err("Failed to setup hotplug state and timer\n");
32562306a36Sopenharmony_ci		return res;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	register_syscore_ops(&armada_370_xp_timer_syscore_ops);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return 0;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic int __init armada_xp_timer_init(struct device_node *np)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct clk *clk = of_clk_get_by_name(np, "fixed");
33662306a36Sopenharmony_ci	int ret;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (IS_ERR(clk)) {
33962306a36Sopenharmony_ci		pr_err("Failed to get clock\n");
34062306a36Sopenharmony_ci		return PTR_ERR(clk);
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	ret = clk_prepare_enable(clk);
34462306a36Sopenharmony_ci	if (ret)
34562306a36Sopenharmony_ci		return ret;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	timer_clk = clk_get_rate(clk);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return armada_370_xp_timer_common_init(np);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ciTIMER_OF_DECLARE(armada_xp, "marvell,armada-xp-timer",
35262306a36Sopenharmony_ci		       armada_xp_timer_init);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int __init armada_375_timer_init(struct device_node *np)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct clk *clk;
35762306a36Sopenharmony_ci	int ret;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	clk = of_clk_get_by_name(np, "fixed");
36062306a36Sopenharmony_ci	if (!IS_ERR(clk)) {
36162306a36Sopenharmony_ci		ret = clk_prepare_enable(clk);
36262306a36Sopenharmony_ci		if (ret)
36362306a36Sopenharmony_ci			return ret;
36462306a36Sopenharmony_ci		timer_clk = clk_get_rate(clk);
36562306a36Sopenharmony_ci	} else {
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci		/*
36862306a36Sopenharmony_ci		 * This fallback is required in order to retain proper
36962306a36Sopenharmony_ci		 * devicetree backwards compatibility.
37062306a36Sopenharmony_ci		 */
37162306a36Sopenharmony_ci		clk = of_clk_get(np, 0);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		/* Must have at least a clock */
37462306a36Sopenharmony_ci		if (IS_ERR(clk)) {
37562306a36Sopenharmony_ci			pr_err("Failed to get clock\n");
37662306a36Sopenharmony_ci			return PTR_ERR(clk);
37762306a36Sopenharmony_ci		}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		ret = clk_prepare_enable(clk);
38062306a36Sopenharmony_ci		if (ret)
38162306a36Sopenharmony_ci			return ret;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		timer_clk = clk_get_rate(clk) / TIMER_DIVIDER;
38462306a36Sopenharmony_ci		timer25Mhz = false;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return armada_370_xp_timer_common_init(np);
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ciTIMER_OF_DECLARE(armada_375, "marvell,armada-375-timer",
39062306a36Sopenharmony_ci		       armada_375_timer_init);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic int __init armada_370_timer_init(struct device_node *np)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct clk *clk;
39562306a36Sopenharmony_ci	int ret;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	clk = of_clk_get(np, 0);
39862306a36Sopenharmony_ci	if (IS_ERR(clk)) {
39962306a36Sopenharmony_ci		pr_err("Failed to get clock\n");
40062306a36Sopenharmony_ci		return PTR_ERR(clk);
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	ret = clk_prepare_enable(clk);
40462306a36Sopenharmony_ci	if (ret)
40562306a36Sopenharmony_ci		return ret;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	timer_clk = clk_get_rate(clk) / TIMER_DIVIDER;
40862306a36Sopenharmony_ci	timer25Mhz = false;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return armada_370_xp_timer_common_init(np);
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ciTIMER_OF_DECLARE(armada_370, "marvell,armada-370-timer",
41362306a36Sopenharmony_ci		       armada_370_timer_init);
414