xref: /linux/drivers/clocksource/timer-stm32-lp.c (revision b6f228e800ccf285906bb1c4c366ce3848a5443e)
148b41c5eSBenjamin Gaignard // SPDX-License-Identifier: GPL-2.0
248b41c5eSBenjamin Gaignard /*
348b41c5eSBenjamin Gaignard  * Copyright (C) STMicroelectronics 2019 - All Rights Reserved
448b41c5eSBenjamin Gaignard  * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
548b41c5eSBenjamin Gaignard  *	    Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
648b41c5eSBenjamin Gaignard  */
748b41c5eSBenjamin Gaignard 
848b41c5eSBenjamin Gaignard #include <linux/clk.h>
948b41c5eSBenjamin Gaignard #include <linux/clockchips.h>
1048b41c5eSBenjamin Gaignard #include <linux/interrupt.h>
1148b41c5eSBenjamin Gaignard #include <linux/mfd/stm32-lptimer.h>
1248b41c5eSBenjamin Gaignard #include <linux/module.h>
1348b41c5eSBenjamin Gaignard #include <linux/of_address.h>
1448b41c5eSBenjamin Gaignard #include <linux/of_irq.h>
1548b41c5eSBenjamin Gaignard #include <linux/platform_device.h>
1648b41c5eSBenjamin Gaignard #include <linux/pm_wakeirq.h>
1748b41c5eSBenjamin Gaignard 
1848b41c5eSBenjamin Gaignard #define CFGR_PSC_OFFSET		9
1948b41c5eSBenjamin Gaignard #define STM32_LP_RATING		1000
2048b41c5eSBenjamin Gaignard #define STM32_TARGET_CLKRATE	(32000 * HZ)
2148b41c5eSBenjamin Gaignard #define STM32_LP_MAX_PSC	7
2248b41c5eSBenjamin Gaignard 
2348b41c5eSBenjamin Gaignard struct stm32_lp_private {
2448b41c5eSBenjamin Gaignard 	struct regmap *reg;
2548b41c5eSBenjamin Gaignard 	struct clock_event_device clkevt;
2648b41c5eSBenjamin Gaignard 	unsigned long period;
2748b41c5eSBenjamin Gaignard 	struct device *dev;
2848b41c5eSBenjamin Gaignard };
2948b41c5eSBenjamin Gaignard 
3048b41c5eSBenjamin Gaignard static struct stm32_lp_private*
3148b41c5eSBenjamin Gaignard to_priv(struct clock_event_device *clkevt)
3248b41c5eSBenjamin Gaignard {
3348b41c5eSBenjamin Gaignard 	return container_of(clkevt, struct stm32_lp_private, clkevt);
3448b41c5eSBenjamin Gaignard }
3548b41c5eSBenjamin Gaignard 
3648b41c5eSBenjamin Gaignard static int stm32_clkevent_lp_shutdown(struct clock_event_device *clkevt)
3748b41c5eSBenjamin Gaignard {
3848b41c5eSBenjamin Gaignard 	struct stm32_lp_private *priv = to_priv(clkevt);
3948b41c5eSBenjamin Gaignard 
4048b41c5eSBenjamin Gaignard 	regmap_write(priv->reg, STM32_LPTIM_CR, 0);
4148b41c5eSBenjamin Gaignard 	regmap_write(priv->reg, STM32_LPTIM_IER, 0);
4248b41c5eSBenjamin Gaignard 	/* clear pending flags */
4348b41c5eSBenjamin Gaignard 	regmap_write(priv->reg, STM32_LPTIM_ICR, STM32_LPTIM_ARRMCF);
4448b41c5eSBenjamin Gaignard 
4548b41c5eSBenjamin Gaignard 	return 0;
4648b41c5eSBenjamin Gaignard }
4748b41c5eSBenjamin Gaignard 
4848b41c5eSBenjamin Gaignard static int stm32_clkevent_lp_set_timer(unsigned long evt,
4948b41c5eSBenjamin Gaignard 				       struct clock_event_device *clkevt,
5048b41c5eSBenjamin Gaignard 				       int is_periodic)
5148b41c5eSBenjamin Gaignard {
5248b41c5eSBenjamin Gaignard 	struct stm32_lp_private *priv = to_priv(clkevt);
5348b41c5eSBenjamin Gaignard 
5448b41c5eSBenjamin Gaignard 	/* disable LPTIMER to be able to write into IER register*/
5548b41c5eSBenjamin Gaignard 	regmap_write(priv->reg, STM32_LPTIM_CR, 0);
5648b41c5eSBenjamin Gaignard 	/* enable ARR interrupt */
5748b41c5eSBenjamin Gaignard 	regmap_write(priv->reg, STM32_LPTIM_IER, STM32_LPTIM_ARRMIE);
5848b41c5eSBenjamin Gaignard 	/* enable LPTIMER to be able to write into ARR register */
5948b41c5eSBenjamin Gaignard 	regmap_write(priv->reg, STM32_LPTIM_CR, STM32_LPTIM_ENABLE);
6048b41c5eSBenjamin Gaignard 	/* set next event counter */
6148b41c5eSBenjamin Gaignard 	regmap_write(priv->reg, STM32_LPTIM_ARR, evt);
6248b41c5eSBenjamin Gaignard 
6348b41c5eSBenjamin Gaignard 	/* start counter */
6448b41c5eSBenjamin Gaignard 	if (is_periodic)
6548b41c5eSBenjamin Gaignard 		regmap_write(priv->reg, STM32_LPTIM_CR,
6648b41c5eSBenjamin Gaignard 			     STM32_LPTIM_CNTSTRT | STM32_LPTIM_ENABLE);
6748b41c5eSBenjamin Gaignard 	else
6848b41c5eSBenjamin Gaignard 		regmap_write(priv->reg, STM32_LPTIM_CR,
6948b41c5eSBenjamin Gaignard 			     STM32_LPTIM_SNGSTRT | STM32_LPTIM_ENABLE);
7048b41c5eSBenjamin Gaignard 
7148b41c5eSBenjamin Gaignard 	return 0;
7248b41c5eSBenjamin Gaignard }
7348b41c5eSBenjamin Gaignard 
7448b41c5eSBenjamin Gaignard static int stm32_clkevent_lp_set_next_event(unsigned long evt,
7548b41c5eSBenjamin Gaignard 					    struct clock_event_device *clkevt)
7648b41c5eSBenjamin Gaignard {
7748b41c5eSBenjamin Gaignard 	return stm32_clkevent_lp_set_timer(evt, clkevt,
7848b41c5eSBenjamin Gaignard 					   clockevent_state_periodic(clkevt));
7948b41c5eSBenjamin Gaignard }
8048b41c5eSBenjamin Gaignard 
8148b41c5eSBenjamin Gaignard static int stm32_clkevent_lp_set_periodic(struct clock_event_device *clkevt)
8248b41c5eSBenjamin Gaignard {
8348b41c5eSBenjamin Gaignard 	struct stm32_lp_private *priv = to_priv(clkevt);
8448b41c5eSBenjamin Gaignard 
8548b41c5eSBenjamin Gaignard 	return stm32_clkevent_lp_set_timer(priv->period, clkevt, true);
8648b41c5eSBenjamin Gaignard }
8748b41c5eSBenjamin Gaignard 
8848b41c5eSBenjamin Gaignard static int stm32_clkevent_lp_set_oneshot(struct clock_event_device *clkevt)
8948b41c5eSBenjamin Gaignard {
9048b41c5eSBenjamin Gaignard 	struct stm32_lp_private *priv = to_priv(clkevt);
9148b41c5eSBenjamin Gaignard 
9248b41c5eSBenjamin Gaignard 	return stm32_clkevent_lp_set_timer(priv->period, clkevt, false);
9348b41c5eSBenjamin Gaignard }
9448b41c5eSBenjamin Gaignard 
9548b41c5eSBenjamin Gaignard static irqreturn_t stm32_clkevent_lp_irq_handler(int irq, void *dev_id)
9648b41c5eSBenjamin Gaignard {
9748b41c5eSBenjamin Gaignard 	struct clock_event_device *clkevt = (struct clock_event_device *)dev_id;
9848b41c5eSBenjamin Gaignard 	struct stm32_lp_private *priv = to_priv(clkevt);
9948b41c5eSBenjamin Gaignard 
10048b41c5eSBenjamin Gaignard 	regmap_write(priv->reg, STM32_LPTIM_ICR, STM32_LPTIM_ARRMCF);
10148b41c5eSBenjamin Gaignard 
10248b41c5eSBenjamin Gaignard 	if (clkevt->event_handler)
10348b41c5eSBenjamin Gaignard 		clkevt->event_handler(clkevt);
10448b41c5eSBenjamin Gaignard 
10548b41c5eSBenjamin Gaignard 	return IRQ_HANDLED;
10648b41c5eSBenjamin Gaignard }
10748b41c5eSBenjamin Gaignard 
10848b41c5eSBenjamin Gaignard static void stm32_clkevent_lp_set_prescaler(struct stm32_lp_private *priv,
10948b41c5eSBenjamin Gaignard 					    unsigned long *rate)
11048b41c5eSBenjamin Gaignard {
11148b41c5eSBenjamin Gaignard 	int i;
11248b41c5eSBenjamin Gaignard 
11348b41c5eSBenjamin Gaignard 	for (i = 0; i <= STM32_LP_MAX_PSC; i++) {
11448b41c5eSBenjamin Gaignard 		if (DIV_ROUND_CLOSEST(*rate, 1 << i) < STM32_TARGET_CLKRATE)
11548b41c5eSBenjamin Gaignard 			break;
11648b41c5eSBenjamin Gaignard 	}
11748b41c5eSBenjamin Gaignard 
11848b41c5eSBenjamin Gaignard 	regmap_write(priv->reg, STM32_LPTIM_CFGR, i << CFGR_PSC_OFFSET);
11948b41c5eSBenjamin Gaignard 
12048b41c5eSBenjamin Gaignard 	/* Adjust rate and period given the prescaler value */
12148b41c5eSBenjamin Gaignard 	*rate = DIV_ROUND_CLOSEST(*rate, (1 << i));
12248b41c5eSBenjamin Gaignard 	priv->period = DIV_ROUND_UP(*rate, HZ);
12348b41c5eSBenjamin Gaignard }
12448b41c5eSBenjamin Gaignard 
12548b41c5eSBenjamin Gaignard static void stm32_clkevent_lp_init(struct stm32_lp_private *priv,
12648b41c5eSBenjamin Gaignard 				  struct device_node *np, unsigned long rate)
12748b41c5eSBenjamin Gaignard {
12848b41c5eSBenjamin Gaignard 	priv->clkevt.name = np->full_name;
12948b41c5eSBenjamin Gaignard 	priv->clkevt.cpumask = cpu_possible_mask;
13048b41c5eSBenjamin Gaignard 	priv->clkevt.features = CLOCK_EVT_FEAT_PERIODIC |
13148b41c5eSBenjamin Gaignard 				CLOCK_EVT_FEAT_ONESHOT;
13248b41c5eSBenjamin Gaignard 	priv->clkevt.set_state_shutdown = stm32_clkevent_lp_shutdown;
13348b41c5eSBenjamin Gaignard 	priv->clkevt.set_state_periodic = stm32_clkevent_lp_set_periodic;
13448b41c5eSBenjamin Gaignard 	priv->clkevt.set_state_oneshot = stm32_clkevent_lp_set_oneshot;
13548b41c5eSBenjamin Gaignard 	priv->clkevt.set_next_event = stm32_clkevent_lp_set_next_event;
13648b41c5eSBenjamin Gaignard 	priv->clkevt.rating = STM32_LP_RATING;
13748b41c5eSBenjamin Gaignard 
13848b41c5eSBenjamin Gaignard 	clockevents_config_and_register(&priv->clkevt, rate, 0x1,
13948b41c5eSBenjamin Gaignard 					STM32_LPTIM_MAX_ARR);
14048b41c5eSBenjamin Gaignard }
14148b41c5eSBenjamin Gaignard 
14248b41c5eSBenjamin Gaignard static int stm32_clkevent_lp_probe(struct platform_device *pdev)
14348b41c5eSBenjamin Gaignard {
14448b41c5eSBenjamin Gaignard 	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
14548b41c5eSBenjamin Gaignard 	struct stm32_lp_private *priv;
14648b41c5eSBenjamin Gaignard 	unsigned long rate;
14748b41c5eSBenjamin Gaignard 	int ret, irq;
14848b41c5eSBenjamin Gaignard 
14948b41c5eSBenjamin Gaignard 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
15048b41c5eSBenjamin Gaignard 	if (!priv)
15148b41c5eSBenjamin Gaignard 		return -ENOMEM;
15248b41c5eSBenjamin Gaignard 
15348b41c5eSBenjamin Gaignard 	priv->reg = ddata->regmap;
15448b41c5eSBenjamin Gaignard 	ret = clk_prepare_enable(ddata->clk);
15548b41c5eSBenjamin Gaignard 	if (ret)
15648b41c5eSBenjamin Gaignard 		return -EINVAL;
15748b41c5eSBenjamin Gaignard 
15848b41c5eSBenjamin Gaignard 	rate = clk_get_rate(ddata->clk);
15948b41c5eSBenjamin Gaignard 	if (!rate) {
16048b41c5eSBenjamin Gaignard 		ret = -EINVAL;
16148b41c5eSBenjamin Gaignard 		goto out_clk_disable;
16248b41c5eSBenjamin Gaignard 	}
16348b41c5eSBenjamin Gaignard 
16448b41c5eSBenjamin Gaignard 	irq = platform_get_irq(to_platform_device(pdev->dev.parent), 0);
16548b41c5eSBenjamin Gaignard 	if (irq <= 0) {
16648b41c5eSBenjamin Gaignard 		ret = irq;
16748b41c5eSBenjamin Gaignard 		goto out_clk_disable;
16848b41c5eSBenjamin Gaignard 	}
16948b41c5eSBenjamin Gaignard 
17048b41c5eSBenjamin Gaignard 	if (of_property_read_bool(pdev->dev.parent->of_node, "wakeup-source")) {
17148b41c5eSBenjamin Gaignard 		ret = device_init_wakeup(&pdev->dev, true);
17248b41c5eSBenjamin Gaignard 		if (ret)
17348b41c5eSBenjamin Gaignard 			goto out_clk_disable;
17448b41c5eSBenjamin Gaignard 
17548b41c5eSBenjamin Gaignard 		ret = dev_pm_set_wake_irq(&pdev->dev, irq);
17648b41c5eSBenjamin Gaignard 		if (ret)
17748b41c5eSBenjamin Gaignard 			goto out_clk_disable;
17848b41c5eSBenjamin Gaignard 	}
17948b41c5eSBenjamin Gaignard 
18048b41c5eSBenjamin Gaignard 	ret = devm_request_irq(&pdev->dev, irq, stm32_clkevent_lp_irq_handler,
18148b41c5eSBenjamin Gaignard 			       IRQF_TIMER, pdev->name, &priv->clkevt);
18248b41c5eSBenjamin Gaignard 	if (ret)
18348b41c5eSBenjamin Gaignard 		goto out_clk_disable;
18448b41c5eSBenjamin Gaignard 
18548b41c5eSBenjamin Gaignard 	stm32_clkevent_lp_set_prescaler(priv, &rate);
18648b41c5eSBenjamin Gaignard 
18748b41c5eSBenjamin Gaignard 	stm32_clkevent_lp_init(priv, pdev->dev.parent->of_node, rate);
18848b41c5eSBenjamin Gaignard 
18948b41c5eSBenjamin Gaignard 	priv->dev = &pdev->dev;
19048b41c5eSBenjamin Gaignard 
19148b41c5eSBenjamin Gaignard 	return 0;
19248b41c5eSBenjamin Gaignard 
19348b41c5eSBenjamin Gaignard out_clk_disable:
19448b41c5eSBenjamin Gaignard 	clk_disable_unprepare(ddata->clk);
19548b41c5eSBenjamin Gaignard 	return ret;
19648b41c5eSBenjamin Gaignard }
19748b41c5eSBenjamin Gaignard 
19848b41c5eSBenjamin Gaignard static const struct of_device_id stm32_clkevent_lp_of_match[] = {
19948b41c5eSBenjamin Gaignard 	{ .compatible = "st,stm32-lptimer-timer", },
20048b41c5eSBenjamin Gaignard 	{},
20148b41c5eSBenjamin Gaignard };
20248b41c5eSBenjamin Gaignard MODULE_DEVICE_TABLE(of, stm32_clkevent_lp_of_match);
20348b41c5eSBenjamin Gaignard 
20448b41c5eSBenjamin Gaignard static struct platform_driver stm32_clkevent_lp_driver = {
20548b41c5eSBenjamin Gaignard 	.probe  = stm32_clkevent_lp_probe,
20648b41c5eSBenjamin Gaignard 	.driver	= {
20748b41c5eSBenjamin Gaignard 		.name = "stm32-lptimer-timer",
208*b6f228e8SKrzysztof Kozlowski 		.of_match_table = stm32_clkevent_lp_of_match,
209ede38f92SUwe Kleine-König 		.suppress_bind_attrs = true,
21048b41c5eSBenjamin Gaignard 	},
21148b41c5eSBenjamin Gaignard };
21248b41c5eSBenjamin Gaignard module_platform_driver(stm32_clkevent_lp_driver);
21348b41c5eSBenjamin Gaignard 
21448b41c5eSBenjamin Gaignard MODULE_ALIAS("platform:stm32-lptimer-timer");
21548b41c5eSBenjamin Gaignard MODULE_DESCRIPTION("STMicroelectronics STM32 clockevent low power driver");
21648b41c5eSBenjamin Gaignard MODULE_LICENSE("GPL v2");
217