xref: /linux/drivers/clocksource/clksrc_st_lpc.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*2874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
270bef01cSLee Jones /*
370bef01cSLee Jones  * Clocksource using the Low Power Timer found in the Low Power Controller (LPC)
470bef01cSLee Jones  *
570bef01cSLee Jones  * Copyright (C) 2015 STMicroelectronics – All Rights Reserved
670bef01cSLee Jones  *
770bef01cSLee Jones  * Author(s): Francesco Virlinzi <francesco.virlinzi@st.com>
870bef01cSLee Jones  *	      Ajit Pal Singh <ajitpal.singh@st.com>
970bef01cSLee Jones  */
1070bef01cSLee Jones 
1170bef01cSLee Jones #include <linux/clk.h>
1270bef01cSLee Jones #include <linux/clocksource.h>
1370bef01cSLee Jones #include <linux/init.h>
1470bef01cSLee Jones #include <linux/of_address.h>
15ff45d8ddSLee Jones #include <linux/sched_clock.h>
1670bef01cSLee Jones #include <linux/slab.h>
1770bef01cSLee Jones 
1870bef01cSLee Jones #include <dt-bindings/mfd/st-lpc.h>
1970bef01cSLee Jones 
2070bef01cSLee Jones /* Low Power Timer */
2170bef01cSLee Jones #define LPC_LPT_LSB_OFF		0x400
2270bef01cSLee Jones #define LPC_LPT_MSB_OFF		0x404
2370bef01cSLee Jones #define LPC_LPT_START_OFF	0x408
2470bef01cSLee Jones 
2570bef01cSLee Jones static struct st_clksrc_ddata {
2670bef01cSLee Jones 	struct clk		*clk;
2770bef01cSLee Jones 	void __iomem		*base;
2870bef01cSLee Jones } ddata;
2970bef01cSLee Jones 
st_clksrc_reset(void)3070bef01cSLee Jones static void __init st_clksrc_reset(void)
3170bef01cSLee Jones {
3270bef01cSLee Jones 	writel_relaxed(0, ddata.base + LPC_LPT_START_OFF);
3370bef01cSLee Jones 	writel_relaxed(0, ddata.base + LPC_LPT_MSB_OFF);
3470bef01cSLee Jones 	writel_relaxed(0, ddata.base + LPC_LPT_LSB_OFF);
3570bef01cSLee Jones 	writel_relaxed(1, ddata.base + LPC_LPT_START_OFF);
3670bef01cSLee Jones }
3770bef01cSLee Jones 
st_clksrc_sched_clock_read(void)38ff45d8ddSLee Jones static u64 notrace st_clksrc_sched_clock_read(void)
39ff45d8ddSLee Jones {
40ff45d8ddSLee Jones 	return (u64)readl_relaxed(ddata.base + LPC_LPT_LSB_OFF);
41ff45d8ddSLee Jones }
42ff45d8ddSLee Jones 
st_clksrc_init(void)4370bef01cSLee Jones static int __init st_clksrc_init(void)
4470bef01cSLee Jones {
4570bef01cSLee Jones 	unsigned long rate;
4670bef01cSLee Jones 	int ret;
4770bef01cSLee Jones 
4870bef01cSLee Jones 	st_clksrc_reset();
4970bef01cSLee Jones 
5070bef01cSLee Jones 	rate = clk_get_rate(ddata.clk);
5170bef01cSLee Jones 
52ff45d8ddSLee Jones 	sched_clock_register(st_clksrc_sched_clock_read, 32, rate);
53ff45d8ddSLee Jones 
5470bef01cSLee Jones 	ret = clocksource_mmio_init(ddata.base + LPC_LPT_LSB_OFF,
5570bef01cSLee Jones 				    "clksrc-st-lpc", rate, 300, 32,
5670bef01cSLee Jones 				    clocksource_mmio_readl_up);
5770bef01cSLee Jones 	if (ret) {
5870bef01cSLee Jones 		pr_err("clksrc-st-lpc: Failed to register clocksource\n");
5970bef01cSLee Jones 		return ret;
6070bef01cSLee Jones 	}
6170bef01cSLee Jones 
6270bef01cSLee Jones 	return 0;
6370bef01cSLee Jones }
6470bef01cSLee Jones 
st_clksrc_setup_clk(struct device_node * np)6570bef01cSLee Jones static int __init st_clksrc_setup_clk(struct device_node *np)
6670bef01cSLee Jones {
6770bef01cSLee Jones 	struct clk *clk;
6870bef01cSLee Jones 
6970bef01cSLee Jones 	clk = of_clk_get(np, 0);
7070bef01cSLee Jones 	if (IS_ERR(clk)) {
7170bef01cSLee Jones 		pr_err("clksrc-st-lpc: Failed to get LPC clock\n");
7270bef01cSLee Jones 		return PTR_ERR(clk);
7370bef01cSLee Jones 	}
7470bef01cSLee Jones 
7570bef01cSLee Jones 	if (clk_prepare_enable(clk)) {
7670bef01cSLee Jones 		pr_err("clksrc-st-lpc: Failed to enable LPC clock\n");
7770bef01cSLee Jones 		return -EINVAL;
7870bef01cSLee Jones 	}
7970bef01cSLee Jones 
8070bef01cSLee Jones 	if (!clk_get_rate(clk)) {
8170bef01cSLee Jones 		pr_err("clksrc-st-lpc: Failed to get LPC clock rate\n");
8270bef01cSLee Jones 		clk_disable_unprepare(clk);
8370bef01cSLee Jones 		return -EINVAL;
8470bef01cSLee Jones 	}
8570bef01cSLee Jones 
8670bef01cSLee Jones 	ddata.clk = clk;
8770bef01cSLee Jones 
8870bef01cSLee Jones 	return 0;
8970bef01cSLee Jones }
9070bef01cSLee Jones 
st_clksrc_of_register(struct device_node * np)9184309e0aSDaniel Lezcano static int __init st_clksrc_of_register(struct device_node *np)
9270bef01cSLee Jones {
9370bef01cSLee Jones 	int ret;
9470bef01cSLee Jones 	uint32_t mode;
9570bef01cSLee Jones 
9670bef01cSLee Jones 	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
9770bef01cSLee Jones 	if (ret) {
9870bef01cSLee Jones 		pr_err("clksrc-st-lpc: An LPC mode must be provided\n");
9984309e0aSDaniel Lezcano 		return ret;
10070bef01cSLee Jones 	}
10170bef01cSLee Jones 
10270bef01cSLee Jones 	/* LPC can either run as a Clocksource or in RTC or WDT mode */
10370bef01cSLee Jones 	if (mode != ST_LPC_MODE_CLKSRC)
10484309e0aSDaniel Lezcano 		return 0;
10570bef01cSLee Jones 
10670bef01cSLee Jones 	ddata.base = of_iomap(np, 0);
10770bef01cSLee Jones 	if (!ddata.base) {
10870bef01cSLee Jones 		pr_err("clksrc-st-lpc: Unable to map iomem\n");
10984309e0aSDaniel Lezcano 		return -ENXIO;
11070bef01cSLee Jones 	}
11170bef01cSLee Jones 
11284309e0aSDaniel Lezcano 	ret = st_clksrc_setup_clk(np);
11384309e0aSDaniel Lezcano 	if (ret) {
11470bef01cSLee Jones 		iounmap(ddata.base);
11584309e0aSDaniel Lezcano 		return ret;
11670bef01cSLee Jones 	}
11770bef01cSLee Jones 
11884309e0aSDaniel Lezcano 	ret = st_clksrc_init();
11984309e0aSDaniel Lezcano 	if (ret) {
12070bef01cSLee Jones 		clk_disable_unprepare(ddata.clk);
12170bef01cSLee Jones 		clk_put(ddata.clk);
12270bef01cSLee Jones 		iounmap(ddata.base);
12384309e0aSDaniel Lezcano 		return ret;
12470bef01cSLee Jones 	}
12570bef01cSLee Jones 
12670bef01cSLee Jones 	pr_info("clksrc-st-lpc: clocksource initialised - running @ %luHz\n",
12770bef01cSLee Jones 		clk_get_rate(ddata.clk));
12884309e0aSDaniel Lezcano 
12984309e0aSDaniel Lezcano 	return ret;
13070bef01cSLee Jones }
13117273395SDaniel Lezcano TIMER_OF_DECLARE(ddata, "st,stih407-lpc", st_clksrc_of_register);
132