Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/counter/ftm-quaddec.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Flex Timer Module Quadrature decoder
4
*
5
* This module implements a driver for decoding the FTM quadrature
6
* of ex. a LS1021A
7
*/
8
9
#include <linux/fsl/ftm.h>
10
#include <linux/module.h>
11
#include <linux/platform_device.h>
12
#include <linux/of.h>
13
#include <linux/io.h>
14
#include <linux/mutex.h>
15
#include <linux/counter.h>
16
#include <linux/bitfield.h>
17
#include <linux/types.h>
18
19
#define FTM_FIELD_UPDATE(ftm, offset, mask, val) \
20
({ \
21
uint32_t flags; \
22
ftm_read(ftm, offset, &flags); \
23
flags &= ~mask; \
24
flags |= FIELD_PREP(mask, val); \
25
ftm_write(ftm, offset, flags); \
26
})
27
28
struct ftm_quaddec {
29
struct platform_device *pdev;
30
void __iomem *ftm_base;
31
bool big_endian;
32
struct mutex ftm_quaddec_mutex;
33
};
34
35
static void ftm_read(struct ftm_quaddec *ftm, uint32_t offset, uint32_t *data)
36
{
37
if (ftm->big_endian)
38
*data = ioread32be(ftm->ftm_base + offset);
39
else
40
*data = ioread32(ftm->ftm_base + offset);
41
}
42
43
static void ftm_write(struct ftm_quaddec *ftm, uint32_t offset, uint32_t data)
44
{
45
if (ftm->big_endian)
46
iowrite32be(data, ftm->ftm_base + offset);
47
else
48
iowrite32(data, ftm->ftm_base + offset);
49
}
50
51
/* Hold mutex before modifying write protection state */
52
static void ftm_clear_write_protection(struct ftm_quaddec *ftm)
53
{
54
uint32_t flag;
55
56
/* First see if it is enabled */
57
ftm_read(ftm, FTM_FMS, &flag);
58
59
if (flag & FTM_FMS_WPEN)
60
FTM_FIELD_UPDATE(ftm, FTM_MODE, FTM_MODE_WPDIS, 1);
61
}
62
63
static void ftm_set_write_protection(struct ftm_quaddec *ftm)
64
{
65
FTM_FIELD_UPDATE(ftm, FTM_FMS, FTM_FMS_WPEN, 1);
66
}
67
68
static void ftm_reset_counter(struct ftm_quaddec *ftm)
69
{
70
/* Reset hardware counter to CNTIN */
71
ftm_write(ftm, FTM_CNT, 0x0);
72
}
73
74
static void ftm_quaddec_init(struct ftm_quaddec *ftm)
75
{
76
ftm_clear_write_protection(ftm);
77
78
/*
79
* Do not write in the region from the CNTIN register through the
80
* PWMLOAD register when FTMEN = 0.
81
* Also reset other fields to zero
82
*/
83
ftm_write(ftm, FTM_MODE, FTM_MODE_FTMEN);
84
ftm_write(ftm, FTM_CNTIN, 0x0000);
85
ftm_write(ftm, FTM_MOD, 0xffff);
86
ftm_write(ftm, FTM_CNT, 0x0);
87
/* Set prescaler, reset other fields to zero */
88
ftm_write(ftm, FTM_SC, FTM_SC_PS_1);
89
90
/* Select quad mode, reset other fields to zero */
91
ftm_write(ftm, FTM_QDCTRL, FTM_QDCTRL_QUADEN);
92
93
/* Unused features and reset to default section */
94
ftm_write(ftm, FTM_POL, 0x0);
95
ftm_write(ftm, FTM_FLTCTRL, 0x0);
96
ftm_write(ftm, FTM_SYNCONF, 0x0);
97
ftm_write(ftm, FTM_SYNC, 0xffff);
98
99
/* Lock the FTM */
100
ftm_set_write_protection(ftm);
101
}
102
103
static void ftm_quaddec_disable(void *ftm)
104
{
105
struct ftm_quaddec *ftm_qua = ftm;
106
107
ftm_clear_write_protection(ftm_qua);
108
ftm_write(ftm_qua, FTM_MODE, 0);
109
ftm_write(ftm_qua, FTM_QDCTRL, 0);
110
/*
111
* This is enough to disable the counter. No clock has been
112
* selected by writing to FTM_SC in init()
113
*/
114
ftm_set_write_protection(ftm_qua);
115
}
116
117
static int ftm_quaddec_get_prescaler(struct counter_device *counter,
118
struct counter_count *count, u32 *cnt_mode)
119
{
120
struct ftm_quaddec *ftm = counter_priv(counter);
121
uint32_t scflags;
122
123
ftm_read(ftm, FTM_SC, &scflags);
124
125
*cnt_mode = FIELD_GET(FTM_SC_PS_MASK, scflags);
126
127
return 0;
128
}
129
130
static int ftm_quaddec_set_prescaler(struct counter_device *counter,
131
struct counter_count *count, u32 cnt_mode)
132
{
133
struct ftm_quaddec *ftm = counter_priv(counter);
134
135
mutex_lock(&ftm->ftm_quaddec_mutex);
136
137
ftm_clear_write_protection(ftm);
138
FTM_FIELD_UPDATE(ftm, FTM_SC, FTM_SC_PS_MASK, cnt_mode);
139
ftm_set_write_protection(ftm);
140
141
/* Also resets the counter as it is undefined anyway now */
142
ftm_reset_counter(ftm);
143
144
mutex_unlock(&ftm->ftm_quaddec_mutex);
145
return 0;
146
}
147
148
static const char * const ftm_quaddec_prescaler[] = {
149
"1", "2", "4", "8", "16", "32", "64", "128"
150
};
151
152
static const enum counter_synapse_action ftm_quaddec_synapse_actions[] = {
153
COUNTER_SYNAPSE_ACTION_BOTH_EDGES
154
};
155
156
static const enum counter_function ftm_quaddec_count_functions[] = {
157
COUNTER_FUNCTION_QUADRATURE_X4
158
};
159
160
static int ftm_quaddec_count_read(struct counter_device *counter,
161
struct counter_count *count,
162
u64 *val)
163
{
164
struct ftm_quaddec *const ftm = counter_priv(counter);
165
uint32_t cntval;
166
167
ftm_read(ftm, FTM_CNT, &cntval);
168
169
*val = cntval;
170
171
return 0;
172
}
173
174
static int ftm_quaddec_count_write(struct counter_device *counter,
175
struct counter_count *count,
176
const u64 val)
177
{
178
struct ftm_quaddec *const ftm = counter_priv(counter);
179
180
if (val != 0) {
181
dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n");
182
return -EINVAL;
183
}
184
185
ftm_reset_counter(ftm);
186
187
return 0;
188
}
189
190
static int ftm_quaddec_count_function_read(struct counter_device *counter,
191
struct counter_count *count,
192
enum counter_function *function)
193
{
194
*function = COUNTER_FUNCTION_QUADRATURE_X4;
195
196
return 0;
197
}
198
199
static int ftm_quaddec_action_read(struct counter_device *counter,
200
struct counter_count *count,
201
struct counter_synapse *synapse,
202
enum counter_synapse_action *action)
203
{
204
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
205
206
return 0;
207
}
208
209
static const struct counter_ops ftm_quaddec_cnt_ops = {
210
.count_read = ftm_quaddec_count_read,
211
.count_write = ftm_quaddec_count_write,
212
.function_read = ftm_quaddec_count_function_read,
213
.action_read = ftm_quaddec_action_read,
214
};
215
216
static struct counter_signal ftm_quaddec_signals[] = {
217
{
218
.id = 0,
219
.name = "Channel 1 Phase A"
220
},
221
{
222
.id = 1,
223
.name = "Channel 1 Phase B"
224
}
225
};
226
227
static struct counter_synapse ftm_quaddec_count_synapses[] = {
228
{
229
.actions_list = ftm_quaddec_synapse_actions,
230
.num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
231
.signal = &ftm_quaddec_signals[0]
232
},
233
{
234
.actions_list = ftm_quaddec_synapse_actions,
235
.num_actions = ARRAY_SIZE(ftm_quaddec_synapse_actions),
236
.signal = &ftm_quaddec_signals[1]
237
}
238
};
239
240
static DEFINE_COUNTER_ENUM(ftm_quaddec_prescaler_enum, ftm_quaddec_prescaler);
241
242
static struct counter_comp ftm_quaddec_count_ext[] = {
243
COUNTER_COMP_COUNT_ENUM("prescaler", ftm_quaddec_get_prescaler,
244
ftm_quaddec_set_prescaler,
245
ftm_quaddec_prescaler_enum),
246
};
247
248
static struct counter_count ftm_quaddec_counts = {
249
.id = 0,
250
.name = "Channel 1 Count",
251
.functions_list = ftm_quaddec_count_functions,
252
.num_functions = ARRAY_SIZE(ftm_quaddec_count_functions),
253
.synapses = ftm_quaddec_count_synapses,
254
.num_synapses = ARRAY_SIZE(ftm_quaddec_count_synapses),
255
.ext = ftm_quaddec_count_ext,
256
.num_ext = ARRAY_SIZE(ftm_quaddec_count_ext)
257
};
258
259
static int ftm_quaddec_probe(struct platform_device *pdev)
260
{
261
struct counter_device *counter;
262
struct ftm_quaddec *ftm;
263
264
struct device_node *node = pdev->dev.of_node;
265
struct resource *io;
266
int ret;
267
268
counter = devm_counter_alloc(&pdev->dev, sizeof(*ftm));
269
if (!counter)
270
return -ENOMEM;
271
ftm = counter_priv(counter);
272
273
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
274
if (!io) {
275
dev_err(&pdev->dev, "Failed to get memory region\n");
276
return -ENODEV;
277
}
278
279
ftm->pdev = pdev;
280
ftm->big_endian = of_property_read_bool(node, "big-endian");
281
ftm->ftm_base = devm_ioremap(&pdev->dev, io->start, resource_size(io));
282
283
if (!ftm->ftm_base) {
284
dev_err(&pdev->dev, "Failed to map memory region\n");
285
return -EINVAL;
286
}
287
counter->name = dev_name(&pdev->dev);
288
counter->parent = &pdev->dev;
289
counter->ops = &ftm_quaddec_cnt_ops;
290
counter->counts = &ftm_quaddec_counts;
291
counter->num_counts = 1;
292
counter->signals = ftm_quaddec_signals;
293
counter->num_signals = ARRAY_SIZE(ftm_quaddec_signals);
294
295
mutex_init(&ftm->ftm_quaddec_mutex);
296
297
ftm_quaddec_init(ftm);
298
299
ret = devm_add_action_or_reset(&pdev->dev, ftm_quaddec_disable, ftm);
300
if (ret)
301
return ret;
302
303
ret = devm_counter_add(&pdev->dev, counter);
304
if (ret)
305
return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");
306
307
return 0;
308
}
309
310
static const struct of_device_id ftm_quaddec_match[] = {
311
{ .compatible = "fsl,ftm-quaddec" },
312
{},
313
};
314
MODULE_DEVICE_TABLE(of, ftm_quaddec_match);
315
316
static struct platform_driver ftm_quaddec_driver = {
317
.driver = {
318
.name = "ftm-quaddec",
319
.of_match_table = ftm_quaddec_match,
320
},
321
.probe = ftm_quaddec_probe,
322
};
323
324
module_platform_driver(ftm_quaddec_driver);
325
326
MODULE_DESCRIPTION("Flex Timer Module Quadrature decoder");
327
MODULE_LICENSE("GPL");
328
MODULE_AUTHOR("Kjeld Flarup <[email protected]>");
329
MODULE_AUTHOR("Patrick Havelange <[email protected]>");
330
MODULE_IMPORT_NS("COUNTER");
331
332