162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) Maxime Coquelin 2015
462306a36Sopenharmony_ci * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/clocksource.h>
962306a36Sopenharmony_ci#include <linux/clockchips.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/of_address.h>
1362306a36Sopenharmony_ci#include <linux/clk.h>
1462306a36Sopenharmony_ci#include <linux/bitops.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define SYST_CSR	0x00
1762306a36Sopenharmony_ci#define SYST_RVR	0x04
1862306a36Sopenharmony_ci#define SYST_CVR	0x08
1962306a36Sopenharmony_ci#define SYST_CALIB	0x0c
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define SYST_CSR_ENABLE BIT(0)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define SYSTICK_LOAD_RELOAD_MASK 0x00FFFFFF
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int __init system_timer_of_register(struct device_node *np)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct clk *clk = NULL;
2862306a36Sopenharmony_ci	void __iomem *base;
2962306a36Sopenharmony_ci	u32 rate;
3062306a36Sopenharmony_ci	int ret;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	base = of_iomap(np, 0);
3362306a36Sopenharmony_ci	if (!base) {
3462306a36Sopenharmony_ci		pr_warn("system-timer: invalid base address\n");
3562306a36Sopenharmony_ci		return -ENXIO;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	ret = of_property_read_u32(np, "clock-frequency", &rate);
3962306a36Sopenharmony_ci	if (ret) {
4062306a36Sopenharmony_ci		clk = of_clk_get(np, 0);
4162306a36Sopenharmony_ci		if (IS_ERR(clk)) {
4262306a36Sopenharmony_ci			ret = PTR_ERR(clk);
4362306a36Sopenharmony_ci			goto out_unmap;
4462306a36Sopenharmony_ci		}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci		ret = clk_prepare_enable(clk);
4762306a36Sopenharmony_ci		if (ret)
4862306a36Sopenharmony_ci			goto out_clk_put;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci		rate = clk_get_rate(clk);
5162306a36Sopenharmony_ci		if (!rate) {
5262306a36Sopenharmony_ci			ret = -EINVAL;
5362306a36Sopenharmony_ci			goto out_clk_disable;
5462306a36Sopenharmony_ci		}
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	writel_relaxed(SYSTICK_LOAD_RELOAD_MASK, base + SYST_RVR);
5862306a36Sopenharmony_ci	writel_relaxed(SYST_CSR_ENABLE, base + SYST_CSR);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	ret = clocksource_mmio_init(base + SYST_CVR, "arm_system_timer", rate,
6162306a36Sopenharmony_ci			200, 24, clocksource_mmio_readl_down);
6262306a36Sopenharmony_ci	if (ret) {
6362306a36Sopenharmony_ci		pr_err("failed to init clocksource (%d)\n", ret);
6462306a36Sopenharmony_ci		if (clk)
6562306a36Sopenharmony_ci			goto out_clk_disable;
6662306a36Sopenharmony_ci		else
6762306a36Sopenharmony_ci			goto out_unmap;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	pr_info("ARM System timer initialized as clocksource\n");
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return 0;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciout_clk_disable:
7562306a36Sopenharmony_ci	clk_disable_unprepare(clk);
7662306a36Sopenharmony_ciout_clk_put:
7762306a36Sopenharmony_ci	clk_put(clk);
7862306a36Sopenharmony_ciout_unmap:
7962306a36Sopenharmony_ci	iounmap(base);
8062306a36Sopenharmony_ci	pr_warn("ARM System timer register failed (%d)\n", ret);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	return ret;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciTIMER_OF_DECLARE(arm_systick, "arm,armv7m-systick",
8662306a36Sopenharmony_ci			system_timer_of_register);
87