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>
17 #include <linux/types.h>
19 #define FTM_FIELD_UPDATE(ftm, offset, mask, val) \
22 ftm_read(ftm, offset, &flags); \
24 flags |= FIELD_PREP(mask, val); \
25 ftm_write(ftm, offset, flags); \
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(void *ftm
)
105 struct ftm_quaddec
*ftm_qua
= ftm
;
107 ftm_clear_write_protection(ftm_qua
);
108 ftm_write(ftm_qua
, FTM_MODE
, 0);
109 ftm_write(ftm_qua
, FTM_QDCTRL
, 0);
111 * This is enough to disable the counter. No clock has been
112 * selected by writing to FTM_SC in init()
114 ftm_set_write_protection(ftm_qua
);
117 static int ftm_quaddec_get_prescaler(struct counter_device
*counter
,
118 struct counter_count
*count
, u32
*cnt_mode
)
120 struct ftm_quaddec
*ftm
= counter_priv(counter
);
123 ftm_read(ftm
, FTM_SC
, &scflags
);
125 *cnt_mode
= FIELD_GET(FTM_SC_PS_MASK
, scflags
);
130 static int ftm_quaddec_set_prescaler(struct counter_device
*counter
,
131 struct counter_count
*count
, u32 cnt_mode
)
133 struct ftm_quaddec
*ftm
= counter_priv(counter
);
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 const enum counter_synapse_action ftm_quaddec_synapse_actions
[] = {
153 COUNTER_SYNAPSE_ACTION_BOTH_EDGES
156 static const enum counter_function ftm_quaddec_count_functions
[] = {
157 COUNTER_FUNCTION_QUADRATURE_X4
160 static int ftm_quaddec_count_read(struct counter_device
*counter
,
161 struct counter_count
*count
,
164 struct ftm_quaddec
*const ftm
= counter_priv(counter
);
167 ftm_read(ftm
, FTM_CNT
, &cntval
);
174 static int ftm_quaddec_count_write(struct counter_device
*counter
,
175 struct counter_count
*count
,
178 struct ftm_quaddec
*const ftm
= counter_priv(counter
);
181 dev_warn(&ftm
->pdev
->dev
, "Can only accept '0' as new counter value\n");
185 ftm_reset_counter(ftm
);
190 static int ftm_quaddec_count_function_read(struct counter_device
*counter
,
191 struct counter_count
*count
,
192 enum counter_function
*function
)
194 *function
= COUNTER_FUNCTION_QUADRATURE_X4
;
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
)
204 *action
= COUNTER_SYNAPSE_ACTION_BOTH_EDGES
;
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
,
216 static struct counter_signal ftm_quaddec_signals
[] = {
219 .name
= "Channel 1 Phase A"
223 .name
= "Channel 1 Phase B"
227 static struct counter_synapse ftm_quaddec_count_synapses
[] = {
229 .actions_list
= ftm_quaddec_synapse_actions
,
230 .num_actions
= ARRAY_SIZE(ftm_quaddec_synapse_actions
),
231 .signal
= &ftm_quaddec_signals
[0]
234 .actions_list
= ftm_quaddec_synapse_actions
,
235 .num_actions
= ARRAY_SIZE(ftm_quaddec_synapse_actions
),
236 .signal
= &ftm_quaddec_signals
[1]
240 static DEFINE_COUNTER_ENUM(ftm_quaddec_prescaler_enum
, ftm_quaddec_prescaler
);
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
),
248 static struct counter_count ftm_quaddec_counts
= {
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
)
259 static int ftm_quaddec_probe(struct platform_device
*pdev
)
261 struct counter_device
*counter
;
262 struct ftm_quaddec
*ftm
;
264 struct device_node
*node
= pdev
->dev
.of_node
;
268 counter
= devm_counter_alloc(&pdev
->dev
, sizeof(*ftm
));
271 ftm
= counter_priv(counter
);
273 io
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
275 dev_err(&pdev
->dev
, "Failed to get memory region\n");
280 ftm
->big_endian
= of_property_read_bool(node
, "big-endian");
281 ftm
->ftm_base
= devm_ioremap(&pdev
->dev
, io
->start
, resource_size(io
));
283 if (!ftm
->ftm_base
) {
284 dev_err(&pdev
->dev
, "Failed to map memory region\n");
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
);
295 mutex_init(&ftm
->ftm_quaddec_mutex
);
297 ftm_quaddec_init(ftm
);
299 ret
= devm_add_action_or_reset(&pdev
->dev
, ftm_quaddec_disable
, ftm
);
303 ret
= devm_counter_add(&pdev
->dev
, counter
);
305 return dev_err_probe(&pdev
->dev
, ret
, "Failed to add counter\n");
310 static const struct of_device_id ftm_quaddec_match
[] = {
311 { .compatible
= "fsl,ftm-quaddec" },
314 MODULE_DEVICE_TABLE(of
, ftm_quaddec_match
);
316 static struct platform_driver ftm_quaddec_driver
= {
318 .name
= "ftm-quaddec",
319 .of_match_table
= ftm_quaddec_match
,
321 .probe
= ftm_quaddec_probe
,
324 module_platform_driver(ftm_quaddec_driver
);
326 MODULE_DESCRIPTION("Flex Timer Module Quadrature decoder");
327 MODULE_LICENSE("GPL");
328 MODULE_AUTHOR("Kjeld Flarup <kfa@deif.com>");
329 MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");
330 MODULE_IMPORT_NS("COUNTER");