brintos

brintos / linux-shallow public Read only

0
0
Text · 13.2 KiB · 8439755 Raw
524 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * STM32 Low-Power Timer Encoder and Counter driver4 *5 * Copyright (C) STMicroelectronics 20176 *7 * Author: Fabrice Gasnier <fabrice.gasnier@st.com>8 *9 * Inspired by 104-quad-8 and stm32-timer-trigger drivers.10 *11 */12 13#include <linux/bitfield.h>14#include <linux/counter.h>15#include <linux/mfd/stm32-lptimer.h>16#include <linux/mod_devicetable.h>17#include <linux/module.h>18#include <linux/pinctrl/consumer.h>19#include <linux/platform_device.h>20#include <linux/types.h>21 22struct stm32_lptim_cnt {23	struct device *dev;24	struct regmap *regmap;25	struct clk *clk;26	u32 ceiling;27	u32 polarity;28	u32 quadrature_mode;29	bool enabled;30};31 32static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)33{34	u32 val;35	int ret;36 37	ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);38	if (ret)39		return ret;40 41	return FIELD_GET(STM32_LPTIM_ENABLE, val);42}43 44static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,45					int enable)46{47	int ret;48	u32 val;49 50	val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);51	ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);52	if (ret)53		return ret;54 55	if (!enable) {56		clk_disable(priv->clk);57		priv->enabled = false;58		return 0;59	}60 61	/* LP timer must be enabled before writing CMP & ARR */62	ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->ceiling);63	if (ret)64		return ret;65 66	ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);67	if (ret)68		return ret;69 70	/* ensure CMP & ARR registers are properly written */71	ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,72				       (val & STM32_LPTIM_CMPOK_ARROK) == STM32_LPTIM_CMPOK_ARROK,73				       100, 1000);74	if (ret)75		return ret;76 77	ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,78			   STM32_LPTIM_CMPOKCF_ARROKCF);79	if (ret)80		return ret;81 82	ret = clk_enable(priv->clk);83	if (ret) {84		regmap_write(priv->regmap, STM32_LPTIM_CR, 0);85		return ret;86	}87	priv->enabled = true;88 89	/* Start LP timer in continuous mode */90	return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,91				  STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);92}93 94static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)95{96	u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |97		   STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;98	u32 val;99 100	/* Setup LP timer encoder/counter and polarity, without prescaler */101	if (priv->quadrature_mode)102		val = enable ? STM32_LPTIM_ENC : 0;103	else104		val = enable ? STM32_LPTIM_COUNTMODE : 0;105	val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);106 107	return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);108}109 110/*111 * In non-quadrature mode, device counts up on active edge.112 * In quadrature mode, encoder counting scenarios are as follows:113 * +---------+----------+--------------------+--------------------+114 * | Active  | Level on |      IN1 signal    |     IN2 signal     |115 * | edge    | opposite +----------+---------+----------+---------+116 * |         | signal   |  Rising  | Falling |  Rising  | Falling |117 * +---------+----------+----------+---------+----------+---------+118 * | Rising  | High ->  |   Down   |    -    |   Up     |    -    |119 * | edge    | Low  ->  |   Up     |    -    |   Down   |    -    |120 * +---------+----------+----------+---------+----------+---------+121 * | Falling | High ->  |    -     |   Up    |    -     |   Down  |122 * | edge    | Low  ->  |    -     |   Down  |    -     |   Up    |123 * +---------+----------+----------+---------+----------+---------+124 * | Both    | High ->  |   Down   |   Up    |   Up     |   Down  |125 * | edges   | Low  ->  |   Up     |   Down  |   Down   |   Up    |126 * +---------+----------+----------+---------+----------+---------+127 */128static const enum counter_function stm32_lptim_cnt_functions[] = {129	COUNTER_FUNCTION_INCREASE,130	COUNTER_FUNCTION_QUADRATURE_X4,131};132 133static const enum counter_synapse_action stm32_lptim_cnt_synapse_actions[] = {134	COUNTER_SYNAPSE_ACTION_RISING_EDGE,135	COUNTER_SYNAPSE_ACTION_FALLING_EDGE,136	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,137	COUNTER_SYNAPSE_ACTION_NONE,138};139 140static int stm32_lptim_cnt_read(struct counter_device *counter,141				struct counter_count *count, u64 *val)142{143	struct stm32_lptim_cnt *const priv = counter_priv(counter);144	u32 cnt;145	int ret;146 147	ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &cnt);148	if (ret)149		return ret;150 151	*val = cnt;152 153	return 0;154}155 156static int stm32_lptim_cnt_function_read(struct counter_device *counter,157					 struct counter_count *count,158					 enum counter_function *function)159{160	struct stm32_lptim_cnt *const priv = counter_priv(counter);161 162	if (!priv->quadrature_mode) {163		*function = COUNTER_FUNCTION_INCREASE;164		return 0;165	}166 167	if (priv->polarity == STM32_LPTIM_CKPOL_BOTH_EDGES) {168		*function = COUNTER_FUNCTION_QUADRATURE_X4;169		return 0;170	}171 172	return -EINVAL;173}174 175static int stm32_lptim_cnt_function_write(struct counter_device *counter,176					  struct counter_count *count,177					  enum counter_function function)178{179	struct stm32_lptim_cnt *const priv = counter_priv(counter);180 181	if (stm32_lptim_is_enabled(priv))182		return -EBUSY;183 184	switch (function) {185	case COUNTER_FUNCTION_INCREASE:186		priv->quadrature_mode = 0;187		return 0;188	case COUNTER_FUNCTION_QUADRATURE_X4:189		priv->quadrature_mode = 1;190		priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;191		return 0;192	default:193		/* should never reach this path */194		return -EINVAL;195	}196}197 198static int stm32_lptim_cnt_enable_read(struct counter_device *counter,199				       struct counter_count *count,200				       u8 *enable)201{202	struct stm32_lptim_cnt *const priv = counter_priv(counter);203	int ret;204 205	ret = stm32_lptim_is_enabled(priv);206	if (ret < 0)207		return ret;208 209	*enable = ret;210 211	return 0;212}213 214static int stm32_lptim_cnt_enable_write(struct counter_device *counter,215					struct counter_count *count,216					u8 enable)217{218	struct stm32_lptim_cnt *const priv = counter_priv(counter);219	int ret;220 221	/* Check nobody uses the timer, or already disabled/enabled */222	ret = stm32_lptim_is_enabled(priv);223	if ((ret < 0) || (!ret && !enable))224		return ret;225	if (enable && ret)226		return -EBUSY;227 228	ret = stm32_lptim_setup(priv, enable);229	if (ret)230		return ret;231 232	ret = stm32_lptim_set_enable_state(priv, enable);233	if (ret)234		return ret;235 236	return 0;237}238 239static int stm32_lptim_cnt_ceiling_read(struct counter_device *counter,240					struct counter_count *count,241					u64 *ceiling)242{243	struct stm32_lptim_cnt *const priv = counter_priv(counter);244 245	*ceiling = priv->ceiling;246 247	return 0;248}249 250static int stm32_lptim_cnt_ceiling_write(struct counter_device *counter,251					 struct counter_count *count,252					 u64 ceiling)253{254	struct stm32_lptim_cnt *const priv = counter_priv(counter);255 256	if (stm32_lptim_is_enabled(priv))257		return -EBUSY;258 259	if (ceiling > STM32_LPTIM_MAX_ARR)260		return -ERANGE;261 262	priv->ceiling = ceiling;263 264	return 0;265}266 267static struct counter_comp stm32_lptim_cnt_ext[] = {268	COUNTER_COMP_ENABLE(stm32_lptim_cnt_enable_read,269			    stm32_lptim_cnt_enable_write),270	COUNTER_COMP_CEILING(stm32_lptim_cnt_ceiling_read,271			     stm32_lptim_cnt_ceiling_write),272};273 274static int stm32_lptim_cnt_action_read(struct counter_device *counter,275				       struct counter_count *count,276				       struct counter_synapse *synapse,277				       enum counter_synapse_action *action)278{279	struct stm32_lptim_cnt *const priv = counter_priv(counter);280	enum counter_function function;281	int err;282 283	err = stm32_lptim_cnt_function_read(counter, count, &function);284	if (err)285		return err;286 287	switch (function) {288	case COUNTER_FUNCTION_INCREASE:289		/* LP Timer acts as up-counter on input 1 */290		if (synapse->signal->id != count->synapses[0].signal->id) {291			*action = COUNTER_SYNAPSE_ACTION_NONE;292			return 0;293		}294 295		switch (priv->polarity) {296		case STM32_LPTIM_CKPOL_RISING_EDGE:297			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;298			return 0;299		case STM32_LPTIM_CKPOL_FALLING_EDGE:300			*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;301			return 0;302		case STM32_LPTIM_CKPOL_BOTH_EDGES:303			*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;304			return 0;305		default:306			/* should never reach this path */307			return -EINVAL;308		}309	case COUNTER_FUNCTION_QUADRATURE_X4:310		*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;311		return 0;312	default:313		/* should never reach this path */314		return -EINVAL;315	}316}317 318static int stm32_lptim_cnt_action_write(struct counter_device *counter,319					struct counter_count *count,320					struct counter_synapse *synapse,321					enum counter_synapse_action action)322{323	struct stm32_lptim_cnt *const priv = counter_priv(counter);324	enum counter_function function;325	int err;326 327	if (stm32_lptim_is_enabled(priv))328		return -EBUSY;329 330	err = stm32_lptim_cnt_function_read(counter, count, &function);331	if (err)332		return err;333 334	/* only set polarity when in counter mode (on input 1) */335	if (function != COUNTER_FUNCTION_INCREASE336	    || synapse->signal->id != count->synapses[0].signal->id)337		return -EINVAL;338 339	switch (action) {340	case COUNTER_SYNAPSE_ACTION_RISING_EDGE:341		priv->polarity = STM32_LPTIM_CKPOL_RISING_EDGE;342		return 0;343	case COUNTER_SYNAPSE_ACTION_FALLING_EDGE:344		priv->polarity = STM32_LPTIM_CKPOL_FALLING_EDGE;345		return 0;346	case COUNTER_SYNAPSE_ACTION_BOTH_EDGES:347		priv->polarity = STM32_LPTIM_CKPOL_BOTH_EDGES;348		return 0;349	default:350		return -EINVAL;351	}352}353 354static const struct counter_ops stm32_lptim_cnt_ops = {355	.count_read = stm32_lptim_cnt_read,356	.function_read = stm32_lptim_cnt_function_read,357	.function_write = stm32_lptim_cnt_function_write,358	.action_read = stm32_lptim_cnt_action_read,359	.action_write = stm32_lptim_cnt_action_write,360};361 362static struct counter_signal stm32_lptim_cnt_signals[] = {363	{364		.id = 0,365		.name = "Channel 1 Quadrature A"366	},367	{368		.id = 1,369		.name = "Channel 1 Quadrature B"370	}371};372 373static struct counter_synapse stm32_lptim_cnt_synapses[] = {374	{375		.actions_list = stm32_lptim_cnt_synapse_actions,376		.num_actions = ARRAY_SIZE(stm32_lptim_cnt_synapse_actions),377		.signal = &stm32_lptim_cnt_signals[0]378	},379	{380		.actions_list = stm32_lptim_cnt_synapse_actions,381		.num_actions = ARRAY_SIZE(stm32_lptim_cnt_synapse_actions),382		.signal = &stm32_lptim_cnt_signals[1]383	}384};385 386/* LP timer with encoder */387static struct counter_count stm32_lptim_enc_counts = {388	.id = 0,389	.name = "LPTimer Count",390	.functions_list = stm32_lptim_cnt_functions,391	.num_functions = ARRAY_SIZE(stm32_lptim_cnt_functions),392	.synapses = stm32_lptim_cnt_synapses,393	.num_synapses = ARRAY_SIZE(stm32_lptim_cnt_synapses),394	.ext = stm32_lptim_cnt_ext,395	.num_ext = ARRAY_SIZE(stm32_lptim_cnt_ext)396};397 398/* LP timer without encoder (counter only) */399static struct counter_count stm32_lptim_in1_counts = {400	.id = 0,401	.name = "LPTimer Count",402	.functions_list = stm32_lptim_cnt_functions,403	.num_functions = 1,404	.synapses = stm32_lptim_cnt_synapses,405	.num_synapses = 1,406	.ext = stm32_lptim_cnt_ext,407	.num_ext = ARRAY_SIZE(stm32_lptim_cnt_ext)408};409 410static int stm32_lptim_cnt_probe(struct platform_device *pdev)411{412	struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);413	struct counter_device *counter;414	struct stm32_lptim_cnt *priv;415	int ret;416 417	if (IS_ERR_OR_NULL(ddata))418		return -EINVAL;419 420	counter = devm_counter_alloc(&pdev->dev, sizeof(*priv));421	if (!counter)422		return -ENOMEM;423	priv = counter_priv(counter);424 425	priv->dev = &pdev->dev;426	priv->regmap = ddata->regmap;427	priv->clk = ddata->clk;428	priv->ceiling = STM32_LPTIM_MAX_ARR;429 430	/* Initialize Counter device */431	counter->name = dev_name(&pdev->dev);432	counter->parent = &pdev->dev;433	counter->ops = &stm32_lptim_cnt_ops;434	if (ddata->has_encoder) {435		counter->counts = &stm32_lptim_enc_counts;436		counter->num_signals = ARRAY_SIZE(stm32_lptim_cnt_signals);437	} else {438		counter->counts = &stm32_lptim_in1_counts;439		counter->num_signals = 1;440	}441	counter->num_counts = 1;442	counter->signals = stm32_lptim_cnt_signals;443 444	platform_set_drvdata(pdev, priv);445 446	ret = devm_counter_add(&pdev->dev, counter);447	if (ret < 0)448		return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");449 450	return 0;451}452 453#ifdef CONFIG_PM_SLEEP454static int stm32_lptim_cnt_suspend(struct device *dev)455{456	struct stm32_lptim_cnt *priv = dev_get_drvdata(dev);457	int ret;458 459	/* Only take care of enabled counter: don't disturb other MFD child */460	if (priv->enabled) {461		ret = stm32_lptim_setup(priv, 0);462		if (ret)463			return ret;464 465		ret = stm32_lptim_set_enable_state(priv, 0);466		if (ret)467			return ret;468 469		/* Force enable state for later resume */470		priv->enabled = true;471	}472 473	return pinctrl_pm_select_sleep_state(dev);474}475 476static int stm32_lptim_cnt_resume(struct device *dev)477{478	struct stm32_lptim_cnt *priv = dev_get_drvdata(dev);479	int ret;480 481	ret = pinctrl_pm_select_default_state(dev);482	if (ret)483		return ret;484 485	if (priv->enabled) {486		priv->enabled = false;487		ret = stm32_lptim_setup(priv, 1);488		if (ret)489			return ret;490 491		ret = stm32_lptim_set_enable_state(priv, 1);492		if (ret)493			return ret;494	}495 496	return 0;497}498#endif499 500static SIMPLE_DEV_PM_OPS(stm32_lptim_cnt_pm_ops, stm32_lptim_cnt_suspend,501			 stm32_lptim_cnt_resume);502 503static const struct of_device_id stm32_lptim_cnt_of_match[] = {504	{ .compatible = "st,stm32-lptimer-counter", },505	{},506};507MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);508 509static struct platform_driver stm32_lptim_cnt_driver = {510	.probe = stm32_lptim_cnt_probe,511	.driver = {512		.name = "stm32-lptimer-counter",513		.of_match_table = stm32_lptim_cnt_of_match,514		.pm = &stm32_lptim_cnt_pm_ops,515	},516};517module_platform_driver(stm32_lptim_cnt_driver);518 519MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");520MODULE_ALIAS("platform:stm32-lptimer-counter");521MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");522MODULE_LICENSE("GPL v2");523MODULE_IMPORT_NS(COUNTER);524