cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pwm-stm32-lp.c (6553B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * STM32 Low-Power Timer PWM driver
      4 *
      5 * Copyright (C) STMicroelectronics 2017
      6 *
      7 * Author: Gerald Baeza <gerald.baeza@st.com>
      8 *
      9 * Inspired by Gerald Baeza's pwm-stm32 driver
     10 */
     11
     12#include <linux/bitfield.h>
     13#include <linux/mfd/stm32-lptimer.h>
     14#include <linux/module.h>
     15#include <linux/of.h>
     16#include <linux/pinctrl/consumer.h>
     17#include <linux/platform_device.h>
     18#include <linux/pwm.h>
     19
     20struct stm32_pwm_lp {
     21	struct pwm_chip chip;
     22	struct clk *clk;
     23	struct regmap *regmap;
     24};
     25
     26static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
     27{
     28	return container_of(chip, struct stm32_pwm_lp, chip);
     29}
     30
     31/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */
     32#define STM32_LPTIM_MAX_PRESCALER	128
     33
     34static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
     35			      const struct pwm_state *state)
     36{
     37	struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
     38	unsigned long long prd, div, dty;
     39	struct pwm_state cstate;
     40	u32 val, mask, cfgr, presc = 0;
     41	bool reenable;
     42	int ret;
     43
     44	pwm_get_state(pwm, &cstate);
     45	reenable = !cstate.enabled;
     46
     47	if (!state->enabled) {
     48		if (cstate.enabled) {
     49			/* Disable LP timer */
     50			ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
     51			if (ret)
     52				return ret;
     53			/* disable clock to PWM counter */
     54			clk_disable(priv->clk);
     55		}
     56		return 0;
     57	}
     58
     59	/* Calculate the period and prescaler value */
     60	div = (unsigned long long)clk_get_rate(priv->clk) * state->period;
     61	do_div(div, NSEC_PER_SEC);
     62	if (!div) {
     63		/* Clock is too slow to achieve requested period. */
     64		dev_dbg(priv->chip.dev, "Can't reach %llu ns\n", state->period);
     65		return -EINVAL;
     66	}
     67
     68	prd = div;
     69	while (div > STM32_LPTIM_MAX_ARR) {
     70		presc++;
     71		if ((1 << presc) > STM32_LPTIM_MAX_PRESCALER) {
     72			dev_err(priv->chip.dev, "max prescaler exceeded\n");
     73			return -EINVAL;
     74		}
     75		div = prd >> presc;
     76	}
     77	prd = div;
     78
     79	/* Calculate the duty cycle */
     80	dty = prd * state->duty_cycle;
     81	do_div(dty, state->period);
     82
     83	if (!cstate.enabled) {
     84		/* enable clock to drive PWM counter */
     85		ret = clk_enable(priv->clk);
     86		if (ret)
     87			return ret;
     88	}
     89
     90	ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
     91	if (ret)
     92		goto err;
     93
     94	if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) ||
     95	    (FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity)) {
     96		val = FIELD_PREP(STM32_LPTIM_PRESC, presc);
     97		val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
     98		mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
     99
    100		/* Must disable LP timer to modify CFGR */
    101		reenable = true;
    102		ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
    103		if (ret)
    104			goto err;
    105
    106		ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
    107					 val);
    108		if (ret)
    109			goto err;
    110	}
    111
    112	if (reenable) {
    113		/* Must (re)enable LP timer to modify CMP & ARR */
    114		ret = regmap_write(priv->regmap, STM32_LPTIM_CR,
    115				   STM32_LPTIM_ENABLE);
    116		if (ret)
    117			goto err;
    118	}
    119
    120	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, prd - 1);
    121	if (ret)
    122		goto err;
    123
    124	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, prd - (1 + dty));
    125	if (ret)
    126		goto err;
    127
    128	/* ensure CMP & ARR registers are properly written */
    129	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
    130				       (val & STM32_LPTIM_CMPOK_ARROK),
    131				       100, 1000);
    132	if (ret) {
    133		dev_err(priv->chip.dev, "ARR/CMP registers write issue\n");
    134		goto err;
    135	}
    136	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
    137			   STM32_LPTIM_CMPOKCF_ARROKCF);
    138	if (ret)
    139		goto err;
    140
    141	if (reenable) {
    142		/* Start LP timer in continuous mode */
    143		ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
    144					 STM32_LPTIM_CNTSTRT,
    145					 STM32_LPTIM_CNTSTRT);
    146		if (ret) {
    147			regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
    148			goto err;
    149		}
    150	}
    151
    152	return 0;
    153err:
    154	if (!cstate.enabled)
    155		clk_disable(priv->clk);
    156
    157	return ret;
    158}
    159
    160static void stm32_pwm_lp_get_state(struct pwm_chip *chip,
    161				   struct pwm_device *pwm,
    162				   struct pwm_state *state)
    163{
    164	struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
    165	unsigned long rate = clk_get_rate(priv->clk);
    166	u32 val, presc, prd;
    167	u64 tmp;
    168
    169	regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
    170	state->enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
    171	/* Keep PWM counter clock refcount in sync with PWM initial state */
    172	if (state->enabled)
    173		clk_enable(priv->clk);
    174
    175	regmap_read(priv->regmap, STM32_LPTIM_CFGR, &val);
    176	presc = FIELD_GET(STM32_LPTIM_PRESC, val);
    177	state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, val);
    178
    179	regmap_read(priv->regmap, STM32_LPTIM_ARR, &prd);
    180	tmp = prd + 1;
    181	tmp = (tmp << presc) * NSEC_PER_SEC;
    182	state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
    183
    184	regmap_read(priv->regmap, STM32_LPTIM_CMP, &val);
    185	tmp = prd - val;
    186	tmp = (tmp << presc) * NSEC_PER_SEC;
    187	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
    188}
    189
    190static const struct pwm_ops stm32_pwm_lp_ops = {
    191	.owner = THIS_MODULE,
    192	.apply = stm32_pwm_lp_apply,
    193	.get_state = stm32_pwm_lp_get_state,
    194};
    195
    196static int stm32_pwm_lp_probe(struct platform_device *pdev)
    197{
    198	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
    199	struct stm32_pwm_lp *priv;
    200	int ret;
    201
    202	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    203	if (!priv)
    204		return -ENOMEM;
    205
    206	priv->regmap = ddata->regmap;
    207	priv->clk = ddata->clk;
    208	priv->chip.dev = &pdev->dev;
    209	priv->chip.ops = &stm32_pwm_lp_ops;
    210	priv->chip.npwm = 1;
    211
    212	ret = devm_pwmchip_add(&pdev->dev, &priv->chip);
    213	if (ret < 0)
    214		return ret;
    215
    216	platform_set_drvdata(pdev, priv);
    217
    218	return 0;
    219}
    220
    221static int __maybe_unused stm32_pwm_lp_suspend(struct device *dev)
    222{
    223	struct stm32_pwm_lp *priv = dev_get_drvdata(dev);
    224	struct pwm_state state;
    225
    226	pwm_get_state(&priv->chip.pwms[0], &state);
    227	if (state.enabled) {
    228		dev_err(dev, "The consumer didn't stop us (%s)\n",
    229			priv->chip.pwms[0].label);
    230		return -EBUSY;
    231	}
    232
    233	return pinctrl_pm_select_sleep_state(dev);
    234}
    235
    236static int __maybe_unused stm32_pwm_lp_resume(struct device *dev)
    237{
    238	return pinctrl_pm_select_default_state(dev);
    239}
    240
    241static SIMPLE_DEV_PM_OPS(stm32_pwm_lp_pm_ops, stm32_pwm_lp_suspend,
    242			 stm32_pwm_lp_resume);
    243
    244static const struct of_device_id stm32_pwm_lp_of_match[] = {
    245	{ .compatible = "st,stm32-pwm-lp", },
    246	{},
    247};
    248MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match);
    249
    250static struct platform_driver stm32_pwm_lp_driver = {
    251	.probe	= stm32_pwm_lp_probe,
    252	.driver	= {
    253		.name = "stm32-pwm-lp",
    254		.of_match_table = of_match_ptr(stm32_pwm_lp_of_match),
    255		.pm = &stm32_pwm_lp_pm_ops,
    256	},
    257};
    258module_platform_driver(stm32_pwm_lp_driver);
    259
    260MODULE_ALIAS("platform:stm32-pwm-lp");
    261MODULE_DESCRIPTION("STMicroelectronics STM32 PWM LP driver");
    262MODULE_LICENSE("GPL v2");