162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) STMicroelectronics 2019 - All Rights Reserved
462306a36Sopenharmony_ci * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
562306a36Sopenharmony_ci *	    Pascal Paillet <p.paillet@st.com> for STMicroelectronics.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk.h>
962306a36Sopenharmony_ci#include <linux/clockchips.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/mfd/stm32-lptimer.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/of_address.h>
1462306a36Sopenharmony_ci#include <linux/of_irq.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/pm_wakeirq.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define CFGR_PSC_OFFSET		9
1962306a36Sopenharmony_ci#define STM32_LP_RATING		1000
2062306a36Sopenharmony_ci#define STM32_TARGET_CLKRATE	(32000 * HZ)
2162306a36Sopenharmony_ci#define STM32_LP_MAX_PSC	7
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct stm32_lp_private {
2462306a36Sopenharmony_ci	struct regmap *reg;
2562306a36Sopenharmony_ci	struct clock_event_device clkevt;
2662306a36Sopenharmony_ci	unsigned long period;
2762306a36Sopenharmony_ci	struct device *dev;
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic struct stm32_lp_private*
3162306a36Sopenharmony_cito_priv(struct clock_event_device *clkevt)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	return container_of(clkevt, struct stm32_lp_private, clkevt);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int stm32_clkevent_lp_shutdown(struct clock_event_device *clkevt)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct stm32_lp_private *priv = to_priv(clkevt);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	regmap_write(priv->reg, STM32_LPTIM_CR, 0);
4162306a36Sopenharmony_ci	regmap_write(priv->reg, STM32_LPTIM_IER, 0);
4262306a36Sopenharmony_ci	/* clear pending flags */
4362306a36Sopenharmony_ci	regmap_write(priv->reg, STM32_LPTIM_ICR, STM32_LPTIM_ARRMCF);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	return 0;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int stm32_clkevent_lp_set_timer(unsigned long evt,
4962306a36Sopenharmony_ci				       struct clock_event_device *clkevt,
5062306a36Sopenharmony_ci				       int is_periodic)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct stm32_lp_private *priv = to_priv(clkevt);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* disable LPTIMER to be able to write into IER register*/
5562306a36Sopenharmony_ci	regmap_write(priv->reg, STM32_LPTIM_CR, 0);
5662306a36Sopenharmony_ci	/* enable ARR interrupt */
5762306a36Sopenharmony_ci	regmap_write(priv->reg, STM32_LPTIM_IER, STM32_LPTIM_ARRMIE);
5862306a36Sopenharmony_ci	/* enable LPTIMER to be able to write into ARR register */
5962306a36Sopenharmony_ci	regmap_write(priv->reg, STM32_LPTIM_CR, STM32_LPTIM_ENABLE);
6062306a36Sopenharmony_ci	/* set next event counter */
6162306a36Sopenharmony_ci	regmap_write(priv->reg, STM32_LPTIM_ARR, evt);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	/* start counter */
6462306a36Sopenharmony_ci	if (is_periodic)
6562306a36Sopenharmony_ci		regmap_write(priv->reg, STM32_LPTIM_CR,
6662306a36Sopenharmony_ci			     STM32_LPTIM_CNTSTRT | STM32_LPTIM_ENABLE);
6762306a36Sopenharmony_ci	else
6862306a36Sopenharmony_ci		regmap_write(priv->reg, STM32_LPTIM_CR,
6962306a36Sopenharmony_ci			     STM32_LPTIM_SNGSTRT | STM32_LPTIM_ENABLE);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return 0;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int stm32_clkevent_lp_set_next_event(unsigned long evt,
7562306a36Sopenharmony_ci					    struct clock_event_device *clkevt)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	return stm32_clkevent_lp_set_timer(evt, clkevt,
7862306a36Sopenharmony_ci					   clockevent_state_periodic(clkevt));
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic int stm32_clkevent_lp_set_periodic(struct clock_event_device *clkevt)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct stm32_lp_private *priv = to_priv(clkevt);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return stm32_clkevent_lp_set_timer(priv->period, clkevt, true);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int stm32_clkevent_lp_set_oneshot(struct clock_event_device *clkevt)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct stm32_lp_private *priv = to_priv(clkevt);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return stm32_clkevent_lp_set_timer(priv->period, clkevt, false);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic irqreturn_t stm32_clkevent_lp_irq_handler(int irq, void *dev_id)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct clock_event_device *clkevt = (struct clock_event_device *)dev_id;
9862306a36Sopenharmony_ci	struct stm32_lp_private *priv = to_priv(clkevt);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	regmap_write(priv->reg, STM32_LPTIM_ICR, STM32_LPTIM_ARRMCF);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (clkevt->event_handler)
10362306a36Sopenharmony_ci		clkevt->event_handler(clkevt);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return IRQ_HANDLED;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic void stm32_clkevent_lp_set_prescaler(struct stm32_lp_private *priv,
10962306a36Sopenharmony_ci					    unsigned long *rate)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	int i;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	for (i = 0; i <= STM32_LP_MAX_PSC; i++) {
11462306a36Sopenharmony_ci		if (DIV_ROUND_CLOSEST(*rate, 1 << i) < STM32_TARGET_CLKRATE)
11562306a36Sopenharmony_ci			break;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	regmap_write(priv->reg, STM32_LPTIM_CFGR, i << CFGR_PSC_OFFSET);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Adjust rate and period given the prescaler value */
12162306a36Sopenharmony_ci	*rate = DIV_ROUND_CLOSEST(*rate, (1 << i));
12262306a36Sopenharmony_ci	priv->period = DIV_ROUND_UP(*rate, HZ);
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic void stm32_clkevent_lp_init(struct stm32_lp_private *priv,
12662306a36Sopenharmony_ci				  struct device_node *np, unsigned long rate)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	priv->clkevt.name = np->full_name;
12962306a36Sopenharmony_ci	priv->clkevt.cpumask = cpu_possible_mask;
13062306a36Sopenharmony_ci	priv->clkevt.features = CLOCK_EVT_FEAT_PERIODIC |
13162306a36Sopenharmony_ci				CLOCK_EVT_FEAT_ONESHOT;
13262306a36Sopenharmony_ci	priv->clkevt.set_state_shutdown = stm32_clkevent_lp_shutdown;
13362306a36Sopenharmony_ci	priv->clkevt.set_state_periodic = stm32_clkevent_lp_set_periodic;
13462306a36Sopenharmony_ci	priv->clkevt.set_state_oneshot = stm32_clkevent_lp_set_oneshot;
13562306a36Sopenharmony_ci	priv->clkevt.set_next_event = stm32_clkevent_lp_set_next_event;
13662306a36Sopenharmony_ci	priv->clkevt.rating = STM32_LP_RATING;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	clockevents_config_and_register(&priv->clkevt, rate, 0x1,
13962306a36Sopenharmony_ci					STM32_LPTIM_MAX_ARR);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int stm32_clkevent_lp_probe(struct platform_device *pdev)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
14562306a36Sopenharmony_ci	struct stm32_lp_private *priv;
14662306a36Sopenharmony_ci	unsigned long rate;
14762306a36Sopenharmony_ci	int ret, irq;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
15062306a36Sopenharmony_ci	if (!priv)
15162306a36Sopenharmony_ci		return -ENOMEM;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	priv->reg = ddata->regmap;
15462306a36Sopenharmony_ci	ret = clk_prepare_enable(ddata->clk);
15562306a36Sopenharmony_ci	if (ret)
15662306a36Sopenharmony_ci		return -EINVAL;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	rate = clk_get_rate(ddata->clk);
15962306a36Sopenharmony_ci	if (!rate) {
16062306a36Sopenharmony_ci		ret = -EINVAL;
16162306a36Sopenharmony_ci		goto out_clk_disable;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	irq = platform_get_irq(to_platform_device(pdev->dev.parent), 0);
16562306a36Sopenharmony_ci	if (irq <= 0) {
16662306a36Sopenharmony_ci		ret = irq;
16762306a36Sopenharmony_ci		goto out_clk_disable;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (of_property_read_bool(pdev->dev.parent->of_node, "wakeup-source")) {
17162306a36Sopenharmony_ci		ret = device_init_wakeup(&pdev->dev, true);
17262306a36Sopenharmony_ci		if (ret)
17362306a36Sopenharmony_ci			goto out_clk_disable;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		ret = dev_pm_set_wake_irq(&pdev->dev, irq);
17662306a36Sopenharmony_ci		if (ret)
17762306a36Sopenharmony_ci			goto out_clk_disable;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, stm32_clkevent_lp_irq_handler,
18162306a36Sopenharmony_ci			       IRQF_TIMER, pdev->name, &priv->clkevt);
18262306a36Sopenharmony_ci	if (ret)
18362306a36Sopenharmony_ci		goto out_clk_disable;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	stm32_clkevent_lp_set_prescaler(priv, &rate);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	stm32_clkevent_lp_init(priv, pdev->dev.parent->of_node, rate);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	priv->dev = &pdev->dev;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return 0;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ciout_clk_disable:
19462306a36Sopenharmony_ci	clk_disable_unprepare(ddata->clk);
19562306a36Sopenharmony_ci	return ret;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic const struct of_device_id stm32_clkevent_lp_of_match[] = {
19962306a36Sopenharmony_ci	{ .compatible = "st,stm32-lptimer-timer", },
20062306a36Sopenharmony_ci	{},
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, stm32_clkevent_lp_of_match);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic struct platform_driver stm32_clkevent_lp_driver = {
20562306a36Sopenharmony_ci	.probe  = stm32_clkevent_lp_probe,
20662306a36Sopenharmony_ci	.driver	= {
20762306a36Sopenharmony_ci		.name = "stm32-lptimer-timer",
20862306a36Sopenharmony_ci		.of_match_table = stm32_clkevent_lp_of_match,
20962306a36Sopenharmony_ci		.suppress_bind_attrs = true,
21062306a36Sopenharmony_ci	},
21162306a36Sopenharmony_ci};
21262306a36Sopenharmony_cimodule_platform_driver(stm32_clkevent_lp_driver);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ciMODULE_ALIAS("platform:stm32-lptimer-timer");
21562306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics STM32 clockevent low power driver");
216