18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) Maxime Coquelin 2015
48c2ecf20Sopenharmony_ci * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
98c2ecf20Sopenharmony_ci#include <linux/clockchips.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci#include <linux/of_address.h>
138c2ecf20Sopenharmony_ci#include <linux/clk.h>
148c2ecf20Sopenharmony_ci#include <linux/bitops.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define SYST_CSR	0x00
178c2ecf20Sopenharmony_ci#define SYST_RVR	0x04
188c2ecf20Sopenharmony_ci#define SYST_CVR	0x08
198c2ecf20Sopenharmony_ci#define SYST_CALIB	0x0c
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define SYST_CSR_ENABLE BIT(0)
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define SYSTICK_LOAD_RELOAD_MASK 0x00FFFFFF
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int __init system_timer_of_register(struct device_node *np)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct clk *clk = NULL;
288c2ecf20Sopenharmony_ci	void __iomem *base;
298c2ecf20Sopenharmony_ci	u32 rate;
308c2ecf20Sopenharmony_ci	int ret;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	base = of_iomap(np, 0);
338c2ecf20Sopenharmony_ci	if (!base) {
348c2ecf20Sopenharmony_ci		pr_warn("system-timer: invalid base address\n");
358c2ecf20Sopenharmony_ci		return -ENXIO;
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "clock-frequency", &rate);
398c2ecf20Sopenharmony_ci	if (ret) {
408c2ecf20Sopenharmony_ci		clk = of_clk_get(np, 0);
418c2ecf20Sopenharmony_ci		if (IS_ERR(clk)) {
428c2ecf20Sopenharmony_ci			ret = PTR_ERR(clk);
438c2ecf20Sopenharmony_ci			goto out_unmap;
448c2ecf20Sopenharmony_ci		}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci		ret = clk_prepare_enable(clk);
478c2ecf20Sopenharmony_ci		if (ret)
488c2ecf20Sopenharmony_ci			goto out_clk_put;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci		rate = clk_get_rate(clk);
518c2ecf20Sopenharmony_ci		if (!rate) {
528c2ecf20Sopenharmony_ci			ret = -EINVAL;
538c2ecf20Sopenharmony_ci			goto out_clk_disable;
548c2ecf20Sopenharmony_ci		}
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR);
588c2ecf20Sopenharmony_ci	writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer", rate,
618c2ecf20Sopenharmony_ci			200, 24, clocksource_mmio_readl_down);
628c2ecf20Sopenharmony_ci	if (ret) {
638c2ecf20Sopenharmony_ci		pr_err("failed to init clocksource (%d)\n", ret);
648c2ecf20Sopenharmony_ci		if (clk)
658c2ecf20Sopenharmony_ci			goto out_clk_disable;
668c2ecf20Sopenharmony_ci		else
678c2ecf20Sopenharmony_ci			goto out_unmap;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	pr_info("ARM System timer initialized as clocksource\n");
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	return 0;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciout_clk_disable:
758c2ecf20Sopenharmony_ci	clk_disable_unprepare(clk);
768c2ecf20Sopenharmony_ciout_clk_put:
778c2ecf20Sopenharmony_ci	clk_put(clk);
788c2ecf20Sopenharmony_ciout_unmap:
798c2ecf20Sopenharmony_ci	iounmap(base);
808c2ecf20Sopenharmony_ci	pr_warn("ARM System timer register failed (%d)\n", ret);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	return ret;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(arm_systick, "arm,armv7m-systick",
868c2ecf20Sopenharmony_ci			system_timer_of_register);
87