1 // SPDX-License-Identifier: GPL-2.0
3 * Flex Timer Module Quadrature decoder
5 * This module implements a driver for decoding the FTM quadrature
9 #include <linux/fsl/ftm.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
14 #include <linux/mutex.h>
15 #include <linux/counter.h>
16 #include <linux/bitfield.h>
18 #define FTM_FIELD_UPDATE(ftm, offset, mask, val) \
21 ftm_read(ftm, offset, &flags); \
23 flags |= FIELD_PREP(mask, val); \
24 ftm_write(ftm, offset, flags); \
28 struct counter_device counter
;
29 struct platform_device
*pdev
;
30 void __iomem
*ftm_base
;
32 struct mutex ftm_quaddec_mutex
;
35 static void ftm_read(struct ftm_quaddec
*ftm
, uint32_t offset
, uint32_t *data
)
38 *data
= ioread32be(ftm
->ftm_base
+ offset
);
40 *data
= ioread32(ftm
->ftm_base
+ offset
);
43 static void ftm_write(struct ftm_quaddec
*ftm
, uint32_t offset
, uint32_t data
)
46 iowrite32be(data
, ftm
->ftm_base
+ offset
);
48 iowrite32(data
, ftm
->ftm_base
+ offset
);
51 /* Hold mutex before modifying write protection state */
52 static void ftm_clear_write_protection(struct ftm_quaddec
*ftm
)
56 /* First see if it is enabled */
57 ftm_read(ftm
, FTM_FMS
, &flag
);
59 if (flag
& FTM_FMS_WPEN
)
60 FTM_FIELD_UPDATE(ftm
, FTM_MODE
, FTM_MODE_WPDIS
, 1);
63 static void ftm_set_write_protection(struct ftm_quaddec
*ftm
)
65 FTM_FIELD_UPDATE(ftm
, FTM_FMS
, FTM_FMS_WPEN
, 1);
68 static void ftm_reset_counter(struct ftm_quaddec
*ftm
)
70 /* Reset hardware counter to CNTIN */
71 ftm_write(ftm
, FTM_CNT
, 0x0);
74 static void ftm_quaddec_init(struct ftm_quaddec
*ftm
)
76 ftm_clear_write_protection(ftm
);
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
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
);
90 /* Select quad mode, reset other fields to zero */
91 ftm_write(ftm
, FTM_QDCTRL
, FTM_QDCTRL_QUADEN
);
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);
100 ftm_set_write_protection(ftm
);
103 static void ftm_quaddec_disable(struct ftm_quaddec
*ftm
)
105 ftm_clear_write_protection(ftm
);
106 ftm_write(ftm
, FTM_MODE
, 0);
107 ftm_write(ftm
, FTM_QDCTRL
, 0);
109 * This is enough to disable the counter. No clock has been
110 * selected by writing to FTM_SC in init()
112 ftm_set_write_protection(ftm
);
115 static int ftm_quaddec_get_prescaler(struct counter_device
*counter
,
116 struct counter_count
*count
,
119 struct ftm_quaddec
*ftm
= counter
->priv
;
122 ftm_read(ftm
, FTM_SC
, &scflags
);
124 *cnt_mode
= FIELD_GET(FTM_SC_PS_MASK
, scflags
);
129 static int ftm_quaddec_set_prescaler(struct counter_device
*counter
,
130 struct counter_count
*count
,
133 struct ftm_quaddec
*ftm
= counter
->priv
;
135 mutex_lock(&ftm
->ftm_quaddec_mutex
);
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
);
141 /* Also resets the counter as it is undefined anyway now */
142 ftm_reset_counter(ftm
);
144 mutex_unlock(&ftm
->ftm_quaddec_mutex
);
148 static const char * const ftm_quaddec_prescaler
[] = {
149 "1", "2", "4", "8", "16", "32", "64", "128"
152 static struct counter_count_enum_ext ftm_quaddec_prescaler_enum
= {
153 .items
= ftm_quaddec_prescaler
,
154 .num_items
= ARRAY_SIZE(ftm_quaddec_prescaler
),
155 .get
= ftm_quaddec_get_prescaler
,
156 .set
= ftm_quaddec_set_prescaler
159 enum ftm_quaddec_synapse_action
{
160 FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES
,
163 static enum counter_synapse_action ftm_quaddec_synapse_actions
[] = {
164 [FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES
] =
165 COUNTER_SYNAPSE_ACTION_BOTH_EDGES
168 enum ftm_quaddec_count_function
{
169 FTM_QUADDEC_COUNT_ENCODER_MODE_1
,
172 static const enum counter_count_function ftm_quaddec_count_functions
[] = {
173 [FTM_QUADDEC_COUNT_ENCODER_MODE_1
] =
174 COUNTER_COUNT_FUNCTION_QUADRATURE_X4
177 static int ftm_quaddec_count_read(struct counter_device
*counter
,
178 struct counter_count
*count
,
179 struct counter_count_read_value
*val
)
181 struct ftm_quaddec
*const ftm
= counter
->priv
;
184 ftm_read(ftm
, FTM_CNT
, &cntval
);
186 counter_count_read_value_set(val
, COUNTER_COUNT_POSITION
, &cntval
);
191 static int ftm_quaddec_count_write(struct counter_device
*counter
,
192 struct counter_count
*count
,
193 struct counter_count_write_value
*val
)
195 struct ftm_quaddec
*const ftm
= counter
->priv
;
199 err
= counter_count_write_value_get(&cnt
, COUNTER_COUNT_POSITION
, val
);
204 dev_warn(&ftm
->pdev
->dev
, "Can only accept '0' as new counter value\n");
208 ftm_reset_counter(ftm
);
213 static int ftm_quaddec_count_function_get(struct counter_device
*counter
,
214 struct counter_count
*count
,
217 *function
= FTM_QUADDEC_COUNT_ENCODER_MODE_1
;
222 static int ftm_quaddec_action_get(struct counter_device
*counter
,
223 struct counter_count
*count
,
224 struct counter_synapse
*synapse
,
227 *action
= FTM_QUADDEC_SYNAPSE_ACTION_BOTH_EDGES
;
232 static const struct counter_ops ftm_quaddec_cnt_ops
= {
233 .count_read
= ftm_quaddec_count_read
,
234 .count_write
= ftm_quaddec_count_write
,
235 .function_get
= ftm_quaddec_count_function_get
,
236 .action_get
= ftm_quaddec_action_get
,
239 static struct counter_signal ftm_quaddec_signals
[] = {
242 .name
= "Channel 1 Phase A"
246 .name
= "Channel 1 Phase B"
250 static struct counter_synapse ftm_quaddec_count_synapses
[] = {
252 .actions_list
= ftm_quaddec_synapse_actions
,
253 .num_actions
= ARRAY_SIZE(ftm_quaddec_synapse_actions
),
254 .signal
= &ftm_quaddec_signals
[0]
257 .actions_list
= ftm_quaddec_synapse_actions
,
258 .num_actions
= ARRAY_SIZE(ftm_quaddec_synapse_actions
),
259 .signal
= &ftm_quaddec_signals
[1]
263 static const struct counter_count_ext ftm_quaddec_count_ext
[] = {
264 COUNTER_COUNT_ENUM("prescaler", &ftm_quaddec_prescaler_enum
),
265 COUNTER_COUNT_ENUM_AVAILABLE("prescaler", &ftm_quaddec_prescaler_enum
),
268 static struct counter_count ftm_quaddec_counts
= {
270 .name
= "Channel 1 Count",
271 .functions_list
= ftm_quaddec_count_functions
,
272 .num_functions
= ARRAY_SIZE(ftm_quaddec_count_functions
),
273 .synapses
= ftm_quaddec_count_synapses
,
274 .num_synapses
= ARRAY_SIZE(ftm_quaddec_count_synapses
),
275 .ext
= ftm_quaddec_count_ext
,
276 .num_ext
= ARRAY_SIZE(ftm_quaddec_count_ext
)
279 static int ftm_quaddec_probe(struct platform_device
*pdev
)
281 struct ftm_quaddec
*ftm
;
283 struct device_node
*node
= pdev
->dev
.of_node
;
287 ftm
= devm_kzalloc(&pdev
->dev
, sizeof(*ftm
), GFP_KERNEL
);
291 platform_set_drvdata(pdev
, ftm
);
293 io
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
295 dev_err(&pdev
->dev
, "Failed to get memory region\n");
300 ftm
->big_endian
= of_property_read_bool(node
, "big-endian");
301 ftm
->ftm_base
= devm_ioremap(&pdev
->dev
, io
->start
, resource_size(io
));
303 if (!ftm
->ftm_base
) {
304 dev_err(&pdev
->dev
, "Failed to map memory region\n");
307 ftm
->counter
.name
= dev_name(&pdev
->dev
);
308 ftm
->counter
.parent
= &pdev
->dev
;
309 ftm
->counter
.ops
= &ftm_quaddec_cnt_ops
;
310 ftm
->counter
.counts
= &ftm_quaddec_counts
;
311 ftm
->counter
.num_counts
= 1;
312 ftm
->counter
.signals
= ftm_quaddec_signals
;
313 ftm
->counter
.num_signals
= ARRAY_SIZE(ftm_quaddec_signals
);
314 ftm
->counter
.priv
= ftm
;
316 mutex_init(&ftm
->ftm_quaddec_mutex
);
318 ftm_quaddec_init(ftm
);
320 ret
= counter_register(&ftm
->counter
);
322 ftm_quaddec_disable(ftm
);
327 static int ftm_quaddec_remove(struct platform_device
*pdev
)
329 struct ftm_quaddec
*ftm
= platform_get_drvdata(pdev
);
331 counter_unregister(&ftm
->counter
);
333 ftm_quaddec_disable(ftm
);
338 static const struct of_device_id ftm_quaddec_match
[] = {
339 { .compatible
= "fsl,ftm-quaddec" },
343 static struct platform_driver ftm_quaddec_driver
= {
345 .name
= "ftm-quaddec",
346 .of_match_table
= ftm_quaddec_match
,
348 .probe
= ftm_quaddec_probe
,
349 .remove
= ftm_quaddec_remove
,
352 module_platform_driver(ftm_quaddec_driver
);
354 MODULE_LICENSE("GPL");
355 MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com");
356 MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com");