18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Clocksource using the Low Power Timer found in the Low Power Controller (LPC)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015 STMicroelectronics – All Rights Reserved
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author(s): Francesco Virlinzi <francesco.virlinzi@st.com>
88c2ecf20Sopenharmony_ci *	      Ajit Pal Singh <ajitpal.singh@st.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/clk.h>
128c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/of_address.h>
158c2ecf20Sopenharmony_ci#include <linux/sched_clock.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <dt-bindings/mfd/st-lpc.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* Low Power Timer */
218c2ecf20Sopenharmony_ci#define LPC_LPT_LSB_OFF		0x400
228c2ecf20Sopenharmony_ci#define LPC_LPT_MSB_OFF		0x404
238c2ecf20Sopenharmony_ci#define LPC_LPT_START_OFF	0x408
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic struct st_clksrc_ddata {
268c2ecf20Sopenharmony_ci	struct clk		*clk;
278c2ecf20Sopenharmony_ci	void __iomem		*base;
288c2ecf20Sopenharmony_ci} ddata;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void __init st_clksrc_reset(void)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	writel_relaxed(0, ddata.base + LPC_LPT_START_OFF);
338c2ecf20Sopenharmony_ci	writel_relaxed(0, ddata.base + LPC_LPT_MSB_OFF);
348c2ecf20Sopenharmony_ci	writel_relaxed(0, ddata.base + LPC_LPT_LSB_OFF);
358c2ecf20Sopenharmony_ci	writel_relaxed(1, ddata.base + LPC_LPT_START_OFF);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic u64 notrace st_clksrc_sched_clock_read(void)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	return (u64)readl_relaxed(ddata.base + LPC_LPT_LSB_OFF);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int __init st_clksrc_init(void)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	unsigned long rate;
468c2ecf20Sopenharmony_ci	int ret;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	st_clksrc_reset();
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	rate = clk_get_rate(ddata.clk);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	sched_clock_register(st_clksrc_sched_clock_read, 32, rate);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	ret = clocksource_mmio_init(ddata.base + LPC_LPT_LSB_OFF,
558c2ecf20Sopenharmony_ci				    "clksrc-st-lpc", rate, 300, 32,
568c2ecf20Sopenharmony_ci				    clocksource_mmio_readl_up);
578c2ecf20Sopenharmony_ci	if (ret) {
588c2ecf20Sopenharmony_ci		pr_err("clksrc-st-lpc: Failed to register clocksource\n");
598c2ecf20Sopenharmony_ci		return ret;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return 0;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int __init st_clksrc_setup_clk(struct device_node *np)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct clk *clk;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	clk = of_clk_get(np, 0);
708c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
718c2ecf20Sopenharmony_ci		pr_err("clksrc-st-lpc: Failed to get LPC clock\n");
728c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (clk_prepare_enable(clk)) {
768c2ecf20Sopenharmony_ci		pr_err("clksrc-st-lpc: Failed to enable LPC clock\n");
778c2ecf20Sopenharmony_ci		return -EINVAL;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (!clk_get_rate(clk)) {
818c2ecf20Sopenharmony_ci		pr_err("clksrc-st-lpc: Failed to get LPC clock rate\n");
828c2ecf20Sopenharmony_ci		clk_disable_unprepare(clk);
838c2ecf20Sopenharmony_ci		return -EINVAL;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	ddata.clk = clk;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return 0;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int __init st_clksrc_of_register(struct device_node *np)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	int ret;
948c2ecf20Sopenharmony_ci	uint32_t mode;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
978c2ecf20Sopenharmony_ci	if (ret) {
988c2ecf20Sopenharmony_ci		pr_err("clksrc-st-lpc: An LPC mode must be provided\n");
998c2ecf20Sopenharmony_ci		return ret;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* LPC can either run as a Clocksource or in RTC or WDT mode */
1038c2ecf20Sopenharmony_ci	if (mode != ST_LPC_MODE_CLKSRC)
1048c2ecf20Sopenharmony_ci		return 0;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	ddata.base = of_iomap(np, 0);
1078c2ecf20Sopenharmony_ci	if (!ddata.base) {
1088c2ecf20Sopenharmony_ci		pr_err("clksrc-st-lpc: Unable to map iomem\n");
1098c2ecf20Sopenharmony_ci		return -ENXIO;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	ret = st_clksrc_setup_clk(np);
1138c2ecf20Sopenharmony_ci	if (ret) {
1148c2ecf20Sopenharmony_ci		iounmap(ddata.base);
1158c2ecf20Sopenharmony_ci		return ret;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	ret = st_clksrc_init();
1198c2ecf20Sopenharmony_ci	if (ret) {
1208c2ecf20Sopenharmony_ci		clk_disable_unprepare(ddata.clk);
1218c2ecf20Sopenharmony_ci		clk_put(ddata.clk);
1228c2ecf20Sopenharmony_ci		iounmap(ddata.base);
1238c2ecf20Sopenharmony_ci		return ret;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	pr_info("clksrc-st-lpc: clocksource initialised - running @ %luHz\n",
1278c2ecf20Sopenharmony_ci		clk_get_rate(ddata.clk));
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return ret;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(ddata, "st,stih407-lpc", st_clksrc_of_register);
132