blob: 1c5909bb160563fa5a7ba7a95284697b964a2760 [file] [log] [blame]
Fabrice Gasnierd8958822017-08-28 12:04:13 +02001/*
2 * STM32 Low-Power Timer Encoder and Counter driver
3 *
4 * Copyright (C) STMicroelectronics 2017
5 *
6 * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
7 *
8 * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
9 *
10 * License terms: GNU General Public License (GPL), version 2
11 */
12
13#include <linux/bitfield.h>
14#include <linux/iio/iio.h>
15#include <linux/mfd/stm32-lptimer.h>
16#include <linux/module.h>
17#include <linux/platform_device.h>
18
19struct stm32_lptim_cnt {
20 struct device *dev;
21 struct regmap *regmap;
22 struct clk *clk;
23 u32 preset;
24 u32 polarity;
25 u32 quadrature_mode;
26};
27
28static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
29{
30 u32 val;
31 int ret;
32
33 ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
34 if (ret)
35 return ret;
36
37 return FIELD_GET(STM32_LPTIM_ENABLE, val);
38}
39
40static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
41 int enable)
42{
43 int ret;
44 u32 val;
45
46 val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
47 ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
48 if (ret)
49 return ret;
50
51 if (!enable) {
52 clk_disable(priv->clk);
53 return 0;
54 }
55
56 /* LP timer must be enabled before writing CMP & ARR */
57 ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
58 if (ret)
59 return ret;
60
61 ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
62 if (ret)
63 return ret;
64
65 /* ensure CMP & ARR registers are properly written */
66 ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
67 (val & STM32_LPTIM_CMPOK_ARROK),
68 100, 1000);
69 if (ret)
70 return ret;
71
72 ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
73 STM32_LPTIM_CMPOKCF_ARROKCF);
74 if (ret)
75 return ret;
76
77 ret = clk_enable(priv->clk);
78 if (ret) {
79 regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
80 return ret;
81 }
82
83 /* Start LP timer in continuous mode */
84 return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
85 STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
86}
87
88static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
89{
90 u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
91 STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
92 u32 val;
93
94 /* Setup LP timer encoder/counter and polarity, without prescaler */
95 if (priv->quadrature_mode)
96 val = enable ? STM32_LPTIM_ENC : 0;
97 else
98 val = enable ? STM32_LPTIM_COUNTMODE : 0;
99 val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
100
101 return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
102}
103
104static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
105 struct iio_chan_spec const *chan,
106 int val, int val2, long mask)
107{
108 struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
109 int ret;
110
111 switch (mask) {
112 case IIO_CHAN_INFO_ENABLE:
113 if (val < 0 || val > 1)
114 return -EINVAL;
115
116 /* Check nobody uses the timer, or already disabled/enabled */
117 ret = stm32_lptim_is_enabled(priv);
118 if ((ret < 0) || (!ret && !val))
119 return ret;
120 if (val && ret)
121 return -EBUSY;
122
123 ret = stm32_lptim_setup(priv, val);
124 if (ret)
125 return ret;
126 return stm32_lptim_set_enable_state(priv, val);
127
128 default:
129 return -EINVAL;
130 }
131}
132
133static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
134 struct iio_chan_spec const *chan,
135 int *val, int *val2, long mask)
136{
137 struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
138 u32 dat;
139 int ret;
140
141 switch (mask) {
142 case IIO_CHAN_INFO_RAW:
143 ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
144 if (ret)
145 return ret;
146 *val = dat;
147 return IIO_VAL_INT;
148
149 case IIO_CHAN_INFO_ENABLE:
150 ret = stm32_lptim_is_enabled(priv);
151 if (ret < 0)
152 return ret;
153 *val = ret;
154 return IIO_VAL_INT;
155
156 case IIO_CHAN_INFO_SCALE:
157 /* Non-quadrature mode: scale = 1 */
158 *val = 1;
159 *val2 = 0;
160 if (priv->quadrature_mode) {
161 /*
162 * Quadrature encoder mode:
163 * - both edges, quarter cycle, scale is 0.25
164 * - either rising/falling edge scale is 0.5
165 */
166 if (priv->polarity > 1)
167 *val2 = 2;
168 else
169 *val2 = 1;
170 }
171 return IIO_VAL_FRACTIONAL_LOG2;
172
173 default:
174 return -EINVAL;
175 }
176}
177
178static const struct iio_info stm32_lptim_cnt_iio_info = {
179 .read_raw = stm32_lptim_read_raw,
180 .write_raw = stm32_lptim_write_raw,
181 .driver_module = THIS_MODULE,
182};
183
184static const char *const stm32_lptim_quadrature_modes[] = {
185 "non-quadrature",
186 "quadrature",
187};
188
189static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
190 const struct iio_chan_spec *chan)
191{
192 struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
193
194 return priv->quadrature_mode;
195}
196
197static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
198 const struct iio_chan_spec *chan,
199 unsigned int type)
200{
201 struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
202
203 if (stm32_lptim_is_enabled(priv))
204 return -EBUSY;
205
206 priv->quadrature_mode = type;
207
208 return 0;
209}
210
211static const struct iio_enum stm32_lptim_quadrature_mode_en = {
212 .items = stm32_lptim_quadrature_modes,
213 .num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
214 .get = stm32_lptim_get_quadrature_mode,
215 .set = stm32_lptim_set_quadrature_mode,
216};
217
218static const char * const stm32_lptim_cnt_polarity[] = {
219 "rising-edge", "falling-edge", "both-edges",
220};
221
222static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
223 const struct iio_chan_spec *chan)
224{
225 struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
226
227 return priv->polarity;
228}
229
230static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
231 const struct iio_chan_spec *chan,
232 unsigned int type)
233{
234 struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
235
236 if (stm32_lptim_is_enabled(priv))
237 return -EBUSY;
238
239 priv->polarity = type;
240
241 return 0;
242}
243
244static const struct iio_enum stm32_lptim_cnt_polarity_en = {
245 .items = stm32_lptim_cnt_polarity,
246 .num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
247 .get = stm32_lptim_cnt_get_polarity,
248 .set = stm32_lptim_cnt_set_polarity,
249};
250
251static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
252 uintptr_t private,
253 const struct iio_chan_spec *chan,
254 char *buf)
255{
256 struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
257
258 return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
259}
260
261static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
262 uintptr_t private,
263 const struct iio_chan_spec *chan,
264 const char *buf, size_t len)
265{
266 struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
267 int ret;
268
269 if (stm32_lptim_is_enabled(priv))
270 return -EBUSY;
271
272 ret = kstrtouint(buf, 0, &priv->preset);
273 if (ret)
274 return ret;
275
276 if (priv->preset > STM32_LPTIM_MAX_ARR)
277 return -EINVAL;
278
279 return len;
280}
281
282/* LP timer with encoder */
283static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
284 {
285 .name = "preset",
286 .shared = IIO_SEPARATE,
287 .read = stm32_lptim_cnt_get_preset,
288 .write = stm32_lptim_cnt_set_preset,
289 },
290 IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
291 IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
292 IIO_ENUM("quadrature_mode", IIO_SEPARATE,
293 &stm32_lptim_quadrature_mode_en),
294 IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
295 {}
296};
297
298static const struct iio_chan_spec stm32_lptim_enc_channels = {
299 .type = IIO_COUNT,
300 .channel = 0,
301 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
302 BIT(IIO_CHAN_INFO_ENABLE) |
303 BIT(IIO_CHAN_INFO_SCALE),
304 .ext_info = stm32_lptim_enc_ext_info,
305 .indexed = 1,
306};
307
308/* LP timer without encoder (counter only) */
309static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
310 {
311 .name = "preset",
312 .shared = IIO_SEPARATE,
313 .read = stm32_lptim_cnt_get_preset,
314 .write = stm32_lptim_cnt_set_preset,
315 },
316 IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
317 IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
318 {}
319};
320
321static const struct iio_chan_spec stm32_lptim_cnt_channels = {
322 .type = IIO_COUNT,
323 .channel = 0,
324 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
325 BIT(IIO_CHAN_INFO_ENABLE) |
326 BIT(IIO_CHAN_INFO_SCALE),
327 .ext_info = stm32_lptim_cnt_ext_info,
328 .indexed = 1,
329};
330
331static int stm32_lptim_cnt_probe(struct platform_device *pdev)
332{
333 struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
334 struct stm32_lptim_cnt *priv;
335 struct iio_dev *indio_dev;
336
337 if (IS_ERR_OR_NULL(ddata))
338 return -EINVAL;
339
340 indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
341 if (!indio_dev)
342 return -ENOMEM;
343
344 priv = iio_priv(indio_dev);
345 priv->dev = &pdev->dev;
346 priv->regmap = ddata->regmap;
347 priv->clk = ddata->clk;
348 priv->preset = STM32_LPTIM_MAX_ARR;
349
350 indio_dev->name = dev_name(&pdev->dev);
351 indio_dev->dev.parent = &pdev->dev;
352 indio_dev->dev.of_node = pdev->dev.of_node;
353 indio_dev->info = &stm32_lptim_cnt_iio_info;
354 if (ddata->has_encoder)
355 indio_dev->channels = &stm32_lptim_enc_channels;
356 else
357 indio_dev->channels = &stm32_lptim_cnt_channels;
358 indio_dev->num_channels = 1;
359
360 platform_set_drvdata(pdev, priv);
361
362 return devm_iio_device_register(&pdev->dev, indio_dev);
363}
364
365static const struct of_device_id stm32_lptim_cnt_of_match[] = {
366 { .compatible = "st,stm32-lptimer-counter", },
367 {},
368};
369MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
370
371static struct platform_driver stm32_lptim_cnt_driver = {
372 .probe = stm32_lptim_cnt_probe,
373 .driver = {
374 .name = "stm32-lptimer-counter",
375 .of_match_table = stm32_lptim_cnt_of_match,
376 },
377};
378module_platform_driver(stm32_lptim_cnt_driver);
379
380MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
381MODULE_ALIAS("platform:stm32-lptimer-counter");
382MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
383MODULE_LICENSE("GPL v2");